|
HowToTest
How to make the first tests with the plugin
IntroductionIf you followed the HowToInstall document, you should have a complete replicable enabled Rails project, albeit without any models and no means to test. So this document will hand you over to create a mock-up project that works. But if you are not so inclined to follow long tutorials, download everything. Inside this .gz file is the complete mock-up exactly as I show in this Wiki Page. Then jump directly to the "Running Tricks" section down below this page. Create some modelsFor this simple example, we will create the ReturnOrder and Batch models. In this example, a ReturnOrder has many Batches and a Batch belongs to one ReturnOrder. Let's create them: ./script/generate model ReturnOrder In its migration file we put: class CreateReturnOrders < ActiveRecord::Migration
def self.up
create_table :return_orders, :id => false do |t|
t.column :id, 'varchar(36) primary key', :null => false
t.column :name, :string
t.column :due_date, :datetime
t.column :created_by, :string, :limit => 36
t.column :updated_by, :string, :limit => 36
t.column :created_at, :timestamp
t.column :updated_at, :timestamp
end
end
def self.down
drop_table :return_orders
end
endNotice that we replaced the auto-increment ID for a varchar(36) to hold the UUID key. Now we create the Batch model: ./script/generate model Batch And then fill in its migration file: class CreateBatches < ActiveRecord::Migration
def self.up
create_table :batches, :id => false do |t|
t.column :id, 'varchar(36) primary key', :null => false
t.column :return_order_id, :string, :limit => 36
t.column :name, :string
t.column :created_by, :string, :limit => 36
t.column :updated_by, :string, :limit => 36
t.column :created_at, :timestamp
t.column :updated_at, :timestamp
end
end
def self.down
drop_table :batches
end
endNotice that both have UUIDs and trackable fields as described before. Now we modify the models this way: class ReturnOrder < ActiveRecord::Base acts_as_replica has_many :batches end class Batch < ActiveRecord::Base acts_as_replica belongs_to :return_order end Now we have replicable models. Every model you want to replicate has to deal with these same requirements. Now, moving on to authentication. Notice that you'd rather use scaffold_resource if you need some HTML screens for a more complete app. RESTful AuthenticationAs I mentioned before, I am using RESTful Authentication as its simple, straight-forward and as a bonus, it is buzzword-compliant (REST). You should see their homepage for more details but for now we can install it as: ./script/plugin install http://svn.techno-weenie.net/projects/plugins/restful_authentication/ Then, we need to generate the User model migration code: ./script/generate authenticated user sessions As I mentioned before, you will need to change the User model structure a bit to conform with our requirements, so open the migration file just created and modify it to become like this: class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users, :id => false do |t|
t.column :id, 'varchar(36) primary key', :null => false
t.column :fullname, :string
t.column :username, :string
t.column :password_salt, :string
t.column :password_hash, :string
t.column :guid, :string, :limit => 36, :null => false
t.column :last_synced, :datetime
t.column :created_by, :string, :limit => 36
t.column :updated_by, :string, :limit => 36
t.column :created_at, :timestamp
t.column :updated_at, :timestamp
end
end
def self.down
drop_table :users
end
endIn summary, the primary key is not enabled for UUID and we added the guid, last_synced and UUID enabled trackable fields. Now we have a complete environment to toy with. We just need some test data to see how the plugin behaves. Test DataCreate an additional migration file to hold the test data: ./script/generate migration AddTestData And put the following code in it: class AddTestData < ActiveRecord::Migration
def self.up
if ENV['RAILS_ENV'] == 'development'
SyncSetting.create :protocol => 'http',
:hostname => 'localhost',
:port => '3001',
:down_uri => '/syncs/down.yaml',
:up_uri => '/syncs/up.yaml',
:handshake_uri => '/syncs/handshake.yaml',
:unique_machine_id => 'cc09fbc6-67e0-11dc-9858-0016cbcc6803'
# now that the tables exists, this should work
$SYNC_CONFIG = ActiveRecord::Base.load_config
# only development will have this user
u = User.new :fullname => 'Administrator',
:login => 'admin',
:password => 'admin',
:guid => 'c0de03f0-2a37-11dc-8929-0016cbcc6803'
u.id = 'e427922e-28fa-11dc-b515-0016cbcc6803'
u.create
else
SyncSetting.create :protocol => 'http',
:hostname => 'localhost',
:port => '3001',
:down_uri => '/syncs/down.yaml',
:up_uri => '/syncs/up.yaml',
:handshake_uri => '/syncs/handshake.yaml',
:unique_machine_id => '0f9c256e-67fd-11dc-ac80-0016cbcc6803'
# now that the tables exists, this should work
$SYNC_CONFIG = ActiveRecord::Base.load_config
# this is the 'server' user
u = User.new :fullname => 'Root',
:login => 'root',
:password => 'root',
:guid => 'b1b2b050-2a3d-11dc-8fa5-0016cbcc6803'
u.id = 'b6ea59ba-2a3d-11dc-8fa5-0016cbcc6803'
u.create
# for the primary tests, the 'server' have to know the 'client' guid
# notice that the :guid here is the same as the :guid in the User instance
# for the 'client' above
RemoteClient.create :client_id => 'e427922e-28fa-11dc-b515-0016cbcc6803',
:guid => 'c0de03f0-2a37-11dc-8929-0016cbcc6803'
end
# regardless of the environment, we have to have this set up
# this is what the acts_as_timestamp does in the ApplicationController
User.current_user = User.find(:first)
if ENV['RAILS_ENV'] == 'development'
# creating some data in the 'client' side
r = ReturnOrder.create(:name => 'Teste 1', :due_date => Time.now)
r.batches.create(:name => 'Batch 1')
r.batches.create(:name => 'Batch 2')
r = ReturnOrder.create(:name => 'Teste 2', :due_date => Time.now)
r = ReturnOrder.create :name => 'Teste 3', :due_date => Time.now
r.batches.create(:name => 'Batch 3')
r = ReturnOrder.find_by_name('Teste 2')
r.destroy
r = ReturnOrder.find_by_name('Teste 1')
r.name = 'Teste 1 - updated'
r.due_date = Time.now
r.update
r.batches.create :name => 'Batch 4'
r.batches.create :name => 'Batch 6'
b = Batch.find_by_name('Batch 6')
b.destroy
r.batches.create :name => 'Batch 7'
b = Batch.find_by_name('Batch 1')
b.name = 'Batch 1 - updated'
b.update
else
# creating some data in the 'server side'
r = ReturnOrder.create(:name => 'Teste 4 - prd', :due_date => Time.now)
r.batches.create(:name => 'Batch 8 - prd')
r.batches.create(:name => 'Batch 9 - prd')
end
end
def self.down
Batch.delete_all
ReturnOrder.delete_all
RemoteClient.delete_all
User.delete_all
end
endRunning TricksWe are almost set. We created the models, the migrations and we are ready to go. I recommend opening 3 separated shell Terminals, all of them in the root of your Rails project. In the first one run this: rm db/*.sql*; rake db:migrate RAILS_ENV=development; rake db:migrate RAILS_ENV=production The reason being that now this entire line is recorded in the shell terminal history (if you're using Bash in some nix) and you only have to replay it to start from a clean slate. On the second terminal type: ./script/server -p 3001 -e production And in the third terminal type: ./script/runner '@logged_user=User.find_by_login("admin").id; load "lib/daemons/replicator.rb"' This will trigger the whole process. This will load the Rails environment and directly run the replication procedure. You can also start the usual script/server mongrel and browse to http://localhost:3000/syncs/perform_sync after logging in at /users/login. The Rails controller will spawn the same background procedure. This is done this way to avoid timeout constraints. You can tweak the syncs_controller.rb with some Ajax observer to check log/replica.log and provide the user with visual feedback. You can follow what's happening from the log dumps in the Terminals. I left enough log info so you don't get too lost. Read the README file from the plugin to understand more and, of course, read the source files. If everything went fine, your web browser should show a page with a content similar to this: Perform Syncing Results: Config: --- :client_version: 0.0.1 :up_uri: /syncs/up.yaml :port: 3001 :handshake_uri: /syncs/handshake.yaml :hostname: localhost :protocol: http :down_uri: /syncs/down.yaml Result Up: --- !ruby/object:ActsAsReplica::Structs::SyncUpResult errors: [] last_updated_at: "2007-07-06T16:28:12-03:00" status: 200 total: 3 Result Down: --- !ruby/object:ActsAsReplica::Structs::SyncUpResult errors: [] last_updated_at: "2007-07-06T16:28:09-03:00" status: 200 total: 9 Meaning that 3 rows were downloaded from the 'server' (the production process) and 9 rows were uploaded back to the 'server'. That's how it works. Or, you can just execute these commands: ./script/runner 'puts ReturnOrder.count; puts Batch.count' -e development ./script/runner 'puts ReturnOrder.count; puts Batch.count' -e production Compare the results in both cases: they should be exactly the same. You can open the ./script/console development or ./script/console production to add more rows in both ends and just reload the same page to make both databases to merge replicate again. In any case, the number of rows in all replicable tables should be the same at all peers. |
Sign in to add a comment

Great