mongo_doc 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY.md ADDED
@@ -0,0 +1,4 @@
1
+ ## 0.4.2
2
+
3
+ * 'Document#update_attributes' uses $ positional operator when used from within an embed_many association
4
+ * Require MongoDB 1.3.4 or greater
data/README.textile CHANGED
@@ -1,6 +1,10 @@
1
1
  h1. MongoDoc
2
2
 
3
- Version: Turbulence (0.4.1) 2010/03/17
3
+ Version: Turbulence (0.4.2) 2010/03/23
4
+
5
+ h2. What's New in Turbulence (0.4.2)
6
+
7
+ Support for the $ positional operator for in-place array updates. This requires MongoDB version 1.3.4 or higher.
4
8
 
5
9
  h2. What's New in Turbulence (0.4.1)
6
10
 
data/Rakefile CHANGED
@@ -10,8 +10,8 @@ begin
10
10
  gem.homepage = "http://github.com/leshill/mongodoc"
11
11
  gem.authors = ["Les Hill"]
12
12
  gem.add_dependency "activesupport", ">= 2.3.4"
13
- gem.add_dependency "mongo", "= 0.19"
14
- gem.add_dependency "mongo_ext", "= 0.19"
13
+ gem.add_dependency "mongo", "= 0.19.1"
14
+ gem.add_dependency "mongo_ext", "= 0.19.1"
15
15
  gem.add_dependency "durran-validatable", "= 2.0.1"
16
16
  gem.add_dependency "leshill-will_paginate", "= 2.3.11"
17
17
  gem.add_development_dependency "rspec", "= 1.3.0"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.1
1
+ 0.4.2
@@ -59,47 +59,37 @@ Feature: Partial Updates
59
59
  | 1 Main St. | Jacksonville | FL | 32218 |
60
60
  And I save the document 'hashrocket_hq'
61
61
 
62
- Scenario: Naive Update
62
+ Scenario: Update
63
63
  When I update the 'note' for 'contractor' to 'Knows MongoDB and MongoDoc'
64
- Then the document 'contractor' roundtrips
64
+ Then the last return value is true
65
+ And the document 'contractor' roundtrips
65
66
 
66
- Scenario: Naive Update on a has one
67
+ Scenario: Update on a has one
67
68
  When I update the 'street' for 'hq_address' to '320 1st Street North'
68
- Then the document 'hashrocket_hq' roundtrips
69
+ Then the last return value is true
70
+ And the document 'hashrocket_hq' roundtrips
69
71
 
70
- Scenario: Naive Update on a has many
72
+ Scenario: Update on a has many
71
73
  When 'hq_address' is the first address of 'hashrocket'
72
74
  And I update the 'street' for 'hq_address' to '320 1st Street North'
73
- Then the document 'hashrocket' roundtrips
75
+ Then the last return value is true
76
+ And the document 'hashrocket' roundtrips
74
77
 
75
- Scenario: Strict Update
76
- When I strict update the 'note' for 'contractor' to 'Knows MongoDB and MongoDoc'
77
- Then the document 'contractor' roundtrips
78
-
79
- Scenario: Strict Update on a has one
80
- When I strict update the 'street' for 'hq_address' to '320 1st Street North'
81
- Then the document 'hashrocket_hq' roundtrips
82
-
83
- Scenario: Strict Update on a has many
84
- When 'hq_address' is the first address of 'hashrocket'
85
- And I strict update the 'street' for 'hq_address' to '320 1st Street North'
86
- Then the document 'hashrocket' roundtrips
87
-
88
- Scenario: Failing Strict Update on a has one
78
+ Scenario: Failing Update on a has one
89
79
  When someone else changes the Address 'address' of 'hashrocket_hq' to
90
80
  | Street | City | State | Zip Code |
91
81
  | 1 Ocean Blvd. | Jacksonville | FL | 32218 |
92
- And I strict update the 'street' for 'hq_address' to '320 1st Street North'
82
+ And I update the 'street' for 'hq_address' to '320 1st Street North'
93
83
  Then the last return value is false
94
84
  And the document 'hashrocket_hq' does not roundtrip
95
85
 
96
- Scenario: Failing Strict Update on a has many
86
+ Scenario: Failing Update on a has many
97
87
  When 'hq_address' is the first address of 'hashrocket'
98
88
  And someone else changes the addresses of 'hashrocket':
99
89
  | Street | City | State | Zip Code |
100
90
  | 320 1st N, #712 | Jacksonville Beach | FL | 32250 |
101
91
  | 1001 Mulligan Street | Chicago | IL | 60611 |
102
92
  | 345 Avenida Grande | Santiago | Chile | |
103
- And I strict update the 'street' for 'hq_address' to '320 1st Street North'
93
+ And I update the 'street' for 'hq_address' to '320 1st Street North'
104
94
  Then the last return value is false
105
95
  And the document 'hashrocket' does not roundtrip
@@ -1,7 +1,6 @@
1
- When /^I(\sstrict\s|\s)update the '(.+)' for '(.+)' to '(.+)'$/ do |strict, attr, doc_name, value|
1
+ When /^I update the '(.+)' for '(.+)' to '(.+)'$/ do |attr, doc_name, value|
2
2
  doc = instance_variable_get("@#{doc_name}")
3
3
  attrs = {attr => value}
4
- attrs.merge!(:__strict__ => true) unless strict.blank?
5
4
  @last_return = doc.update_attributes(attrs)
6
5
  end
7
6
 
@@ -92,8 +92,8 @@ module MongoDoc
92
92
 
93
93
  protected
94
94
 
95
- def annotated_keys(src, attrs)
96
- assoc_path = "#{assoc_name}.#{index(src)}"
95
+ def annotated_keys(src, attrs, selector = false)
96
+ assoc_path = "#{assoc_name}" + (selector ? "" : ".$")
97
97
  annotated = {}
98
98
  attrs.each do |(key, value)|
99
99
  annotated["#{assoc_path}.#{key}"] = value
@@ -81,7 +81,7 @@ module MongoDoc
81
81
 
82
82
  protected
83
83
 
84
- def annotated_keys(src, attrs)
84
+ def annotated_keys(src, attrs, selector = false)
85
85
  assoc_path = "#{assoc_name}.#{index(src)}"
86
86
  annotated = {}
87
87
  attrs.each do |(key, value)|
@@ -11,8 +11,8 @@ module MongoDoc
11
11
  @_parent = parent
12
12
  end
13
13
 
14
- def _path_to_root(src, attrs)
15
- _parent._path_to_root(src, annotated_keys(src, attrs))
14
+ def _path_to_root(src, attrs, selector = false)
15
+ _parent._path_to_root(src, annotated_keys(src, attrs, selector), selector)
16
16
  end
17
17
 
18
18
  def _root=(root)
@@ -37,7 +37,7 @@ module MongoDoc
37
37
 
38
38
  protected
39
39
 
40
- def annotated_keys(src, hash)
40
+ def annotated_keys(src, hash, selector = false)
41
41
  annotated = {}
42
42
  hash.each do |(key, value)|
43
43
  annotated["#{assoc_name}.#{key}"] = value
@@ -40,9 +40,9 @@ module MongoDoc
40
40
  end
41
41
  end
42
42
 
43
- def _path_to_root(src, attrs)
43
+ def _path_to_root(src, attrs, selector = false)
44
44
  return attrs unless _parent
45
- _parent._path_to_root(self, attrs)
45
+ _parent._path_to_root(self, attrs, selector)
46
46
  end
47
47
 
48
48
  module ClassMethods
@@ -82,7 +82,7 @@ module MongoDoc
82
82
  end
83
83
 
84
84
  def verify_server_version(connection)
85
- raise UnsupportedServerVersionError.new('MongoDoc requires at least mongoDB version 1.3.2') unless connection.server_version >= "1.3.2"
85
+ raise UnsupportedServerVersionError.new('MongoDoc requires at least mongoDB version 1.3.2') unless connection.server_version >= "1.3.4"
86
86
  end
87
87
  end
88
88
  end
@@ -80,15 +80,10 @@ module MongoDoc
80
80
  end
81
81
 
82
82
  def update_attributes(attrs)
83
- strict = attrs.delete(:__strict__)
84
83
  self.attributes = attrs
85
84
  return save if new_record?
86
85
  return false unless valid?
87
- if strict
88
- _strict_update_attributes(_path_to_root(self, attrs), false)
89
- else
90
- _naive_update_attributes(_path_to_root(self, attrs), false)
91
- end
86
+ _update({}, attrs, false)
92
87
  end
93
88
 
94
89
  def update_attributes!(attrs)
@@ -96,11 +91,7 @@ module MongoDoc
96
91
  self.attributes = attrs
97
92
  return save! if new_record?
98
93
  raise DocumentInvalidError unless valid?
99
- if strict
100
- _strict_update_attributes(_path_to_root(self, attrs), true)
101
- else
102
- _naive_update_attributes(_path_to_root(self, attrs), true)
103
- end
94
+ _update({}, attrs, true)
104
95
  end
105
96
 
106
97
  module ClassMethods
@@ -139,21 +130,12 @@ module MongoDoc
139
130
  self.class.collection
140
131
  end
141
132
 
142
- def _naive_update_attributes(attrs, safe)
143
- return _root.send(:_naive_update_attributes, attrs, safe) if _root
144
- _update({}, attrs, safe)
145
- end
146
-
147
133
  def _remove
148
134
  _collection.remove({'_id' => _id})
149
135
  end
150
136
 
151
- def _strict_update_attributes(attrs, safe, selector = {})
152
- return _root.send(:_strict_update_attributes, attrs, safe, _path_to_root(self, '_id' => _id)) if _root
153
- _update(selector, attrs, safe)
154
- end
155
-
156
137
  def _update(selector, data, safe)
138
+ return _root.send(:_update, _path_to_root(self, {'_id' => _id}, true), _path_to_root(self, data), safe) if _root
157
139
  _collection.update({'_id' => _id}.merge(selector), MongoDoc::Query.set_modifier(data), :safe => safe)
158
140
  end
159
141
 
data/lib/mongo_doc.rb CHANGED
@@ -4,7 +4,7 @@ require 'validatable'
4
4
  require 'will_paginate/collection'
5
5
 
6
6
  module MongoDoc
7
- VERSION = '0.4.1'
7
+ VERSION = '0.4.2'
8
8
  end
9
9
 
10
10
  require 'mongo_doc/connection'
data/mongo_doc.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{mongo_doc}
8
- s.version = "0.4.1"
8
+ s.version = "0.4.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Les Hill"]
12
- s.date = %q{2010-03-17}
12
+ s.date = %q{2010-03-23}
13
13
  s.description = %q{ODM for MongoDB}
14
14
  s.email = %q{leshill@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.files = [
21
21
  ".document",
22
22
  ".gitignore",
23
+ "HISTORY.md",
23
24
  "LICENSE",
24
25
  "README.textile",
25
26
  "Rakefile",
@@ -120,6 +121,7 @@ Gem::Specification.new do |s|
120
121
  "perf/mongo_doc_runner.rb",
121
122
  "perf/ruby_driver_runner.rb",
122
123
  "script/console",
124
+ "spec/array_including_argument_matcher.rb",
123
125
  "spec/associations/collection_proxy_spec.rb",
124
126
  "spec/associations/document_proxy_spec.rb",
125
127
  "spec/associations/hash_proxy_spec.rb",
@@ -157,7 +159,8 @@ Gem::Specification.new do |s|
157
159
  s.rubygems_version = %q{1.3.6}
158
160
  s.summary = %q{ODM for MongoDB}
159
161
  s.test_files = [
160
- "spec/associations/collection_proxy_spec.rb",
162
+ "spec/array_including_argument_matcher.rb",
163
+ "spec/associations/collection_proxy_spec.rb",
161
164
  "spec/associations/document_proxy_spec.rb",
162
165
  "spec/associations/hash_proxy_spec.rb",
163
166
  "spec/associations_spec.rb",
@@ -194,16 +197,16 @@ Gem::Specification.new do |s|
194
197
 
195
198
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
196
199
  s.add_runtime_dependency(%q<activesupport>, [">= 2.3.4"])
197
- s.add_runtime_dependency(%q<mongo>, ["= 0.19"])
198
- s.add_runtime_dependency(%q<mongo_ext>, ["= 0.19"])
200
+ s.add_runtime_dependency(%q<mongo>, ["= 0.19.1"])
201
+ s.add_runtime_dependency(%q<mongo_ext>, ["= 0.19.1"])
199
202
  s.add_runtime_dependency(%q<durran-validatable>, ["= 2.0.1"])
200
203
  s.add_runtime_dependency(%q<leshill-will_paginate>, ["= 2.3.11"])
201
204
  s.add_development_dependency(%q<rspec>, ["= 1.3.0"])
202
205
  s.add_development_dependency(%q<cucumber>, ["= 0.6.2"])
203
206
  else
204
207
  s.add_dependency(%q<activesupport>, [">= 2.3.4"])
205
- s.add_dependency(%q<mongo>, ["= 0.19"])
206
- s.add_dependency(%q<mongo_ext>, ["= 0.19"])
208
+ s.add_dependency(%q<mongo>, ["= 0.19.1"])
209
+ s.add_dependency(%q<mongo_ext>, ["= 0.19.1"])
207
210
  s.add_dependency(%q<durran-validatable>, ["= 2.0.1"])
208
211
  s.add_dependency(%q<leshill-will_paginate>, ["= 2.3.11"])
209
212
  s.add_dependency(%q<rspec>, ["= 1.3.0"])
@@ -211,8 +214,8 @@ Gem::Specification.new do |s|
211
214
  end
212
215
  else
213
216
  s.add_dependency(%q<activesupport>, [">= 2.3.4"])
214
- s.add_dependency(%q<mongo>, ["= 0.19"])
215
- s.add_dependency(%q<mongo_ext>, ["= 0.19"])
217
+ s.add_dependency(%q<mongo>, ["= 0.19.1"])
218
+ s.add_dependency(%q<mongo_ext>, ["= 0.19.1"])
216
219
  s.add_dependency(%q<durran-validatable>, ["= 2.0.1"])
217
220
  s.add_dependency(%q<leshill-will_paginate>, ["= 2.3.11"])
218
221
  s.add_dependency(%q<rspec>, ["= 1.3.0"])
@@ -0,0 +1,62 @@
1
+ # From http://gist.github.com/62943
2
+ # Author http://github.com/trotter
3
+ module Spec
4
+ module Mocks
5
+ module ArgumentMatchers
6
+
7
+ class ArrayIncludingMatcher
8
+ # We'll allow an array of arguments to be passed in, so that you can do
9
+ # things like obj.should_receive(:blah).with(array_including('a', 'b'))
10
+ def initialize(*expected)
11
+ @expected = expected
12
+ end
13
+
14
+ # actual is the array (hopefully) passed to the method by the user.
15
+ # We'll check that it includes all the expected values, and return false
16
+ # if it doesn't or if we blow up because #include? is not defined.
17
+ def ==(actual)
18
+ @expected.each do |expected|
19
+ return false unless actual.include?(expected)
20
+ end
21
+ true
22
+ rescue NoMethodError => ex
23
+ return false
24
+ end
25
+
26
+ def description
27
+ "array_including(#{@expected.join(', ')})"
28
+ end
29
+ end
30
+
31
+ class ArrayNotIncludingMatcher
32
+ def initialize(*expected)
33
+ @expected = expected
34
+ end
35
+
36
+ def ==(actual)
37
+ @expected.each do |expected|
38
+ return false if actual.include?(expected)
39
+ end
40
+ true
41
+ rescue NoMethodError => ex
42
+ return false
43
+ end
44
+
45
+ def description
46
+ "array_not_including(#{@expected.join(', ')})"
47
+ end
48
+ end
49
+
50
+ # array_including is a helpful wrapper that allows us to actually type
51
+ # #with(array_including(...)) instead of ArrayIncludingMatcher.new(...)
52
+ def array_including(*args)
53
+ ArrayIncludingMatcher.new(*args)
54
+ end
55
+
56
+ def array_not_including(*args)
57
+ ArrayNotIncludingMatcher.new(*args)
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -7,10 +7,20 @@ describe MongoDoc::Associations::CollectionProxy do
7
7
  attr_accessor :name
8
8
  end
9
9
 
10
- let(:root) { stub('root', :register_save_observer => nil) }
10
+ let(:root) { CollectionProxyTest.new }
11
11
  let(:proxy) { MongoDoc::Associations::CollectionProxy.new(:assoc_name => 'embed_many_name', :assoc_class => CollectionProxyTest, :root => root, :parent => root) }
12
12
  let(:item) { CollectionProxyTest.new }
13
13
 
14
+ describe "#_path_to_root" do
15
+ it "inserts the association name and '$' when not a selector" do
16
+ proxy._path_to_root(CollectionProxyTest.new, 'name' => 'value').should == {"embed_many_name.$.name" => 'value'}
17
+ end
18
+
19
+ it "inserts the association name when a selector" do
20
+ proxy._path_to_root(CollectionProxyTest.new, {'name' => 'value'}, true).should == {"embed_many_name.name" => 'value'}
21
+ end
22
+ end
23
+
14
24
  context "#<<" do
15
25
  it "appends the item to the collection" do
16
26
  (proxy << item).should include(item)
@@ -7,11 +7,16 @@ describe MongoDoc::Associations::HashProxy do
7
7
  attr_accessor :name
8
8
  end
9
9
 
10
- let(:root) { stub('root', :register_save_observer => nil) }
10
+ let(:root) { HashProxyTest.new }
11
11
  let(:proxy) { MongoDoc::Associations::HashProxy.new(:assoc_name => 'embed_hash_name', :assoc_class => HashProxyTest, :root => root, :parent => root) }
12
12
  let(:item) { HashProxyTest.new }
13
13
  let(:other_item) {[1,2]}
14
14
 
15
+ it "inserts the association name and key into the _path_to_root" do
16
+ proxy.stub(:index).and_return('key')
17
+ proxy._path_to_root(HashProxyTest.new, 'name' => 'value').should == {"embed_hash_name.key.name" => 'value'}
18
+ end
19
+
15
20
  context "#[]=" do
16
21
  it "adds the item to the hash" do
17
22
  proxy['new'] = item
@@ -105,12 +105,12 @@ describe "MongoDoc::Connection.Connections" do
105
105
  let(:connection) { stub('connection') }
106
106
 
107
107
  it "raises when the server version is unsupported" do
108
- connection.stub(:server_version).and_return(Mongo::ServerVersion.new('1.3.1'))
108
+ connection.stub(:server_version).and_return(Mongo::ServerVersion.new('1.3.2'))
109
109
  lambda { MongoDoc::Connection.send(:verify_server_version, connection) }.should raise_error(MongoDoc::UnsupportedServerVersionError)
110
110
  end
111
111
 
112
112
  it "returns when the server version is supported" do
113
- connection.stub(:server_version).and_return(Mongo::ServerVersion.new('1.3.2'))
113
+ connection.stub(:server_version).and_return(Mongo::ServerVersion.new('1.3.4'))
114
114
  lambda { MongoDoc::Connection.send(:verify_server_version, connection) }.should_not raise_error(MongoDoc::UnsupportedServerVersionError)
115
115
  end
116
116
  end
@@ -258,219 +258,114 @@ describe "MongoDoc::Document" do
258
258
  end
259
259
 
260
260
  context "updating attributes" do
261
- class UpdateAttributesRoot
261
+ class UpdateAttributesChild
262
262
  include MongoDoc::Document
263
263
 
264
- embed :update_attributes_child
264
+ attr_accessor :child_data
265
265
  end
266
266
 
267
- class UpdateAttributesChild
267
+ class UpdateAttributes
268
268
  include MongoDoc::Document
269
269
 
270
270
  attr_accessor :data
271
+ embed :child
271
272
  end
272
273
 
273
- let(:data) {'data'}
274
+ let(:collection) { stub(:update => nil) }
274
275
 
275
- let(:attrs) {{:data => data}}
276
+ let(:new_doc) { UpdateAttributes.new }
276
277
 
277
- let(:path_attrs) {{'update_attributes_child.data' => data}}
278
-
279
- let(:doc) do
280
- doc = UpdateAttributesChild.new
281
- doc._id = 'id'
282
- doc.stub(:_naive_update_attributes)
278
+ let(:existing_doc) do
279
+ doc = UpdateAttributes.new
280
+ doc._id = 'exists'
281
+ doc.stub(:_collection).and_return(collection)
283
282
  doc
284
283
  end
285
284
 
286
- before do
287
- root = UpdateAttributesRoot.new
288
- root.update_attributes_child = doc
289
- root._id = 'id'
285
+ let(:child) do
286
+ child = UpdateAttributesChild.new
287
+ child._id = 'child exists'
288
+ existing_doc.child = child
289
+ child
290
290
  end
291
291
 
292
- context "#update_attributes" do
293
- it "delegates to save if the object is a new record" do
294
- check = 'check'
295
- doc.stub(:new_record?).and_return(true)
296
- doc.should_receive(:save).and_return(check)
297
- doc.update_attributes(attrs).should == check
292
+ describe "#update_attributes" do
293
+ it "delegates to save if the doc is a new record" do
294
+ new_doc.should_receive(:save)
295
+ new_doc.update_attributes(:data => 'data')
298
296
  end
299
297
 
300
- it "sets the attributes" do
301
- doc.update_attributes(attrs)
302
- doc.data.should == data
303
- end
298
+ context "with an existing doc" do
304
299
 
305
- it "normalizes the attributes to the parent" do
306
- doc.should_receive(:_path_to_root)
307
- doc.update_attributes(attrs)
308
- end
300
+ subject { existing_doc.update_attributes(:data => 'data') }
309
301
 
310
- it "validates" do
311
- doc.should_receive(:valid?)
312
- doc.update_attributes(attrs)
313
- end
302
+ it "sets the attributes" do
303
+ subject
304
+ existing_doc.data.should == 'data'
305
+ end
314
306
 
315
- it "returns false if the object is not valid" do
316
- doc.stub(:valid?).and_return(false)
317
- doc.update_attributes(attrs).should be_false
318
- end
307
+ it "validates the doc" do
308
+ existing_doc.should_receive(:valid?)
309
+ subject
310
+ end
319
311
 
320
- context "if valid" do
321
- context "and strict" do
322
- it "delegates to _strict_update_attributes" do
323
- strict_attrs = attrs.merge(:__strict__ => true)
324
- doc.should_receive(:_strict_update_attributes).with(path_attrs, false)
325
- doc.update_attributes(strict_attrs)
326
- end
312
+ it "returns false if the doc is not valid" do
313
+ existing_doc.stub(:valid?).and_return(false)
314
+ should be_false
327
315
  end
328
316
 
329
- context "and naive" do
330
- it "delegates to _naive_update_attributes" do
331
- doc.should_receive(:_naive_update_attributes).with(path_attrs, false)
332
- doc.update_attributes(attrs)
333
- end
317
+ it "delegates to collection update" do
318
+ collection.should_receive(:update).with({'_id' => existing_doc._id}, {'$set' => {:data => 'data'}}, :safe => false)
319
+ subject
334
320
  end
335
321
 
336
- it "returns the result of _naive_update_attributes" do
337
- result = 'check'
338
- doc.stub(:_naive_update_attributes).and_return(result)
339
- doc.update_attributes(attrs).should == result
322
+ context "that is embedded" do
323
+ it "delegates to the root's collection update" do
324
+ collection.should_receive(:update).with({'_id' => existing_doc._id, 'child._id' => child._id}, {'$set' => {'child.child_data' => 'data'}}, :safe => false)
325
+ child.update_attributes(:child_data => 'data')
326
+ end
340
327
  end
341
328
  end
342
329
  end
343
330
 
344
- context "#update_attributes!" do
345
- it "delegates to save! if the object is a new record" do
346
- check = 'check'
347
- doc.stub(:new_record?).and_return(true)
348
- doc.should_receive(:save!).and_return(check)
349
- doc.update_attributes!(attrs).should == check
350
- end
351
-
352
- it "sets the attributes" do
353
- doc.update_attributes!(attrs)
354
- doc.data.should == data
331
+ describe "#update_attributes!" do
332
+ it "delegates to save! if the doc is a new record" do
333
+ new_doc.should_receive(:save!)
334
+ new_doc.update_attributes!(:data => 'data')
355
335
  end
356
336
 
357
- it "normalizes the attributes to the parent" do
358
- doc.should_receive(:_path_to_root)
359
- doc.update_attributes!(attrs)
360
- end
361
-
362
- it "validates" do
363
- doc.should_receive(:valid?).and_return(true)
364
- doc.update_attributes!(attrs)
365
- end
337
+ context "with an existing doc" do
366
338
 
367
- it "raises if not valid" do
368
- doc.stub(:valid?).and_return(false)
369
- expect do
370
- doc.update_attributes!(attrs)
371
- end.should raise_error(MongoDoc::DocumentInvalidError)
372
- end
339
+ subject { existing_doc.update_attributes!(:data => 'data') }
373
340
 
374
- context "if valid" do
375
- context "and strict" do
376
- it "delegates to _strict_update_attributes with safe == true" do
377
- strict_attrs = attrs.merge(:__strict__ => true)
378
- doc.should_receive(:_strict_update_attributes).with(path_attrs, true)
379
- doc.update_attributes!(strict_attrs)
380
- end
341
+ it "sets the attributes" do
342
+ subject
343
+ existing_doc.data.should == 'data'
381
344
  end
382
345
 
383
- context "and naive" do
384
- it "delegates to _naive_update_attributes with safe == true" do
385
- doc.should_receive(:_naive_update_attributes).with(path_attrs, true)
386
- doc.update_attributes!(attrs)
387
- end
346
+ it "validates the doc" do
347
+ existing_doc.should_receive(:valid?).and_return(true)
348
+ subject
388
349
  end
389
350
 
390
- it "returns the result of _naive_update_attributes" do
391
- result = 'check'
392
- doc.stub(:_naive_update_attributes).and_return(result)
393
- doc.update_attributes!(attrs).should == result
351
+ it "raises if not valid" do
352
+ existing_doc.stub(:valid?).and_return(false)
353
+ expect do
354
+ subject
355
+ end.should raise_error(MongoDoc::DocumentInvalidError)
394
356
  end
395
- end
396
- end
397
- end
398
-
399
- context "#_naive_update_attributes" do
400
- class NaiveUpdateAttributes
401
- include MongoDoc::Document
402
- end
403
-
404
357
 
405
- let(:id) { 'id' }
406
-
407
- let(:attrs) { {:data => 'data'} }
408
-
409
- let(:safe) { false }
410
-
411
- let(:doc) do
412
- doc = NaiveUpdateAttributes.new
413
- doc.stub(:_id).and_return(id)
414
- doc
415
- end
416
-
417
- it "without a root delegates to _update" do
418
- doc.should_receive(:_update).with({}, attrs, safe)
419
- doc.send(:_naive_update_attributes, attrs, safe)
420
- end
421
-
422
- it "with a root, calls _naive_update_attributes on the root" do
423
- root = NaiveUpdateAttributes.new
424
- doc.stub(:_root).and_return(root)
425
- root.should_receive(:_naive_update_attributes).with(attrs, safe)
426
- doc.send(:_naive_update_attributes, attrs, safe)
427
- end
428
- end
429
-
430
- context "#_strict_update_attributes" do
431
- class StrictUpdateAttributes
432
- include MongoDoc::Document
433
- end
434
-
435
- let(:id) { 'id' }
436
-
437
- let(:attrs) { {:data => 'data'} }
438
-
439
- let(:selector) { {:selector => 'selector'} }
440
-
441
- let(:safe) { false }
442
-
443
- let(:doc) do
444
- doc = StrictUpdateAttributes.new
445
- doc.stub(:_id).and_return(id)
446
- doc
447
- end
448
-
449
- context "without a root" do
450
- it "without a root delegates to _update" do
451
- doc.should_receive(:_update).with(selector, attrs, safe)
452
- doc.send(:_strict_update_attributes, attrs, safe, selector)
453
- end
454
- end
455
-
456
- context "with a root" do
457
- let(:root) { StrictUpdateAttributes.new }
458
-
459
- before do
460
- doc.stub(:_root).and_return(root)
461
- end
462
-
463
- it "calls _path_to_root on our id" do
464
- root.stub(:_strict_update_attributes)
465
- doc.should_receive(:_path_to_root).with(doc, '_id' => id)
466
- doc.send(:_strict_update_attributes, attrs, safe)
467
- end
358
+ it "delegates to collection update" do
359
+ collection.should_receive(:update).with({'_id' => existing_doc._id}, {'$set' => {:data => 'data'}}, :safe => true)
360
+ subject
361
+ end
468
362
 
469
- it "calls _strict_update_attributes on the root with our selector" do
470
- selector = {'path._id' => id}
471
- doc.stub(:_path_to_root).with(doc, '_id' => id).and_return(selector)
472
- root.should_receive(:_strict_update_attributes).with(attrs, safe, selector)
473
- doc.send(:_strict_update_attributes, attrs, safe)
363
+ context "that is embedded" do
364
+ it "delegates to the root's collection update" do
365
+ collection.should_receive(:update).with({'_id' => existing_doc._id, 'child._id' => child._id}, {'$set' => {'child.child_data' => 'data'}}, :safe => true)
366
+ child.update_attributes!(:child_data => 'data')
367
+ end
368
+ end
474
369
  end
475
370
  end
476
371
  end
@@ -41,17 +41,17 @@ describe "Saving embedded documents" do
41
41
  end
42
42
  end
43
43
 
44
- context "update_attributes naive" do
44
+ context "update_attributes" do
45
45
  context "with no embed_many, update_attributes" do
46
46
  let(:root) { NestedChild.new(:leaf => leaf) }
47
47
 
48
- it "calls the root document's _naive_update_attributes with a full attribute path and not safe" do
49
- root.should_receive(:_naive_update_attributes).with({'leaf.data' => data}, false)
48
+ it "calls the root document's _update with a full attribute path and not safe" do
49
+ root.should_receive(:_update).with({"leaf._id"=>"id"}, {'leaf.data' => data}, false)
50
50
  leaf.update_attributes(:data => data)
51
51
  end
52
52
 
53
- it "(with bang!) calls the root document's _naive_update_attributes with a full attribute path and safe" do
54
- root.should_receive(:_naive_update_attributes).with({'leaf.data' => data}, true)
53
+ it "(with bang!) calls the root document's _update with a full attribute path and safe" do
54
+ root.should_receive(:_update).with({"leaf._id"=>"id"}, {'leaf.data' => data}, true)
55
55
  leaf.update_attributes!(:data => data)
56
56
  end
57
57
  end
@@ -59,51 +59,15 @@ describe "Saving embedded documents" do
59
59
  context "with embed_many, update_attributes" do
60
60
  let(:root) { NestedDocsRoot.new(:nested_children => [NestedChild.new(:leaf => leaf)]) }
61
61
 
62
- it "calls the root document's _naive_update_attributes with a full attribute path and not safe" do
63
- root.should_receive(:_naive_update_attributes).with({'nested_children.0.leaf.data' => data}, false)
62
+ it "calls the root document's _update with a full attribute path and not safe" do
63
+ root.should_receive(:_update).with({"nested_children.leaf._id"=>"id"}, {'nested_children.$.leaf.data' => data}, false)
64
64
  leaf.update_attributes(:data => data)
65
65
  end
66
66
 
67
- it "(with bang!) calls the root document's _naive_update_attributes with a full attribute path and safe" do
68
- root.should_receive(:_naive_update_attributes).with({'nested_children.0.leaf.data' => data}, true)
67
+ it "(with bang!) calls the root document's _update with a full attribute path and safe" do
68
+ root.should_receive(:_update).with({"nested_children.leaf._id"=>"id"}, {'nested_children.$.leaf.data' => data}, true)
69
69
  leaf.update_attributes!(:data => data)
70
70
  end
71
71
  end
72
72
  end
73
-
74
- context "update_attributes strict" do
75
- let(:leaf_id) { 'leaf_id' }
76
-
77
- before do
78
- leaf.stub(:_id).and_return(leaf_id)
79
- end
80
-
81
- context "with no embed_many, update_attributes" do
82
- let(:root) { NestedChild.new(:leaf => leaf) }
83
-
84
- it "calls the root document's _strict_update_attributes with a full attribute path and not safe" do
85
- root.should_receive(:_strict_update_attributes).with({'leaf.data' => data}, false, 'leaf._id' => leaf_id)
86
- leaf.update_attributes(:data => data, :__strict__ => true)
87
- end
88
-
89
- it "(with bang!) calls the root document's _naive_update_attributes with a full attribute path and safe" do
90
- root.should_receive(:_strict_update_attributes).with({'leaf.data' => data}, true, 'leaf._id' => leaf_id)
91
- leaf.update_attributes!(:data => data, :__strict__ => true)
92
- end
93
- end
94
-
95
- context "with embed_many, update_attributes" do
96
- let(:root) { NestedDocsRoot.new(:nested_children => [NestedChild.new(:leaf => leaf)]) }
97
-
98
- it "calls the root document's _naive_update_attributes with a full attribute path and not safe" do
99
- root.should_receive(:_strict_update_attributes).with({'nested_children.0.leaf.data' => data}, false, 'nested_children.0.leaf._id' => leaf_id)
100
- leaf.update_attributes(:data => data, :__strict__ => true)
101
- end
102
-
103
- it "(with bang!) calls the root document's _naive_update_attributes with a full attribute path and safe" do
104
- root.should_receive(:_strict_update_attributes).with({'nested_children.0.leaf.data' => data}, true, 'nested_children.0.leaf._id' => leaf_id)
105
- leaf.update_attributes!(:data => data, :__strict__ => true)
106
- end
107
- end
108
- end
109
73
  end
data/spec/index_spec.rb CHANGED
@@ -51,12 +51,12 @@ describe MongoDoc::Index do
51
51
  context "Compound index" do
52
52
 
53
53
  it "creates a compound index" do
54
- collection.should_receive(:create_index).with([[:first_name, Mongo::ASCENDING], [:last_name, Mongo::ASCENDING]], false)
54
+ collection.should_receive(:create_index).with(array_including([:first_name, Mongo::ASCENDING], [:last_name, Mongo::ASCENDING]), false)
55
55
  IndexTest.index(:first_name => :asc, :last_name => :asc)
56
56
  end
57
57
 
58
58
  it "creates a unique compound index" do
59
- collection.should_receive(:create_index).with([[:first_name, Mongo::ASCENDING], [:last_name, Mongo::ASCENDING]], true)
59
+ collection.should_receive(:create_index).with(array_including([:first_name, Mongo::ASCENDING], [:last_name, Mongo::ASCENDING]), true)
60
60
  IndexTest.index(:first_name => :asc, :last_name => :asc, :unique => true)
61
61
  end
62
62
  end
data/spec/spec_helper.rb CHANGED
@@ -5,6 +5,7 @@ require 'spec'
5
5
  require 'spec/autorun'
6
6
  require 'bson_matchers'
7
7
  require 'hash_matchers'
8
+ require 'array_including_argument_matcher'
8
9
  require 'document_ext'
9
10
 
10
11
  Spec::Runner.configure do |config|
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 4
8
- - 1
9
- version: 0.4.1
8
+ - 2
9
+ version: 0.4.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - Les Hill
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-03-17 00:00:00 -04:00
17
+ date: 2010-03-23 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -41,7 +41,8 @@ dependencies:
41
41
  segments:
42
42
  - 0
43
43
  - 19
44
- version: "0.19"
44
+ - 1
45
+ version: 0.19.1
45
46
  type: :runtime
46
47
  version_requirements: *id002
47
48
  - !ruby/object:Gem::Dependency
@@ -54,7 +55,8 @@ dependencies:
54
55
  segments:
55
56
  - 0
56
57
  - 19
57
- version: "0.19"
58
+ - 1
59
+ version: 0.19.1
58
60
  type: :runtime
59
61
  version_requirements: *id003
60
62
  - !ruby/object:Gem::Dependency
@@ -126,6 +128,7 @@ extra_rdoc_files:
126
128
  files:
127
129
  - .document
128
130
  - .gitignore
131
+ - HISTORY.md
129
132
  - LICENSE
130
133
  - README.textile
131
134
  - Rakefile
@@ -226,6 +229,7 @@ files:
226
229
  - perf/mongo_doc_runner.rb
227
230
  - perf/ruby_driver_runner.rb
228
231
  - script/console
232
+ - spec/array_including_argument_matcher.rb
229
233
  - spec/associations/collection_proxy_spec.rb
230
234
  - spec/associations/document_proxy_spec.rb
231
235
  - spec/associations/hash_proxy_spec.rb
@@ -287,6 +291,7 @@ signing_key:
287
291
  specification_version: 3
288
292
  summary: ODM for MongoDB
289
293
  test_files:
294
+ - spec/array_including_argument_matcher.rb
290
295
  - spec/associations/collection_proxy_spec.rb
291
296
  - spec/associations/document_proxy_spec.rb
292
297
  - spec/associations/hash_proxy_spec.rb