require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') describe Octopus::Model do describe "#using method" do it "should return self after calling the #using method" do User.using(:canada).should == Octopus::ScopeProxy.new(:canada, User) end it "should allow selecting the shards on scope" do User.using(:canada).create!(:name => 'oi') User.using(:canada).count.should == 1 User.count.should == 0 end it "should allow selecting the shard using #new" do u = User.using(:canada).new u.name = "Thiago" u.save User.using(:canada).count.should == 1 User.using(:brazil).count.should == 0 u1 = User.new u1.name = "Joaquim" u2 = User.using(:canada).new u2.name = "Manuel" u1.save() u2.save() User.using(:canada).all.should == [u, u2] User.all.should == [u1] end it "should select the correct shard" do #TODO - Investigate this - why we need to set to master!? ActiveRecord::Base.connection_proxy.current_shard = :master User.using(:canada) User.create!(:name => 'oi') User.count.should == 1 end it "should allow scoping dynamically" do User.using(:canada).using(:master).using(:canada).create!(:name => 'oi') User.using(:canada).using(:master).count.should == 0 User.using(:master).using(:canada).count.should == 1 end it "should allow find inside blocks" do @user = User.using(:brazil).create!(:name => "Thiago") User.using(:brazil) do User.first.should == @user end User.using(:brazil).where(:name => "Thiago").first.should == @user end it "should clean the current_shard after executing the current query" do User.using(:canada).create!(:name => "oi") User.count.should == 0 end it "should support both groups and alone shards" do u = User.using(:alone_shard).create!(:name => "Alone") User.using(:alone_shard).count.should == 1 User.using(:canada).count.should == 0 User.using(:brazil).count.should == 0 User.count.should == 0 end describe "#current_shard attribute" do it "should store the attribute when you create or find an object" do u = User.using(:alone_shard).create!(:name => "Alone") u.current_shard.should == :alone_shard User.using(:canada).create!(:name => 'oi') u = User.using(:canada).find_by_name("oi") u.current_shard.should == :canada end it "should store the attribute when you find multiple instances" do 5.times { User.using(:alone_shard).create!(:name => "Alone") } User.using(:alone_shard).all.each do |u| u.current_shard.should == :alone_shard end end it "should works when you find, and after that, alter that object" do alone_user = User.using(:alone_shard).create!(:name => "Alone") master_user = User.using(:master).create!(:name => "Master") alone_user.name = "teste" alone_user.save User.using(:master).find(:first).name.should == "Master" User.using(:alone_shard).find(:first).name.should == "teste" end it "should work for the reload method" do User.using(:alone_shard).create!(:name => "Alone") u = User.using(:alone_shard).where(:name => "Alone").first u.reload u.name.should == "Alone" end end describe "passing a block" do it "should allow queries be executed inside the block, ponting to a specific shard" do User.using(:canada) do User.create(:name => "oi") end User.using(:canada).count.should == 1 User.using(:master).count.should == 0 User.count.should == 0 end it "should allow execute queries inside a model" do u = User.new u.awesome_queries() User.using(:canada).count.should == 1 User.count.should == 0 end end describe "raising errors" do it "should raise a error when you specify a shard that doesn't exist" do lambda { User.using(:crazy_shard).create!(:name => 'Thiago') }.should raise_error("Nonexistent Shard Name: crazy_shard") end end end describe "using a postgresql shard" do it "should update the Arel Engine" do User.using(:postgresql_shard).arel_engine.connection.adapter_name.should == "PostgreSQL" User.using(:alone_shard).arel_engine.connection.adapter_name.should == "MySQL" end it "should works with writes and reads" do u = User.using(:postgresql_shard).create!(:name => "PostgreSQL User") User.using(:postgresql_shard).all.should == [u] User.using(:alone_shard).find(:all).should == [] end end describe "AR basic methods" do it "increment" do u = User.using(:brazil).create!(:name => "Teste", :number => 10) u = User.using(:brazil).find_by_number(10) u.increment(:number) u.save() u = User.using(:brazil).find_by_number(11).should_not be_nil end it "increment!" do u = User.using(:brazil).create!(:name => "Teste", :number => 10) u = User.using(:brazil).find_by_number(10) u.increment!(:number) u = User.using(:brazil).find_by_number(11).should_not be_nil end it "toggle" do u = User.using(:brazil).create!(:name => "Teste", :admin => false) u = User.using(:brazil).find_by_name('Teste') u.toggle(:admin) u.save() u = User.using(:brazil).find_by_name('Teste').admin.should be_true end it "toggle!" do u = User.using(:brazil).create!(:name => "Teste", :admin => false) u = User.using(:brazil).find_by_name('Teste') u.toggle!(:admin) u = User.using(:brazil).find_by_name('Teste').admin.should be_true end it "count" do u = User.using(:brazil).create!(:name => "User1") u2 = User.using(:brazil).create!(:name => "User2") u3 = User.using(:brazil).create!(:name => "User3") User.using(:brazil).where(:name => "User2").count.should == 1 end it "update_attributes" do @user = User.using(:brazil).create!(:name => "User1") @user2 = User.using(:brazil).find(@user.id) @user2.update_attributes(:name => "Joaquim") User.using(:brazil).where(:name => "Joaquim").first.should_not be_nil end it "using update_attributes inside a block" do ActiveRecord::Base.connection.run_queries_on_shard :brazil do @user = User.create!(:name => "User1") @user2 = User.find(@user.id) @user2.update_attributes(:name => "Joaquim") end User.using(:brazil).where(:name => "Joaquim").first.should_not be_nil end it "update_attribute" do @user = User.using(:brazil).create!(:name => "User1") @user2 = User.using(:brazil).find(@user.id) @user2.update_attributes(:name => "Joaquim") User.using(:brazil).where(:name => "Joaquim").first.should_not be_nil end it "transaction" do #TODO - Investigate this - why we need to set to master!? ActiveRecord::Base.connection_proxy.current_shard = :master u = User.create!(:name => "Thiago") User.using(:brazil).count.should == 0 User.using(:master).count.should == 1 User.using(:brazil).transaction do User.where(:name => "Thiago").first.should be_nil User.create!(:name => "Brazil") end User.using(:brazil).count.should == 1 User.using(:master).count.should == 1 end describe "deleting a record" do before(:each) do @user = User.using(:brazil).create!(:name => "User1") @user2 = User.using(:brazil).find(@user.id) end it "delete" do @user2.delete lambda { User.using(:brazil).find(@user2.id) }.should raise_error(ActiveRecord::RecordNotFound) end it "destroy" do @user2.destroy lambda { User.using(:brazil).find(@user2.id) }.should raise_error(ActiveRecord::RecordNotFound) end end end describe "#replicated_model method" do it "should be replicated" do using_enviroment :production_replicated do ActiveRecord::Base.connection_proxy.instance_variable_get(:@replicated).should be_true end end it "should mark the Cat model as replicated" do using_enviroment :production_replicated do User.read_inheritable_attribute(:replicated).should be_false Cat.read_inheritable_attribute(:replicated).should be_true end end end end