# HG changeset patch # User Tyler Rick # Date 1267148357 28800 # Node ID 80300ab7a55f3b59856cfb84b9ccc1310e69b9e8 # Parent 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 Added tests to ensure that include? works as expected diff -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 -r 80300ab7a55f3b59856cfb84b9ccc1310e69b9e8 spec/db/schema.rb --- a/spec/db/schema.rb Thu Feb 25 16:15:38 2010 -0800 +++ b/spec/db/schema.rb Thu Feb 25 17:39:17 2010 -0800 @@ -18,4 +18,13 @@ t.column "maximum_occupancy", :integer end + create_table "doors_rooms", :id => false, :force => true do |t| + t.column "door_id", :integer + t.column "room_id", :integer + end + + create_table "doors", :force => true do |t| + t.column "name", :string + end + end diff -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 -r 80300ab7a55f3b59856cfb84b9ccc1310e69b9e8 spec/has_and_belongs_to_many_with_deferred_save_spec.rb --- a/spec/has_and_belongs_to_many_with_deferred_save_spec.rb Thu Feb 25 16:15:38 2010 -0800 +++ b/spec/has_and_belongs_to_many_with_deferred_save_spec.rb Thu Feb 25 17:39:17 2010 -0800 @@ -10,8 +10,12 @@ @people << Person.create @room = Room.new(:maximum_occupancy => 2) end + after :all do + Person.delete_all + Room.delete_all + end - it "initial checks" do + it "passes initial checks" do Room .count.should == 0 Person.count.should == 3 @@ -48,21 +52,23 @@ Room.count_by_sql("select count(*) from people_rooms").should == 2 @room.valid? - @room.errors.on(:people).should == "There are too many people in this room" + @room.errors.on(:people).should == "This room has reached its maximum occupancy" @room.people.size. should == 3 # Just like with normal attributes that fail validation... the attribute still contains the invalid data but we refuse to save until it is changed to something that is *valid*. end it "when we try to save, it should fail, because room.people is still invaild" do @room.save.should == false Room.count_by_sql("select count(*) from people_rooms").should == 2 # It's still not there, because it didn't pass the validation. - @room.errors.on(:people).should == "There are too many people in this room" + @room.errors.on(:people).should == "This room has reached its maximum occupancy" @room.people.size. should == 3 + @people.map {|p| p.reload; p.rooms.size}.should == [1, 1, 0] end it "when we reload, it should go back to only having 2 people in the room" do @room.reload @room.people.size. should == 2 @room.people_without_deferred_save.size. should == 2 + @people.map {|p| p.reload; p.rooms.size}. should == [1, 1, 0] end it "if they try to go around our accessors and use the original accessors, then (and only then) will the exception be raised in before_adding_person..." do @@ -70,5 +76,71 @@ @room.people_without_deferred_save << @people[2] end.should raise_error(RuntimeError) end + + it "lets you bypass the validation on Room if we add the association from the other side (person.rooms <<)?" do + @people[2].rooms << @room + @people[2].rooms.size.should == 1 + + # Adding it from one direction does not add it to the other object's association (@room.people), so the validation passes. + @room.reload.people.size.should == 2 + @people[2].valid? + @people[2].errors.full_messages.should == [] + @people[2].save.should == true + + # It is only after reloading that @room.people has this 3rd object, causing it to be invalid, and by then it's too late to do anything about it. + @room.reload.people.size.should == 3 + @room.valid?.should == false + end + + it "only if you add the validation to both sides, can you ensure that the size of the association does not exceed some limit" do + @room.reload.people.size.should == 3 + @room.people.delete(@people[2]) + @room.save.should == true + @room.reload.people.size.should == 2 + @people[2].reload.rooms.size.should == 0 + + class << @people[2] + def validate + rooms.each do |room| + this_room_unsaved = rooms_without_deferred_save.include?(room) ? 0 : 1 + if room.people.size + this_room_unsaved > room.maximum_occupancy + errors.add :rooms, "This room has reached its maximum occupancy" + end + end + end + end + + @people[2].rooms << @room + @people[2].rooms.size.should == 1 + + @room.reload.people.size.should == 2 + @people[2].valid? + @people[2].errors.on(:rooms).should == "This room has reached its maximum occupancy" + @room.reload.people.size.should == 2 + end end + + describe "doors" do + before :all do + @rooms = [] + @rooms << Room.create(:name => 'Kitchen', :maximum_occupancy => 1) + @rooms << Room.create(:name => 'Dining room', :maximum_occupancy => 10) + @door = Door.new(:name => 'Kitchen-Dining-room door') + end + + it "passes initial checks" do + Room.count.should == 2 + Door.count.should == 0 + + @door.rooms.should == [] + @door.rooms_without_deferred_save.should == [] + end + + it "the association has an include? method" do + @door.rooms << @rooms[0] + @door.rooms.include?(@rooms[0]).should be_true + @door.rooms.include?(@rooms[1]).should be_false + end + end + end diff -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 -r 80300ab7a55f3b59856cfb84b9ccc1310e69b9e8 spec/models/door.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/models/door.rb Thu Feb 25 17:39:17 2010 -0800 @@ -0,0 +1,3 @@ +class Door < ActiveRecord::Base + has_and_belongs_to_many_with_deferred_save :rooms +end diff -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 -r 80300ab7a55f3b59856cfb84b9ccc1310e69b9e8 spec/models/person.rb --- a/spec/models/person.rb Thu Feb 25 16:15:38 2010 -0800 +++ b/spec/models/person.rb Thu Feb 25 17:39:17 2010 -0800 @@ -1,3 +1,7 @@ class Person < ActiveRecord::Base - has_and_belongs_to_many :room + has_and_belongs_to_many_with_deferred_save :rooms, :validate => true + + def validate + + end end diff -r 6f8d44db522b947c2491e2b2f96790e3a7c16fc7 -r 80300ab7a55f3b59856cfb84b9ccc1310e69b9e8 spec/models/room.rb --- a/spec/models/room.rb Thu Feb 25 16:15:38 2010 -0800 +++ b/spec/models/room.rb Thu Feb 25 17:39:17 2010 -0800 @@ -1,9 +1,10 @@ class Room < ActiveRecord::Base has_and_belongs_to_many_with_deferred_save :people, :before_add => :before_adding_person + has_and_belongs_to_many_with_deferred_save :doors def validate if people.size > maximum_occupancy - errors.add :people, "There are too many people in this room" + errors.add :people, "This room has reached its maximum occupancy" end end