Permalink
Browse files

added support for arrays of embedded documents

  • Loading branch information...
1 parent 15ec243 commit dde497639130e9bb6fb85458aaabbed9e5ce6e6a Karl Seguin committed Jul 31, 2011
@@ -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
View
@@ -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
@@ -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
View
@@ -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
View
@@ -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
@@ -0,0 +1,7 @@
+require 'child'
+class BusyParent
+ include MongoLight::Document
+ mongo_accessor({
+ :name => :name,
+ :children => {:field => :c, :class => Child, :array => true}})
+end
View
@@ -0,0 +1,6 @@
+class Child
+ include MongoLight::EmbeddedDocument
+ mongo_accessor({:name => :n, :power => :p})
+end
+
+
View
@@ -0,0 +1,7 @@
+require 'child'
+class Parent
+ include MongoLight::Document
+ mongo_accessor({
+ :name => :name,
+ :child => {:field => :c, :class => Child}})
+end
View
@@ -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

0 comments on commit dde4976

Please sign in to comment.