spec/document_spec.rb in mongodoc-0.1.2 vs spec/document_spec.rb in mongodoc-0.2.0

- old
+ new

@@ -1,78 +1,93 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper') describe "MongoDoc::Document" do context "satisfies form_for requirements" do - class FormForTest < MongoDoc::Document + class FormForTest + include MongoDoc::Document + + key :data end - + before do @doc = FormForTest.new @doc._id = '1' end - + it "#id returns the _id" do @doc.id.should == @doc._id end - + it "#to_param returns the _id" do @doc.to_param.should == @doc._id end - + context "#new_record?" do it "is true when the object does not have an _id" do @doc._id = nil @doc.should be_new_record end - + it "is false when the object has an id" do @doc.should_not be_new_record end end + + context "attributes" do + it "has an initialize method that takes a hash" do + data = 'data' + FormForTest.new(:data => data).data.should == data + end + + it "can set attributes from a hash" do + test = FormForTest.new + data = 'data' + test.attributes = {:data => data} + test.data.should == data + end + + it "returns all its attributes" do + data = 'data' + test = FormForTest.new(:data => data) + test.attributes.should == {:data => data} + end + end end context "validations" do - class SimpleValidationTest < MongoDoc::Document + class SimpleValidationTest + include MongoDoc::Document + key :data validates_presence_of :data end - it "are part of Base" do - Validatable.should === MongoDoc::Document.new + it "are included by MongoDoc::Document" do + Validatable.should === SimpleValidationTest.new end it "valid? fails when a document is invalid" do doc = SimpleValidationTest.new doc.should_not be_valid doc.should have(1).error_on(:data) end end - context ".criteria" do - class CriteriaTest < MongoDoc::Document - key :data - end - - it "creates a new criteria for the document" do - CriteriaTest.criteria.should be_a_kind_of(MongoDoc::Criteria) - end - - it "sets the criteria klass" do - CriteriaTest.criteria.klass.should == CriteriaTest - end - end - context "saving" do - class SaveRoot < MongoDoc::Document + class SaveRoot + include MongoDoc::Document + has_many :save_children end - - class SaveChild < MongoDoc::Document + + class SaveChild + include MongoDoc::Document + key :data end - + before do @root = SaveRoot.new @root.stub(:_save) @child = SaveChild.new @root.save_children << @child @@ -82,17 +97,17 @@ it "delegates to the root" do validate = true @root.should_receive(:save).with(validate) @child.save(validate) end - + context "when validating" do it "validates" do @root.should_receive(:valid?) @root.save(true) end - + context "and valid" do it "delegates to _save" do @root.should_receive(:_save).with(false) @root.save(true) end @@ -102,25 +117,25 @@ @root.stub(:valid?).and_return(true) @root.should_receive(:_save).and_return(id) @root.save(true).should == id end end - + context "and invalid" do it "does not call _save" do @root.stub(:valid?).and_return(false) @root.should_not_receive(:_save) @root.save(true) end - + it "returns false" do @root.stub(:valid?).and_return(false) @root.save(true).should be_false end end end - + context "when not validating" do it "does not validate" do @root.should_not_receive(:valid?) @root.save(false) end @@ -135,22 +150,22 @@ @root.stub(:_save).and_return(id) @root.save(false).should == id end end end - + context "#save!" do it "delegates to the root" do @root.should_receive(:save!) @child.save! end - + it "validates" do @root.should_receive(:valid?).and_return(true) @root.save! end - + it "returns the result of _save if valid" do id = 'id' @root.stub(:valid?).and_return(true) @root.should_receive(:_save).with(true).and_return(id) @root.save!.should == id @@ -162,27 +177,28 @@ @root.save! end.should raise_error(MongoDoc::DocumentInvalidError) end end end - + context "#_save" do - class SaveTest < MongoDoc::Document + class SaveTest + include MongoDoc::Document end - + before do @collection = stub('collection') @doc = SaveTest.new @doc.stub(:_collection).and_return(@collection) end - + it "delegates to the collection save" do safe = true @collection.should_receive(:save) @doc.send(:_save, safe) end - + it "sets the _id of the document" do id = 'id' @collection.stub(:save).and_return(id) @doc.send(:_save, true) @doc._id.should == id @@ -194,173 +210,191 @@ @doc.send(:_save, true).should == id end end context "creating" do - class CreateTest < MongoDoc::Document + class CreateTest + include MongoDoc::Document + key :data validates_presence_of :data end before do @value = 'value' CreateTest.stub(:_create).and_return(true) end - + context ".create" do it "creates a new document" do obj = CreateTest.new CreateTest.should_receive(:new).and_return(obj) CreateTest.create end - + it "delegates to _create with safe => false" do obj = CreateTest.new(:data => @value) CreateTest.stub(:new).and_return(obj) CreateTest.should_receive(:_create).with(obj, false).and_return(true) CreateTest.create(:data => @value) end - + it "sets the passed attributes" do CreateTest.create(:data => @value).data.should == @value end it "returns a valid document" do CreateTest.should === CreateTest.create(:data => @value) end - + it "validates" do CreateTest.create.errors.should_not be_empty end it "returns an invalid document" do CreateTest.should === CreateTest.create end end - + context ".create!" do it "creates a new document" do obj = CreateTest.new CreateTest.should_receive(:new).and_return(obj) CreateTest.create! rescue nil end - + it "delegates to _create with safe => true" do obj = CreateTest.new(:data => @value) CreateTest.stub(:new).and_return(obj) CreateTest.should_receive(:_create).with(obj, true).and_return(true) CreateTest.create!(:data => @value) end - + it "sets the passed attributes" do CreateTest.create!(:data => @value).data.should == @value end it "returns a valid document" do CreateTest.should === CreateTest.create!(:data => @value) end - + it "raises when invalid" do expect do CreateTest.create! end.should raise_error(MongoDoc::DocumentInvalidError) end end end context "#_create" do - class CreateTest < MongoDoc::Document + class CreateTest + include MongoDoc::Document end - + before do @collection = stub('collection') @collection.stub(:insert) @doc = CreateTest.new CreateTest.stub(:collection).and_return(@collection) end - + it "delegates to the collection insert with safe" do safe = true @collection.should_receive(:insert).with(@doc, hash_including(:safe => safe)) CreateTest.send(:_create, @doc, safe) end - + it "sets the _id of the document" do id = 'id' @collection.stub(:insert).and_return(id) CreateTest.send(:_create, @doc, false) @doc._id.should == id end - + it "returns the _id" do id = 'id' @collection.stub(:insert).and_return(id) CreateTest.send(:_create, @doc, false).should == id - end + end end context "updating attributes" do - class UpdateAttributesRoot < MongoDoc::Document + class UpdateAttributesRoot + include MongoDoc::Document + has_one :update_attribute_child end - - class UpdateAttributesChild < MongoDoc::Document + + class UpdateAttributesChild + include MongoDoc::Document + key :data end - + before do @data = 'data' @doc = UpdateAttributesChild.new UpdateAttributesRoot.new.update_attribute_child = @doc @attrs = {:data => @data} @path_attrs = {'update_attribute_child.data' => @data} - @doc.stub(:_propose_update_attributes) + @doc.stub(:_naive_update_attributes) end context "#update_attributes" do + it "sets the attributes" do @doc.update_attributes(@attrs) @doc.data.should == @data end it "normalizes the attributes to the parent" do - @doc.should_receive(:path_to_root) + @doc.should_receive(:_path_to_root) @doc.update_attributes(@attrs) end it "validates" do @doc.should_receive(:valid?) @doc.update_attributes(@attrs) end - + it "returns false if the object is not valid" do @doc.stub(:valid?).and_return(false) @doc.update_attributes(@attrs).should be_false end - + context "if valid" do - it "delegates to _propose_update_attributes" do - @doc.should_receive(:_propose_update_attributes).with(@doc, @path_attrs, false) - @doc.update_attributes(@attrs) + context "and strict" do + it "delegates to _strict_update_attributes" do + strict_attrs = @attrs.merge(:__strict__ => true) + @doc.should_receive(:_strict_update_attributes).with(@path_attrs, false) + @doc.update_attributes(strict_attrs) + end end - it "returns the result of _propose_update_attributes" do + context "and naive" do + it "delegates to _naive_update_attributes" do + @doc.should_receive(:_naive_update_attributes).with(@path_attrs, false) + @doc.update_attributes(@attrs) + end + end + + it "returns the result of _naive_update_attributes" do result = 'check' - @doc.stub(:_propose_update_attributes).and_return(result) + @doc.stub(:_naive_update_attributes).and_return(result) @doc.update_attributes(@attrs).should == result end end end - + context "#update_attributes!" do it "sets the attributes" do @doc.update_attributes!(@attrs) @doc.data.should == @data end it "normalizes the attributes to the parent" do - @doc.should_receive(:path_to_root) + @doc.should_receive(:_path_to_root) @doc.update_attributes!(@attrs) end it "validates" do @doc.should_receive(:valid?).and_return(true) @@ -373,201 +407,135 @@ @doc.update_attributes!(@attrs) end.should raise_error(MongoDoc::DocumentInvalidError) end context "if valid" do - it "delegates to _propose_update_attributes with safe == true" do - @doc.should_receive(:_propose_update_attributes).with(@doc, @path_attrs, true) - @doc.update_attributes!(@attrs) + context "and strict" do + it "delegates to _strict_update_attributes with safe == true" do + strict_attrs = @attrs.merge(:__strict__ => true) + @doc.should_receive(:_strict_update_attributes).with(@path_attrs, true) + @doc.update_attributes!(strict_attrs) + end end - - it "returns the result of _propose_update_attributes" do + + context "and naive" do + it "delegates to _naive_update_attributes with safe == true" do + @doc.should_receive(:_naive_update_attributes).with(@path_attrs, true) + @doc.update_attributes!(@attrs) + end + end + + it "returns the result of _naive_update_attributes" do result = 'check' - @doc.stub(:_propose_update_attributes).and_return(result) + @doc.stub(:_naive_update_attributes).and_return(result) @doc.update_attributes!(@attrs).should == result end end end end - - context "#_propose_update_attributes" do - class ProposeUpdateAttributes < MongoDoc::Document - end - before do - @attrs = {:data => 1} - @safe = true + context "#_naive_update_attributes" do + class NaiveUpdateAttributes + include MongoDoc::Document end - - context "when not a child" do - before do - @obj = ProposeUpdateAttributes.new - end - - it "delegates to _update_attributes when not a child" do - @obj.should_receive(:_update_attributes).with(@attrs, @safe) - @obj.send(:_propose_update_attributes, @obj, @attrs, @safe) - end - - it "returns the results of _update_attributes" do - result = 'check' - @obj.stub(:_update_attributes).and_return(result) - @obj.send(:_propose_update_attributes, @obj, @attrs, @safe).should == result - end - end - - context "when a child" do - before do - @obj = ProposeUpdateAttributes.new - @parent = stub('parent') - @obj._parent = @parent - end - - it "delegates to the parent when a child" do - @parent.should_receive(:_propose_update_attributes).with(@obj, @attrs, @safe) - @obj.send(:_propose_update_attributes, @obj, @attrs, @safe) - end - - it "returns the results of the parent's _propose_update_attributes" do - result = 'check' - @parent.stub(:_propose_update_attributes).and_return(result) - @obj.send(:_propose_update_attributes, @obj, @attrs, @safe).should == result - end - end - end - context "#_update_attributes" do - class UpdateAttributes < MongoDoc::Document - end - before do + @id = 'id' + @attrs = {:data => 'data'} + @safe = false + @doc = NaiveUpdateAttributes.new + @doc.stub(:_id).and_return(@id) @collection = stub('collection') @collection.stub(:update) - @doc = UpdateAttributes.new - @doc.stub(:_id).and_return(@id) - UpdateAttributes.stub(:collection).and_return(@collection) - @attrs = {:data => 'value'} + @doc.stub(:_collection).and_return(@collection) end - - it "uses the set modifier for the attributes" do - safe = true - MongoDoc::Query.should_receive(:set_modifier).with(@attrs) - @collection.stub(:update) - @doc.send(:_update_attributes, @attrs, safe) - end - it "delegates to the collection update with safe" do - safe = true - @collection.should_receive(:update).with({'_id' => @id}, MongoDoc::Query.set_modifier(@attrs), {:safe => safe}) - @doc.send(:_update_attributes, @attrs, safe) + it "calls update on the collection without a root" do + @collection.should_receive(:update).with({'_id' => @id}, MongoDoc::Query.set_modifier(@attrs), {:safe => @safe}) + @doc.send(:_naive_update_attributes, @attrs, @safe) end - it "returns the result" do - result = 'check' - @collection.stub(:update).and_return(result) - @doc.send(:_update_attributes, @attrs, true) - end - end - - context "from a nested document" do - class NestedDocsRoot < MongoDoc::Document - has_many :nested_children + it "with a root, calls _naive_update_attributes on the root" do + root = NaiveUpdateAttributes.new + @doc.stub(:_root).and_return(root) + root.should_receive(:_naive_update_attributes).with(@attrs, @safe) + @doc.send(:_naive_update_attributes, @attrs, @safe) end + end - class NestedChild < MongoDoc::Document - has_one :leaf + context "#_strict_update_attributes" do + class StrictUpdateAttributes + include MongoDoc::Document end - class LeafDoc < MongoDoc::Document - key :data + before do + @id = 'id' + @attrs = {:data => 'data'} + @selector = {'selector' => 'selector'} + @safe = false + @doc = StrictUpdateAttributes.new + @doc.stub(:_id).and_return(@id) + @collection = stub('collection') + @collection.stub(:update) + @doc.stub(:_collection).and_return(@collection) end - context "#save" do - before do - @leaf = LeafDoc.new - @root = NestedDocsRoot.new(:nested_children => [NestedChild.new(:leaf => @leaf)]) + context "without a root" do + it "calls update on the collection" do + @collection.should_receive(:update).with({'_id' => @id}.merge(@selector), MongoDoc::Query.set_modifier(@attrs), :safe => @safe) + @doc.send(:_strict_update_attributes, @attrs, @safe, @selector) end - - it "calls the root document's save" do - @root.should_receive(:save).with(true) - @leaf.save - end - - it "(with bang!) calls the root documents save!" do - @root.should_receive(:save!) - @leaf.save! - end end - context "with no has_many, update_attributes" do + context "with a root" do before do - @leaf = LeafDoc.new - @root = NestedChild.new(:leaf => @leaf) + @root = StrictUpdateAttributes.new + @root.stub(:_collection).and_return(@collection) + @doc.stub(:_root).and_return(@root) + @doc.stub(:_selector_path_to_root).and_return({'path._id' => @id}) end - it "calls the root document's _update_attributes with a full attribute path and not safe" do - @root.should_receive(:_update_attributes).with({'leaf.data' => 'data'}, false) - @leaf.update_attributes(:data => 'data') + it "calls _selector_path_to_root on our id" do + @doc.should_receive(:_selector_path_to_root).with('_id' => @id).and_return({'path._id' => @id}) + @doc.send(:_strict_update_attributes, @attrs, @safe) end - it "(with bang!) calls the root document's _update_attributes with a full attribute path and safe" do - @root.should_receive(:_update_attributes).with({'leaf.data' => 'data'}, true) - @leaf.update_attributes!(:data => 'data') + it "calls _strict_update_attributes on the root with our selector" do + @root.should_receive(:_strict_update_attributes).with(@attrs, @safe, 'path._id' => @id) + @doc.send(:_strict_update_attributes, @attrs, @safe) end end - - context "with has_many, update_attributes" do - before do - @leaf = LeafDoc.new - NestedDocsRoot.new(:nested_children => [NestedChild.new(:leaf => @leaf)]) - end - - it "returns false" do - @leaf.update_attributes(:data => 'data').should be_false - end - - it "sets an error on base" do - @leaf.update_attributes(:data => 'data') - @leaf.errors[:base].should_not be_nil - end - - it "(with bang!) returns false" do - @leaf.update_attributes!(:data => 'data').should be_false - end - - it "(with bang!) sets an error on base" do - @leaf.update_attributes(:data => 'data') - @leaf.errors[:base].should_not be_nil - end - end - end describe "bson" do - class BSONTest < MongoDoc::Document + class BSONTest + include MongoDoc::Document + key :other end class BSONDerived < BSONTest + include MongoDoc::Document + key :derived end - + class OtherObject attr_accessor :value end - + before do @value = 'value' @other = OtherObject.new @other.value = @value @doc = BSONTest.new(:other => @other) end it "encodes the class for the object" do @doc.to_bson[MongoDoc::BSON::CLASS_KEY].should == BSONTest.name end - + it "renders a json representation of the object" do @doc.to_bson.should be_bson_eql({MongoDoc::BSON::CLASS_KEY => BSONTest.name, "other" => {MongoDoc::BSON::CLASS_KEY => OtherObject.name, "value" => @value}}) end it "includes the _id of the object" do @@ -592,15 +560,19 @@ MongoDoc::BSON.decode(@doc.to_bson).other.should be_kind_of(OtherObject) end context "associations" do context "has_one" do - class TestHasOneBsonDoc < MongoDoc::Document + class TestHasOneBsonDoc + include MongoDoc::Document + has_one :subdoc end - class SubHasOneBsonDoc < MongoDoc::Document + class SubHasOneBsonDoc + include MongoDoc::Document + key :attr end it "#to_bson renders a bson representation of the document" do doc = TestHasOneBsonDoc.new @@ -619,15 +591,18 @@ end end context "has_many" do - class SubHasManyBsonDoc < MongoDoc::Document + class SubHasManyBsonDoc + include MongoDoc::Document + key :attr end - class TestHasManyBsonDoc < MongoDoc::Document + class TestHasManyBsonDoc + include MongoDoc::Document has_many :subdoc, :class_name => 'SubHasManyBsonDoc' end it "#to_bson renders a bson representation of the document" do doc = TestHasManyBsonDoc.new @@ -652,17 +627,11 @@ end end end context "misc class methods" do - class ClassMethods < MongoDoc::Document - end - - it ".count calls the collection count" do - collection = stub('collection') - ClassMethods.stub(:collection).and_return(collection) - collection.should_receive(:count).and_return(1) - ClassMethods.count + class ClassMethods + include MongoDoc::Document end it ".collection_name returns the name of the collection for this class" do ClassMethods.collection_name.should == ClassMethods.to_s.tableize.gsub('/', '.') end