diff --git a/lib/mongo_light/embedded_document.rb b/lib/mongo_light/embedded_document.rb index 0493aad..f610ca8 100644 --- a/lib/mongo_light/embedded_document.rb +++ b/lib/mongo_light/embedded_document.rb @@ -11,7 +11,7 @@ def mongo_accessor(map) define_method(k) { @attributes[k] } define_method("#{k}=") {|value| @attributes[k] = value } if v.is_a?(Hash) - @unmap[v[:field].to_s] = {:prop => k, :class => v[:class]} + @unmap[v[:field].to_s] = {:prop => k, :class => v[:class], :array => v[:array]} else @unmap[v.to_s] = k end @@ -21,14 +21,17 @@ def map(raw) return {} if raw.nil? || !raw.is_a?(Hash) hash = {} raw.each do |key, value| + sym = key.to_sym if value.is_a?(EmbeddedDocument) v = value.class.map(value.attributes) elsif value.is_a?(Hash) v = map(value) + elsif value.is_a?(Array) && @map[sym].is_a?(Hash) && @map[sym][:array] + v = value.map{|vv| vv.class.map(vv.attributes)} else v = value end - hash[map_key(key.to_sym)] = v + hash[map_key(sym)] = v end return hash end @@ -44,7 +47,11 @@ def unmap(data, raw = false) if @unmap[key].is_a?(Hash) real_key = @unmap[key][:prop] c = @unmap[key][:class] - v = raw ? c.unmap(value) : c.new(c.unmap(value)) + if @unmap[key][:array] + v = value.map{|vv| raw ? c.unmap(vv) : c.new(c.unmap(vv))} + else + v = raw ? c.unmap(value) : c.new(c.unmap(value)) + end else real_key = key == '_id' ? :_id : @unmap[key] v = value diff --git a/readme.markdown b/readme.markdown index 60ed379..61d7992 100644 --- a/readme.markdown +++ b/readme.markdown @@ -54,11 +54,16 @@ Next, use the following fancy syntax for your root document: class User include MongoLight::Document - mongo_accessor({:name => :name, password => :pw, :email => :e, :comments => {:field => :c, :class => Comment}}) + mongo_accessor({:name => :name, password => :pw, :email => :e, :comment => {:field => :c, :class => Comment}}) end Again, this'll make `comments` accessible on your objects, but store it within the `c` field. +If you want an array of embedded documents, simply specify `:array => true`: + + ... + :comments => {:field => :c, :class => Comment, array => true}}) + Please note that aliasing completely fails when querying embedded documents - you need to use the shortened name: User.find({:name => 'blah', 'c.t' => 'blah2}) #yes, it sucks diff --git a/spec/embedded_array_spec.rb b/spec/embedded_array_spec.rb new file mode 100644 index 0000000..2e93838 --- /dev/null +++ b/spec/embedded_array_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe 'embedded array behavior' do + it "saves a single embedded document" do + child = Child.new({:name => 'child', :power => 88}) + parent = BusyParent.new({:name => 'my name', :children => [child]}) + parent.save + found = BusyParent.find_one({:name => 'my name'}) + found.children.length.should == 1 + found.children[0].name.should == 'child' + found.children[0].power.should == 88 + end + it "saves a muliple embedded document" do + child1 = Child.new({:name => 'child1', :power => 88}) + child2 = Child.new({:name => 'child2', :power => 89}) + parent = BusyParent.new({:name => 'my name', :children => [child1, child2]}) + parent.save + found = BusyParent.find_one({:name => 'my name'}) + found.children.length.should == 2 + found.children[0].name.should == 'child1' + found.children[0].power.should == 88 + found.children[1].name.should == 'child2' + found.children[1].power.should == 89 + end +end \ No newline at end of file diff --git a/spec/embedded_spec.rb b/spec/embedded_spec.rb new file mode 100644 index 0000000..29e6f71 --- /dev/null +++ b/spec/embedded_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe 'embedded behavior' do + it "saves the embedded document" do + child = Child.new({:name => 'child', :power => 88}) + parent = Parent.new({:name => 'my name', :child => child}) + parent.save + found = Parent.find_one({:name => 'my name'}) + found.child.name.should == 'child' + found.child.power.should == 88 + end + it "returns the raw document with embedded objects" do + child = Child.new({:name => 'child', :power => 88}) + parent = Parent.new({:name => 'my name', :child => child}) + parent.save + found = Parent.find_one({:name => 'my name'}, {:raw => true}) + found.should == {:_id => parent.id, :name => 'my name', :child => {:name => 'child', :power => 88}} + end + it "finds a document by it's child" do + child = Child.new({:name => 'child', :power => 88}) + parent = Parent.new({:name => 'my name', :child => child}) + parent.save + Parent.count({'c.p' => 88}).should == 1 + end +end \ No newline at end of file diff --git a/spec/remove_spec.rb b/spec/remove_spec.rb new file mode 100644 index 0000000..be49691 --- /dev/null +++ b/spec/remove_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe 'remove behavior' do + it "removes the matching documents" do + Factory.create(:simple, {:name => 'paul', :power => 49}) + Factory.create(:simple, {:name => 'jessica', :power => 123}) + Simple.remove({:power => 49}) + Simple.count.should == 1 + Simple.count({:name => 'jessica'}).should == 1 + end + + it "removes all documents" do + Factory.create(:simple, {:name => 'paul', :power => 49}) + Factory.create(:simple, {:name => 'jessica', :power => 123}) + Simple.remove + Simple.count.should == 0 + end + + it "removes no document" do + Factory.create(:simple, {:name => 'paul', :power => 49}) + Factory.create(:simple, {:name => 'jessica', :power => 123}) + Simple.remove({:over9000 => true}) + Simple.count.should == 2 + end +end \ No newline at end of file diff --git a/spec/support/busy_parent.rb b/spec/support/busy_parent.rb new file mode 100644 index 0000000..4989b3b --- /dev/null +++ b/spec/support/busy_parent.rb @@ -0,0 +1,7 @@ +require 'child' +class BusyParent + include MongoLight::Document + mongo_accessor({ + :name => :name, + :children => {:field => :c, :class => Child, :array => true}}) +end \ No newline at end of file diff --git a/spec/support/child.rb b/spec/support/child.rb new file mode 100644 index 0000000..0f7cfcf --- /dev/null +++ b/spec/support/child.rb @@ -0,0 +1,6 @@ +class Child + include MongoLight::EmbeddedDocument + mongo_accessor({:name => :n, :power => :p}) +end + + \ No newline at end of file diff --git a/spec/support/parent.rb b/spec/support/parent.rb new file mode 100644 index 0000000..a217dea --- /dev/null +++ b/spec/support/parent.rb @@ -0,0 +1,7 @@ +require 'child' +class Parent + include MongoLight::Document + mongo_accessor({ + :name => :name, + :child => {:field => :c, :class => Child}}) +end \ No newline at end of file diff --git a/spec/update_spec.rb b/spec/update_spec.rb new file mode 100644 index 0000000..861cac6 --- /dev/null +++ b/spec/update_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe 'update behavior' do + it "updates the matching documents" do + Factory.create(:simple, {:name => 'paul', :power => 2}) + Factory.create(:simple, {:name => 'jessica', :power => 2}) + Simple.update({:power => 2}, {'$set' => {:power => 4}}, {:multi => true}) + Simple.count.should == 2 + Simple.count({:power => 4}).should == 2 + end + + it "updates a single document" do + Factory.create(:simple, {:name => 'paul', :power => 2}) + Factory.create(:simple, {:name => 'jessica', :power => 2}) + Simple.update({:power => 2}, {:power => 4}) + Simple.count.should == 2 + Simple.count({:power => 4}).should == 1 + end + + it "updates no document" do + Factory.create(:simple, {:name => 'paul', :power => 49}) + Factory.create(:simple, {:name => 'jessica', :power => 123}) + Simple.remove({:over9000 => true, :power => 442}) + Simple.count.should == 2 + Simple.count({:power => 442}).should == 0 + end +end \ No newline at end of file