mm_partial_update 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README.rdoc +71 -0
- data/lib/mm_partial_update.rb +32 -0
- data/lib/mm_partial_update/embedded_collection.rb +47 -0
- data/lib/mm_partial_update/extensions.rb +37 -0
- data/lib/mm_partial_update/one_embedded_proxy.rb +22 -0
- data/lib/mm_partial_update/plugins/document.rb +44 -0
- data/lib/mm_partial_update/plugins/embedded_document.rb +43 -0
- data/lib/mm_partial_update/plugins/partial_update.rb +93 -0
- data/lib/mm_partial_update/update_command.rb +114 -0
- data/lib/mm_partial_update/version.rb +4 -0
- data/test/functional/plugins/test_document.rb +91 -0
- data/test/functional/plugins/test_embedded_document.rb +112 -0
- data/test/functional/plugins/test_partial_update.rb +101 -0
- data/test/functional/test_update_command.rb +261 -0
- data/test/models.rb +20 -0
- data/test/test_helper.rb +96 -0
- data/test/test_mm_partial_update.rb +9 -0
- data/test/test_partial_update.rb +221 -0
- data/test/test_update_command.rb +133 -0
- metadata +177 -0
data/test/models.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
class Person
|
2
|
+
include MongoMapper::Document
|
3
|
+
key :name, String
|
4
|
+
many :pets
|
5
|
+
one :favorite_pet, :class_name=>'Pet'
|
6
|
+
end
|
7
|
+
|
8
|
+
class Pet
|
9
|
+
include MongoMapper::EmbeddedDocument
|
10
|
+
key :name, String
|
11
|
+
key :age, Integer
|
12
|
+
many :fleas
|
13
|
+
one :favorite_flea, :class_name=>'Flea'
|
14
|
+
end
|
15
|
+
|
16
|
+
class Flea
|
17
|
+
include MongoMapper::EmbeddedDocument
|
18
|
+
key :name, String
|
19
|
+
end
|
20
|
+
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
5
|
+
require 'mm_partial_update'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'ostruct'
|
8
|
+
|
9
|
+
require 'log_buddy'
|
10
|
+
require 'matchy'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
class Test::Unit::TestCase
|
14
|
+
def Doc(name='Class', &block)
|
15
|
+
klass = Class.new
|
16
|
+
klass.class_eval do
|
17
|
+
include MongoMapper::Document
|
18
|
+
set_collection_name :test
|
19
|
+
|
20
|
+
if name
|
21
|
+
class_eval "def self.name; '#{name}' end"
|
22
|
+
class_eval "def self.to_s; '#{name}' end"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
klass.class_eval(&block) if block_given?
|
27
|
+
klass.collection.remove
|
28
|
+
klass
|
29
|
+
end
|
30
|
+
|
31
|
+
def EDoc(name='Class', &block)
|
32
|
+
klass = Class.new
|
33
|
+
klass.class_eval do
|
34
|
+
include MongoMapper::EmbeddedDocument
|
35
|
+
|
36
|
+
if name
|
37
|
+
class_eval "def self.name; '#{name}' end"
|
38
|
+
class_eval "def self.to_s; '#{name}' end"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
klass.class_eval(&block) if block_given?
|
43
|
+
klass
|
44
|
+
end
|
45
|
+
|
46
|
+
def drop_indexes(klass)
|
47
|
+
if klass.database.collection_names.include?(klass.collection.name)
|
48
|
+
klass.collection.drop_indexes
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
custom_matcher :be_true do |receiver, matcher, args|
|
53
|
+
matcher.positive_failure_message = "Expected #{receiver} to be true but it wasn't"
|
54
|
+
matcher.negative_failure_message = "Expected #{receiver} not to be true but it was"
|
55
|
+
receiver.eql?(true)
|
56
|
+
end
|
57
|
+
|
58
|
+
custom_matcher :be_false do |receiver, matcher, args|
|
59
|
+
matcher.positive_failure_message = "Expected #{receiver} to be false but it wasn't"
|
60
|
+
matcher.negative_failure_message = "Expected #{receiver} not to be false but it was"
|
61
|
+
receiver.eql?(false)
|
62
|
+
end
|
63
|
+
|
64
|
+
custom_matcher :have_error_on do |receiver, matcher, args|
|
65
|
+
receiver.valid?
|
66
|
+
attribute = args[0]
|
67
|
+
expected_message = args[1]
|
68
|
+
|
69
|
+
if expected_message.nil?
|
70
|
+
matcher.positive_failure_message = "#{receiver} had no errors on #{attribute}"
|
71
|
+
matcher.negative_failure_message = "#{receiver} had errors on #{attribute} #{receiver.errors.inspect}"
|
72
|
+
!receiver.errors[attribute].blank?
|
73
|
+
else
|
74
|
+
actual = receiver.errors[attribute]
|
75
|
+
matcher.positive_failure_message = %Q(Expected error on #{attribute} to be "#{expected_message}" but was "#{actual}")
|
76
|
+
matcher.negative_failure_message = %Q(Expected error on #{attribute} not to be "#{expected_message}" but was "#{actual}")
|
77
|
+
actual.include? expected_message
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
custom_matcher :have_index do |receiver, matcher, args|
|
82
|
+
index_name = args[0]
|
83
|
+
matcher.positive_failure_message = "#{receiver} does not have index named #{index_name}, but should"
|
84
|
+
matcher.negative_failure_message = "#{receiver} does have index named #{index_name}, but should not"
|
85
|
+
!receiver.collection.index_information.detect { |index| index[0] == index_name }.nil?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
log_dir = File.expand_path('../../log', __FILE__)
|
90
|
+
FileUtils.mkdir_p(log_dir) unless File.exist?(log_dir)
|
91
|
+
logger = Logger.new(log_dir + '/test.log')
|
92
|
+
|
93
|
+
LogBuddy.init(:logger => logger)
|
94
|
+
MongoMapper.connection = Mongo::Connection.new('127.0.0.1', 27017, :logger => logger)
|
95
|
+
MongoMapper.database = "mm-test-#{RUBY_VERSION.gsub('.', '-')}"
|
96
|
+
MongoMapper.database.collections.each { |c| c.drop_indexes }
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require "models"
|
3
|
+
|
4
|
+
class TestPartialUpdate < Test::Unit::TestCase
|
5
|
+
|
6
|
+
context "#prepare_update_command" do
|
7
|
+
|
8
|
+
context "on root documents" do
|
9
|
+
|
10
|
+
should "return an empty command if not changed" do
|
11
|
+
doc = Person.create!
|
12
|
+
doc.send(:prepare_update_command).to_h.empty?.should be_true
|
13
|
+
end
|
14
|
+
|
15
|
+
should "return to_mongo if the entity is new" do
|
16
|
+
doc = Person.new
|
17
|
+
doc.name = "hello"
|
18
|
+
doc.send(:prepare_update_command).to_h.should ==
|
19
|
+
{"$set"=>doc.to_mongo}
|
20
|
+
end
|
21
|
+
|
22
|
+
should "provide set changed fields" do
|
23
|
+
doc = Person.create
|
24
|
+
doc.name = "hello"
|
25
|
+
doc.send(:prepare_update_command).to_h.should ==
|
26
|
+
{"$set"=>{"name"=>"hello"}}
|
27
|
+
end
|
28
|
+
|
29
|
+
context "with changes to 'one' embedded associations" do
|
30
|
+
should "report a new child" do
|
31
|
+
doc = Person.create
|
32
|
+
doc.favorite_pet.build :name=>"Magma"
|
33
|
+
doc.send(:prepare_update_command).to_h.should == {
|
34
|
+
"$set"=>{'favorite_pet'=>doc.favorite_pet.to_mongo}
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
should "report changed child" do
|
39
|
+
doc = Person.new
|
40
|
+
doc.favorite_pet.build :name=>"Magma"
|
41
|
+
doc.save
|
42
|
+
doc.favorite_pet.name = "Ugly"
|
43
|
+
doc.send(:prepare_update_command).to_h.should == {
|
44
|
+
"$set"=>{"favorite_pet.name"=>"Ugly"}
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
should "report a deleted child" do
|
49
|
+
doc = Person.new
|
50
|
+
doc.favorite_pet.build :name=>"Magma"
|
51
|
+
doc.save
|
52
|
+
doc.favorite_pet = nil
|
53
|
+
doc.send(:prepare_update_command).to_h.should == {
|
54
|
+
"$set"=>{'favorite_pet'=>nil}
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
context "with changes to 'many' embedded associations" do
|
60
|
+
should "report a new child" do
|
61
|
+
doc = Person.create
|
62
|
+
doc.pets.build :name=>"Magma"
|
63
|
+
doc.send(:prepare_update_command).to_h.should == {
|
64
|
+
:pushes=>{"pets"=>[doc.pets[0].to_mongo]}
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
should "report a changed child" do
|
69
|
+
doc = Person.new
|
70
|
+
doc.pets.build :name=>"Magma"
|
71
|
+
doc.save!
|
72
|
+
doc.pets[0].name = "Ugly"
|
73
|
+
doc.send(:prepare_update_command).to_h.should == {
|
74
|
+
"$set"=>{"pets.0.name"=>"Ugly"}
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
should "report a deleted child" do
|
79
|
+
doc = Person.new
|
80
|
+
deleted = doc.pets.build :name=>"Magma"
|
81
|
+
doc.save!
|
82
|
+
doc.pets.pop
|
83
|
+
doc.send(:prepare_update_command).to_h.should == {
|
84
|
+
:pulls=>{"pets"=>[deleted._id]}
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
should "report a mix of added, modified and deleted" do
|
89
|
+
doc = Person.new
|
90
|
+
doc.pets.build :name => "Magma"
|
91
|
+
deleted = doc.pets.build :name => "Ugly"
|
92
|
+
doc.pets.build :name => "Bipolar"
|
93
|
+
doc.save!
|
94
|
+
doc.pets.reject! {|p|p.name=="Ugly"}
|
95
|
+
doc.pets[0].name = "Debris"
|
96
|
+
doc.pets.build :name => "Hades"
|
97
|
+
doc.send(:prepare_update_command).to_h.should == {
|
98
|
+
"$set"=>{"pets.0.name"=>"Debris"},
|
99
|
+
:pulls=>{"pets"=>[deleted._id]},
|
100
|
+
:pushes=>{"pets"=>[doc.pets[-1].to_mongo]}
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
should "update database selectors after save" do
|
105
|
+
doc = Person.new
|
106
|
+
doc.pets.build :name=>"Magma"
|
107
|
+
doc.pets.build :name=>"Ugly"
|
108
|
+
doc.pets.build :name=>"Bipolar"
|
109
|
+
doc.save!
|
110
|
+
doc.pets.reject! {|p|p.name=="Ugly"}
|
111
|
+
doc.save!
|
112
|
+
doc.pets.length.should == 2
|
113
|
+
doc.pets.each_with_index do |pet,index|
|
114
|
+
pet.instance_variable_get("@_database_position").should == index
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context "with changes to nested one associations" do
|
120
|
+
should "report a new grandchild" do
|
121
|
+
doc = Person.new
|
122
|
+
pet = doc.pets.build :name=>"Magma"
|
123
|
+
doc.save!
|
124
|
+
pet.favorite_flea.build :name=>"Fleatus"
|
125
|
+
doc.send(:prepare_update_command).to_h.should == {
|
126
|
+
"$set"=>{"pets.0.favorite_flea"=>pet.favorite_flea.to_mongo}
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
should "report a modified grandchild" do
|
131
|
+
doc = Person.new
|
132
|
+
pet = doc.pets.build :name=>"Magma"
|
133
|
+
flea = pet.favorite_flea.build :name=>"Fleatus"
|
134
|
+
doc.save!
|
135
|
+
flea.name = "Fleatasia"
|
136
|
+
doc.send(:prepare_update_command).to_h.should == {
|
137
|
+
"$set"=>{"pets.0.favorite_flea.name"=>"Fleatasia"}
|
138
|
+
}
|
139
|
+
end
|
140
|
+
|
141
|
+
should "report a deleted grandchild" do
|
142
|
+
doc = Person.new
|
143
|
+
pet = doc.pets.build :name=>"Magma"
|
144
|
+
pet.favorite_flea.build :name=>"Fleatus"
|
145
|
+
doc.save!
|
146
|
+
pet.favorite_flea = nil
|
147
|
+
doc.send(:prepare_update_command).to_h.should == {
|
148
|
+
"$set"=>{"pets.0.favorite_flea"=>nil}
|
149
|
+
}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "with changes to nested many associations" do
|
154
|
+
should "report a new grandchild" do
|
155
|
+
doc = Person.create
|
156
|
+
pet = doc.pets.build :name=>"Magma"
|
157
|
+
doc.save!
|
158
|
+
pet.fleas.build :name=>'Fleatus'
|
159
|
+
doc.send(:prepare_update_command).to_h.should == {
|
160
|
+
:pushes=>{"pets.0.fleas"=>[pet.fleas[0].to_mongo]}
|
161
|
+
}
|
162
|
+
end
|
163
|
+
should "report a changed grandchild" do
|
164
|
+
doc = Person.new
|
165
|
+
pet = doc.pets.build :name=>"Magma"
|
166
|
+
pet.fleas.build :name=>"Fleatus"
|
167
|
+
doc.save!
|
168
|
+
pet.fleas[0].name = "Fleatasia"
|
169
|
+
doc.send(:prepare_update_command).to_h.should == {
|
170
|
+
"$set"=>{"pets.0.fleas.0.name"=>"Fleatasia"}
|
171
|
+
}
|
172
|
+
end
|
173
|
+
|
174
|
+
should "report a deleted child" do
|
175
|
+
doc = Person.new
|
176
|
+
doc.pets.build :name=>"Magma"
|
177
|
+
doc.pets[0].fleas.build :name=>"Fleatus"
|
178
|
+
doc.save!
|
179
|
+
deleted = doc.pets[0].fleas.pop
|
180
|
+
doc.send(:prepare_update_command).to_h.should == {
|
181
|
+
:pulls=>{"pets.0.fleas"=>[deleted._id]}
|
182
|
+
}
|
183
|
+
end
|
184
|
+
|
185
|
+
should "report a mix of added, modified and deleted" do
|
186
|
+
doc = Person.new
|
187
|
+
pet = doc.pets.build :name=>"Magma"
|
188
|
+
pet.fleas.build :name => "Fleatus"
|
189
|
+
deleted = pet.fleas.build :name => "Fleatasia"
|
190
|
+
pet.fleas.build :name => "Soto"
|
191
|
+
doc.save!
|
192
|
+
pet.fleas.reject! {|f|f.name=="Fleatasia"}
|
193
|
+
pet.fleas[0].name = "Rinsai"
|
194
|
+
pet.fleas.build :name => "Dogen"
|
195
|
+
doc.send(:prepare_update_command).to_h.should == {
|
196
|
+
:pulls=>{"pets.0.fleas"=>[deleted._id]},
|
197
|
+
:pushes=>{"pets.0.fleas"=>[doc.pets[0].fleas[-1].to_mongo]},
|
198
|
+
"$set"=>{"pets.0.fleas.0.name"=>"Rinsai"}
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
202
|
+
should "update database selectors after save" do
|
203
|
+
doc = Person.new
|
204
|
+
pet = doc.pets.build :name=>"Magma"
|
205
|
+
pet.fleas.build :name=>"Fleatus"
|
206
|
+
pet.fleas.build :name=>"Soto"
|
207
|
+
pet.fleas.build :name=>"Rinsai"
|
208
|
+
doc.save!
|
209
|
+
pet.fleas.reject! {|p|p.name=="Soto"}
|
210
|
+
doc.save!
|
211
|
+
pet.fleas.length.should == 2
|
212
|
+
pet.fleas.each_with_index do |flea,index|
|
213
|
+
flea.instance_variable_get("@_database_position").should == index
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class TestUpdateCommand < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "UpdateCommand" do
|
6
|
+
setup do
|
7
|
+
@edoc = EDoc { key :name, String }
|
8
|
+
@doc = Doc { key :name, String }
|
9
|
+
@doc.many :children, :class=>@edoc
|
10
|
+
|
11
|
+
@command = MmPartialUpdate::UpdateCommand.new(@doc.new)
|
12
|
+
@defaults = {"a_field"=>2, "another_field"=>2}
|
13
|
+
@command.set nil, @defaults
|
14
|
+
end
|
15
|
+
|
16
|
+
context "#document_selector" do
|
17
|
+
should "be derived from the id of the root document" do
|
18
|
+
doc = @doc.new
|
19
|
+
edoc = doc.children.build
|
20
|
+
MmPartialUpdate::UpdateCommand.new(doc).document_selector.should == {:_id=>doc._id}
|
21
|
+
MmPartialUpdate::UpdateCommand.new(edoc).document_selector.should == {:_id=>doc._id}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "#set" do
|
26
|
+
|
27
|
+
context "with a blank selector and replace = true" do
|
28
|
+
should "set the fields hash directly onto $set" do
|
29
|
+
@command.set(nil, {"a_different_field"=>3}, :replace=>{})
|
30
|
+
@command.to_h["$set"].should == {"a_different_field"=>3}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "with a blank selector" do
|
35
|
+
should "merge the fields hash directly into $set" do
|
36
|
+
@command.set nil, {"a_different_field"=>3}
|
37
|
+
@command.to_h["$set"].should == {"a_different_field"=>3}.merge(@defaults)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "with replace = true" do
|
42
|
+
should "replace the key at 'selector' with the fields hash" do
|
43
|
+
@command.set "sel", {"this"=>:that}
|
44
|
+
@command.set "sel", {"that"=>:this}, :replace=>true
|
45
|
+
@command.to_h["$set"]["sel"].should == {"that"=>:this}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "with a selector and replace = false" do
|
50
|
+
should "merge each key of fields into $set, namespaced with the selector" do
|
51
|
+
@command.set "sel", {"that"=>:this, "the"=>:other}
|
52
|
+
@command.to_h["$set"].should == {"sel.that"=>:this, "sel.the"=>:other}.merge(@defaults)
|
53
|
+
@command.set "sel", {"that"=>:not_this, "b"=>:c}
|
54
|
+
@command.to_h["$set"].should == {"sel.that" => :not_this, "sel.the"=>:other,"sel.b"=>:c}.merge(@defaults)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
context "#unset" do
|
61
|
+
should "add the selector to the $unset hash" do
|
62
|
+
@command.unset "a_field"
|
63
|
+
@command.to_h["$unset"].should == {"a_field"=>true}
|
64
|
+
end
|
65
|
+
|
66
|
+
should "nullify a field when :nullify=>true" do
|
67
|
+
@command.unset "a_field", :nullify=>true
|
68
|
+
@command.to_h["$set"].should == @defaults.merge("a_field"=>nil)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
context "#push" do
|
74
|
+
should "add the field hash to the $pushAll array for the selector" do
|
75
|
+
@command.push "a_collection", {"a"=>:b, "c"=>:d}
|
76
|
+
@command.to_h[:pushes].should == {"a_collection" => [{"a"=>:b, "c"=>:d}]}
|
77
|
+
end
|
78
|
+
|
79
|
+
should "append the field hash to the$pushAll array for the selector" do
|
80
|
+
@command.push "a_collection", {"a"=>:b, "c"=>:d}
|
81
|
+
@command.push "a_collection", {"a"=>:e, "f"=>:g}
|
82
|
+
@command.to_h[:pushes].should == {"a_collection" => [{"a"=>:b, "c"=>:d},
|
83
|
+
{"a"=>:e, "f"=>:g}]}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "#pull" do
|
88
|
+
should "add the document id to the $pull array for the selector" do
|
89
|
+
@command.pull "a_collection", "123"
|
90
|
+
@command.to_h[:pulls].should == {"a_collection" => ["123"]}
|
91
|
+
end
|
92
|
+
|
93
|
+
should "append the doc id to the $pull array for the selector" do
|
94
|
+
@command.pull "a_collection", "123"
|
95
|
+
@command.pull "a_collection", "456"
|
96
|
+
@command.to_h[:pulls].should == {"a_collection" => ["123","456"]}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "#merge" do
|
101
|
+
should "merge the commands hash with an incoming UpdateCommand" do
|
102
|
+
another = MmPartialUpdate::UpdateCommand.new(@doc.new)
|
103
|
+
another.set "this", {"that"=>"the other"}
|
104
|
+
another.unset "me"
|
105
|
+
another.push "a", {"a"=>:b}
|
106
|
+
another.pull "b", "c"
|
107
|
+
@command.merge(another).should == @command.to_h.merge(another.to_h)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "#prepare_mongodb_commands" do
|
112
|
+
should "separate operations into sets, pulls and pushes" do
|
113
|
+
@command.set "a", {:b=>2}
|
114
|
+
@command.set "a.c", {"c"=>"d"}
|
115
|
+
@command.push "hi", {:ho=>true}
|
116
|
+
@command.push "hi", {:snack=>"yummy"}
|
117
|
+
@command.push "ho", {:cheeri=>0}
|
118
|
+
@command.pull "zig", "zag"
|
119
|
+
db_commands = @command.send(:prepare_mongodb_commands)
|
120
|
+
db_commands.length.should == 4
|
121
|
+
|
122
|
+
db_commands[0].should == {"$set"=>{"a.b"=>2, "a.c.c"=>"d"}.merge(@defaults)}
|
123
|
+
db_commands[1].should == {"$pull"=>{"zig"=>{"_id"=>{"$in"=>["zag"]}}}}
|
124
|
+
db_commands[2].should == {"$pushAll"=>{"hi"=>[{:ho=>true},{:snack=>"yummy"}]}}
|
125
|
+
db_commands[3].should == {"$pushAll"=>{"ho"=>[{:cheeri=>0}]}}
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|