What's new? | Help | Directory | Sign in
Google
                
Search
for
Updated Sep 25, 2007 by fabioakita
Labels: Phase-QA
HowToTest  
How to make the first tests with the plugin

Introduction

If 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 models

For 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
end

Notice 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
end

Notice 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 Authentication

As 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
end

In 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 Data

Create 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
end

Running Tricks

We 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.


Comment by rajkishan.m, Nov 11, 2007

Great


Sign in to add a comment