require File.dirname(__FILE__) + "/spec_helper" describe "DataMapper::Persistence::ConvenienceMethods::ClassMethods" do before(:all) do fixtures(:animals) fixtures(:exhibits) end describe 'A record' do it 'should save and return true if validations pass' do count = Exhibit.count bugs_and_more_bugs = Exhibit.new(:name => 'Bugs And More Bugs') bugs_and_more_bugs.save.should be_true Exhibit.count.should == count + 1 end it 'should return false on save if validations fail' do count = Exhibit.count bugs_and_more_bugs = Exhibit.new bugs_and_more_bugs.save.should be_false Exhibit.count.should == count end it 'should reload its attributes' do frog = Animal.first(:name => 'Frog') frog.name = 'MegaFrog' frog.name.should == 'MegaFrog' frog.reload! frog.name.should == 'Frog' end it "should prepare it's associations for reload" do chippy = Animal.first(:name => 'Cup') amazonia = Exhibit.first(:name => 'Amazonia') amazonia.animals << chippy amazonia.animals.should include(chippy) amazonia.reload! amazonia.animals.should_not include(chippy) end it 'should be destroyed!' do capybara = Animal.create(:name => 'Capybara') count = Animal.count capybara.destroy! Animal.first(:name => 'Capybara').should be_nil Animal.count.should == count - 1 end end it 'should return the first match using find_or_create' do count = Animal.count frog = Animal.find_or_create(:name => 'Frog') frog.name.should == 'Frog' Animal.count.should == count end it 'should create a record if a match is not found with find_or_create' do count = Animal.count capybara = Animal.find_or_create(:name => 'Capybara') capybara.name.should == 'Capybara' Animal.count.should == count + 1 end it 'should return all records' do all_animals = Animal.all all_animals.length.should == Animal.count all_animals.each do |animal| animal.class.should == Animal end end it 'should return the first record' do Animal.first.should == Animal.find(:first) end it 'should return a count of the records' do Animal.count.should == Animal.find_by_sql("SELECT COUNT(*) FROM animals")[0] end it 'should delete all records' do Animal.delete_all Animal.count.should == 0 fixtures(:animals) end #it 'should be truncated' do # Animal.truncate! # Animal.count.should == 0 # # fixtures(:animals) #end it 'should find a matching record given an id' do Animal.find(1).name.should == 'Frog' end it 'should find all records' do Animal.find(:all).length.should == Animal.count end it 'should find all matching records given some condition' do Animal.find(:all, :conditions => ["name = ?", "Frog"])[0].name.should == 'Frog' end it 'should find the first matching record' do Animal.find(:first).name.should == 'Frog' end it 'should find the first matching record given some condition' do Animal.find(:first, :conditions => ["name = ?", "Frog"]).name.should == 'Frog' end it 'should select records using the supplied sql fragment' do Animal.find_by_sql("SELECT name FROM animals WHERE name='Frog'")[0].should == 'Frog' end it 'should retrieve the indexed element' do Animal[1].id.should == 1 end it 'should create a new record' do count = Animal.count capybara = Animal.create(:name => 'Capybara') capybara.name.should == 'Capybara' Animal.count.should == count + 1 end end # Can't describe DataMapper::Persistence because # rspec will include it for some crazy reason! describe "DataMapper::Persistence" do it "should raise ObjectNotFoundError for missing objects with the indexer finder" do # pending "http://wm.lighthouseapp.com/projects/4819-datamapper/tickets/111-should-raise-objectnotfounderror-on-the-indexer-finder" lambda { Zoo[900] }.should raise_error(DataMapper::ObjectNotFoundError) end it "should raise IncompleteModelDefinitionError for a model with no properties" do lambda { class IncompleteZoo; include DataMapper::Persistence; end; IncompleteZoo.new }.should raise_error(DataMapper::Persistence::IncompleteModelDefinitionError) end it "attributes method should load all lazy-loaded values" do Animal.first(:name => 'Cup').attributes[:notes].should == 'I am a Cup!' end it "mass assignment should call methods" do class Animal attr_reader :test def test=(value) @test = value + '!' end end a = Animal.new(:test => 'testing') a.test.should == 'testing!' end it "should be dirty" do x = Person.create(:name => 'a') x.should_not be_dirty x.name = 'dslfay' x.should be_dirty end it "should be dirty when set to nil" do x = Person.create(:name => 'a') x.should_not be_dirty x.name = "asdfasfd" x.should be_dirty end it "should return a diff" do x = Person.new(:name => 'Sam', :age => 30, :occupation => 'Programmer') y = Person.new(:name => 'Amy', :age => 21, :occupation => 'Programmer') diff = (x ^ y) diff.should include(:name) diff.should include(:age) diff[:name].should eql(['Sam', 'Amy']) diff[:age].should eql([30, 21]) x.destroy! y.destroy! end it "should update attributes" do x = Person.create(:name => 'Sam') x.update_attributes(:age => 30).should eql(true) x.age.should eql(30) x.should_not be_dirty end it "should return the table for a given model" do Person.table.should be_a_kind_of(DataMapper::Adapters::Sql::Mappings::Table) end it "should support boolean accessors" do dolphin = Animal.first(:name => 'Dolphin') dolphin.should be_nice end it "should be comparable" do p1 = Person.create(:name => 'Sam') p2 = Person[p1.id] p1.should == p2 end # This is unnecessary. Use #dirty? to check for changes. # it "should not be equal if attributes have changed" do # p1 = Person.create(:name => 'Sam') # p2 = Person[p1.id] # p2.name = "Paul" # # p1.should_not == p2 # end end describe 'A new record' do before(:each) do @bob = Person.new(:name => 'Bob', :age => 30, :occupation => 'Sales') end it 'should be dirty' do @bob.dirty?.should == true end it 'set attributes should be dirty' do attributes = @bob.attributes.dup.reject { |k,v| k == :id } @bob.dirty_attributes.should == { :name => 'Bob', :age => 30, :occupation => 'Sales' } end it 'should be marked as new' do @bob.new_record?.should == true end it 'should have a nil id' do @bob.id.should == nil end it "should not have dirty attributes when not dirty" do x = Person.create(:name => 'a') x.should_not be_dirty x.dirty_attributes.should be_empty end it "should only list attributes that have changed in the dirty attributes hash" do x = Person.create(:name => 'a') x.name = "asdfr" x.should be_dirty x.dirty_attributes.keys.should == [:name] end it "should not have original_values when a new record" do x = Person.new(:name => 'a') x.original_values.should be_empty end it "should have original_values after saved" do x = Person.new(:name => 'a') x.save x.original_values.should_not be_empty x.original_values.keys.should include(:name) x.original_values[:name].should == 'a' end it "should have original values when created" do x = Person.create(:name => 'a') x.original_values.should_not be_empty x.original_values.keys.should include(:name) x.original_values[:name].should == "a" end it "should have original values when loaded from the database" do Person.create(:name => 'a') x = Person.first(:name => 'a') x.original_values.should_not be_empty x.original_values.keys.should include(:name) x.original_values[:name].should == "a" end it "should reset the original values when not new, changed then saved" do x = Person.create(:name => 'a') x.should_not be_new_record x.original_values[:name].should == "a" x.name = "b" x.save x.original_values[:name].should == "b" end it "should allow a value to be set to nil" do x = Person.create(:name => 'a') x.name = nil x.save x.reload! x.name.should be_nil end end describe 'Properties' do it 'should default to public method visibility' do class SoftwareEngineer #< DataMapper::Base # please do not remove this include DataMapper::Persistence set_table_name 'people' property :name, :string end public_properties = SoftwareEngineer.public_instance_methods.select { |m| ["name", "name="].include?(m) } public_properties.length.should == 2 end it 'should respect protected property options' do class SanitationEngineer #< DataMapper::Base # please do not remove this include DataMapper::Persistence set_table_name 'people' property :name, :string, :reader => :protected property :age, :integer, :writer => :protected end protected_properties = SanitationEngineer.protected_instance_methods.select { |m| ["name", "age="].include?(m) } protected_properties.length.should == 2 end it 'should respect private property options' do class ElectricalEngineer #< DataMapper::Base # please do not remove this include DataMapper::Persistence set_table_name 'people' property :name, :string, :reader => :private property :age, :integer, :writer => :private end private_properties = ElectricalEngineer.private_instance_methods.select { |m| ["name", "age="].include?(m) } private_properties.length.should == 2 end it 'should set both reader and writer visibiliy when accessor option is passed' do class TrainEngineer #< DataMapper::Base # please do not remove this include DataMapper::Persistence property :name, :string, :accessor => :private end private_properties = TrainEngineer.private_instance_methods.select { |m| ["name", "name="].include?(m) } private_properties.length.should == 2 end it 'should only be listed in attributes if they have public getters' do class SalesEngineer #< DataMapper::Base # please do not remove this include DataMapper::Persistence set_table_name 'people' property :name, :string property :age, :integer, :reader => :private end @sam = SalesEngineer.first(:name => 'Sam') # note: id default key gets a public reader by default (but writer is protected) @sam.attributes.should == {:id => @sam.id, :name => @sam.name} end it 'should not allow mass assignment if private or protected' do class ChemicalEngineer #< DataMapper::Base # please do not remove this include DataMapper::Persistence set_table_name 'people' property :name, :string, :writer => :private property :age, :integer end @sam = ChemicalEngineer.first(:name => 'Sam') @sam.attributes = {:name => 'frank', :age => 101} @sam.age.should == 101 @sam.name.should == 'Sam' end it 'should allow :protected to be passed as an alias for a public reader, protected writer' do class CivilEngineer #< DataMapper::Base # please do not remove this include DataMapper::Persistence set_table_name 'people' property :name, :string, :protected => true end CivilEngineer.public_instance_methods.should include("name") CivilEngineer.protected_instance_methods.should include("name=") end it 'should allow :private to be passed as an alias for a public reader, private writer' do class AudioEngineer #< DataMapper::Base # please do not remove this include DataMapper::Persistence set_table_name 'people' property :name, :string, :private => true end AudioEngineer.public_instance_methods.should include("name") AudioEngineer.private_instance_methods.should include("name=") end it 'should raise an error when invalid options are passsed' do lambda do class JumpyCow #< DataMapper::Base # please do not remove this include DataMapper::Persistence set_table_name 'animals' property :name, :string, :laze => true end end.should raise_error(ArgumentError) end it 'should raise an error when the first argument to index isnt an array' do lambda do class JumpyCow #< DataMapper::Base # please do not remove this include DataMapper::Persistence set_table_name 'animals' index :name, :parent end end.should raise_error(ArgumentError) end it "should return true on saving a new record" do bob = User.new(:name => 'bob', :email => 'bob@example.com') bob.save!.should == true bob.destroy! end it "should assign to public setters" do x = Project.new x.attributes = { :set_us_up_the_bomb => true } x.should be_set_up_for_bomb end it "should protect private setters" do x = Project.new x.attributes = { :be_wery_sneaky => true } x.should_not be_wery_sneaky end it "should not call initialize on materialization" do # pending "http://wm.lighthouseapp.com/projects/4819-datamapper/tickets/113-use-allocate-to-materialize-objects" x = Tomato.new x.should be_initialized x.save! x.name.should eql('Ugly') x.should be_bruised x.name = 'Bob' x.save! x2 = Tomato.first(:name => 'Bob') x2.should_not be_bruised x2.heal! x2.should == x x2.name.should eql('Bob') x2.should_not be_initialized x3 = Tomato.get(x.key) x3.should_not be_bruised x3.heal! x3.should == x x3.name.should eql('Bob') x3.should_not be_initialized end it "should report persistence" do Tomato.should be_persistent end end