shingara-mongomapper 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/.gitignore +7 -0
  2. data/History +70 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +39 -0
  5. data/Rakefile +73 -0
  6. data/VERSION +1 -0
  7. data/bin/mmconsole +56 -0
  8. data/lib/mongomapper.rb +77 -0
  9. data/lib/mongomapper/associations.rb +84 -0
  10. data/lib/mongomapper/associations/base.rb +69 -0
  11. data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
  12. data/lib/mongomapper/associations/belongs_to_proxy.rb +22 -0
  13. data/lib/mongomapper/associations/many_documents_proxy.rb +103 -0
  14. data/lib/mongomapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
  15. data/lib/mongomapper/associations/many_embedded_proxy.rb +17 -0
  16. data/lib/mongomapper/associations/many_polymorphic_proxy.rb +11 -0
  17. data/lib/mongomapper/associations/many_proxy.rb +6 -0
  18. data/lib/mongomapper/associations/proxy.rb +63 -0
  19. data/lib/mongomapper/callbacks.rb +106 -0
  20. data/lib/mongomapper/document.rb +348 -0
  21. data/lib/mongomapper/dynamic_finder.rb +38 -0
  22. data/lib/mongomapper/embedded_document.rb +265 -0
  23. data/lib/mongomapper/finder_options.rb +85 -0
  24. data/lib/mongomapper/key.rb +76 -0
  25. data/lib/mongomapper/observing.rb +50 -0
  26. data/lib/mongomapper/pagination.rb +52 -0
  27. data/lib/mongomapper/rails_compatibility/document.rb +15 -0
  28. data/lib/mongomapper/rails_compatibility/embedded_document.rb +25 -0
  29. data/lib/mongomapper/save_with_validation.rb +19 -0
  30. data/lib/mongomapper/serialization.rb +55 -0
  31. data/lib/mongomapper/serializers/json_serializer.rb +92 -0
  32. data/lib/mongomapper/support.rb +30 -0
  33. data/lib/mongomapper/validations.rb +47 -0
  34. data/mongomapper.gemspec +142 -0
  35. data/test/NOTE_ON_TESTING +1 -0
  36. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +53 -0
  37. data/test/functional/associations/test_belongs_to_proxy.rb +45 -0
  38. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +131 -0
  39. data/test/functional/associations/test_many_embedded_proxy.rb +106 -0
  40. data/test/functional/associations/test_many_polymorphic_proxy.rb +261 -0
  41. data/test/functional/associations/test_many_proxy.rb +295 -0
  42. data/test/functional/test_associations.rb +47 -0
  43. data/test/functional/test_callbacks.rb +85 -0
  44. data/test/functional/test_document.rb +952 -0
  45. data/test/functional/test_pagination.rb +81 -0
  46. data/test/functional/test_rails_compatibility.rb +30 -0
  47. data/test/functional/test_validations.rb +172 -0
  48. data/test/models.rb +139 -0
  49. data/test/test_helper.rb +67 -0
  50. data/test/unit/serializers/test_json_serializer.rb +157 -0
  51. data/test/unit/test_association_base.rb +144 -0
  52. data/test/unit/test_document.rb +123 -0
  53. data/test/unit/test_embedded_document.rb +526 -0
  54. data/test/unit/test_finder_options.rb +183 -0
  55. data/test/unit/test_key.rb +247 -0
  56. data/test/unit/test_mongomapper.rb +28 -0
  57. data/test/unit/test_observing.rb +101 -0
  58. data/test/unit/test_pagination.rb +113 -0
  59. data/test/unit/test_rails_compatibility.rb +34 -0
  60. data/test/unit/test_serializations.rb +52 -0
  61. data/test/unit/test_validations.rb +259 -0
  62. metadata +189 -0
@@ -0,0 +1,295 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class ManyProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ clear_all_collections
7
+ end
8
+
9
+ should "default reader to empty array" do
10
+ project = Project.new
11
+ project.statuses.should == []
12
+ end
13
+
14
+ should "allow adding to association like it was an array" do
15
+ project = Project.new
16
+ project.statuses << Status.new
17
+ project.statuses.push Status.new
18
+ project.statuses.concat Status.new
19
+ project.statuses.size.should == 3
20
+ end
21
+
22
+ should "be able to replace the association" do
23
+ project = Project.new
24
+ project.statuses = [Status.new("name" => "ready")]
25
+ project.save.should be_true
26
+
27
+ from_db = Project.find(project.id)
28
+ from_db.statuses.size.should == 1
29
+ from_db.statuses[0].name.should == "ready"
30
+ end
31
+
32
+ should "correctly assign foreign key when using <<, push and concat" do
33
+ project = Project.new
34
+ project.statuses << Status.new(:name => '<<')
35
+ project.statuses.push Status.new(:name => 'push')
36
+ project.statuses.concat Status.new(:name => 'concat')
37
+
38
+ from_db = Project.find(project.id)
39
+ from_db.statuses[0].project_id.should == project.id
40
+ from_db.statuses[1].project_id.should == project.id
41
+ from_db.statuses[2].project_id.should == project.id
42
+ end
43
+
44
+ context "build" do
45
+ should "assign foreign key" do
46
+ project = Project.create
47
+ status = project.statuses.build
48
+ status.project_id.should == project.id
49
+ end
50
+
51
+ should "allow assigning attributes" do
52
+ project = Project.create
53
+ status = project.statuses.build(:name => 'Foo')
54
+ status.name.should == 'Foo'
55
+ end
56
+ end
57
+
58
+ context "create" do
59
+ should "assign foreign key" do
60
+ project = Project.create
61
+ status = project.statuses.create
62
+ status.project_id.should == project.id
63
+ end
64
+
65
+ should "save record" do
66
+ project = Project.create
67
+ lambda {
68
+ project.statuses.create
69
+ }.should change { Status.count }
70
+ end
71
+
72
+ should "allow passing attributes" do
73
+ project = Project.create
74
+ status = project.statuses.create(:name => 'Foo!')
75
+ status.name.should == 'Foo!'
76
+ end
77
+ end
78
+
79
+ context "count" do
80
+ should "work scoped to association" do
81
+ project = Project.create
82
+ 3.times { project.statuses.create }
83
+
84
+ other_project = Project.create
85
+ 2.times { other_project.statuses.create }
86
+
87
+ project.statuses.count.should == 3
88
+ other_project.statuses.count.should == 2
89
+ end
90
+
91
+ should "work with conditions" do
92
+ project = Project.create
93
+ project.statuses.create(:name => 'Foo')
94
+ project.statuses.create(:name => 'Other 1')
95
+ project.statuses.create(:name => 'Other 2')
96
+
97
+ project.statuses.count(:name => 'Foo').should == 1
98
+ end
99
+ end
100
+
101
+ context "Unassociating documents" do
102
+ setup do
103
+ @project = Project.create
104
+ @project.statuses << Status.create(:name => '1')
105
+ @project.statuses << Status.create(:name => '2')
106
+
107
+ @project2 = Project.create
108
+ @project2.statuses << Status.create(:name => '1')
109
+ @project2.statuses << Status.create(:name => '2')
110
+ end
111
+
112
+ should "work with destroy all" do
113
+ @project.statuses.count.should == 2
114
+ @project.statuses.destroy_all
115
+ @project.statuses.count.should == 0
116
+
117
+ @project2.statuses.count.should == 2
118
+ Status.count.should == 2
119
+ end
120
+
121
+ should "work with destroy all and conditions" do
122
+ @project.statuses.count.should == 2
123
+ @project.statuses.destroy_all(:name => '1')
124
+ @project.statuses.count.should == 1
125
+
126
+ @project2.statuses.count.should == 2
127
+ Status.count.should == 3
128
+ end
129
+
130
+ should "work with delete all" do
131
+ @project.statuses.count.should == 2
132
+ @project.statuses.delete_all
133
+ @project.statuses.count.should == 0
134
+
135
+ @project2.statuses.count.should == 2
136
+ Status.count.should == 2
137
+ end
138
+
139
+ should "work with delete all and conditions" do
140
+ @project.statuses.count.should == 2
141
+ @project.statuses.delete_all(:name => '1')
142
+ @project.statuses.count.should == 1
143
+
144
+ @project2.statuses.count.should == 2
145
+ Status.count.should == 3
146
+ end
147
+
148
+ should "work with nullify" do
149
+ @project.statuses.count.should == 2
150
+ @project.statuses.nullify
151
+ @project.statuses.count.should == 0
152
+
153
+ @project2.statuses.count.should == 2
154
+ Status.count.should == 4
155
+ Status.count(:name => '1').should == 2
156
+ Status.count(:name => '2').should == 2
157
+ end
158
+ end
159
+
160
+ context "Finding scoped to association" do
161
+ setup do
162
+ @project1 = Project.new(:name => 'Project 1')
163
+ @brand_new = Status.create(:name => 'New', :position => 1 )
164
+ @complete = Status.create(:name => 'Complete', :position => 2)
165
+ @project1.statuses = [@brand_new, @complete]
166
+ @project1.save
167
+
168
+ @project2 = Project.create(:name => 'Project 2')
169
+ @in_progress = Status.create(:name => 'In Progress')
170
+ @archived = Status.create(:name => 'Archived')
171
+ @another_complete = Status.create(:name => 'Complete')
172
+ @project2.statuses = [@in_progress, @archived, @another_complete]
173
+ @project2.save
174
+ end
175
+
176
+ context "with :all" do
177
+ should "work" do
178
+ @project1.statuses.find(:all, :order => "position asc").should == [@brand_new, @complete]
179
+ end
180
+
181
+ should "work with conditions" do
182
+ statuses = @project1.statuses.find(:all, :conditions => {'name' => 'Complete'})
183
+ statuses.should == [@complete]
184
+ end
185
+
186
+ should "work with order" do
187
+ statuses = @project1.statuses.find(:all, :order => 'name asc')
188
+ statuses.should == [@complete, @brand_new]
189
+ end
190
+ end
191
+
192
+ context "with #all" do
193
+ should "work" do
194
+ @project1.statuses.all(:order => "position asc").should == [@brand_new, @complete]
195
+ end
196
+
197
+ should "work with conditions" do
198
+ statuses = @project1.statuses.all(:conditions => {'name' => 'Complete'})
199
+ statuses.should == [@complete]
200
+ end
201
+
202
+ should "work with order" do
203
+ statuses = @project1.statuses.all(:order => 'name asc')
204
+ statuses.should == [@complete, @brand_new]
205
+ end
206
+ end
207
+
208
+ context "with :first" do
209
+ should "work" do
210
+ @project1.statuses.find(:first, :order => 'name').should == @complete
211
+ end
212
+
213
+ should "work with conditions" do
214
+ status = @project1.statuses.find(:first, :conditions => {:name => 'Complete'})
215
+ status.should == @complete
216
+ end
217
+ end
218
+
219
+ context "with #first" do
220
+ should "work" do
221
+ @project1.statuses.first(:order => 'name').should == @complete
222
+ end
223
+
224
+ should "work with conditions" do
225
+ status = @project1.statuses.first(:conditions => {:name => 'Complete'})
226
+ status.should == @complete
227
+ end
228
+ end
229
+
230
+ context "with :last" do
231
+ should "work" do
232
+ @project1.statuses.find(:last, :order => "position asc").should == @complete
233
+ end
234
+
235
+ should "work with conditions" do
236
+ status = @project1.statuses.find(:last, :conditions => {:name => 'New'})
237
+ status.should == @brand_new
238
+ end
239
+ end
240
+
241
+ context "with #last" do
242
+ should "work" do
243
+ @project1.statuses.last(:order => "position asc").should == @complete
244
+ end
245
+
246
+ should "work with conditions" do
247
+ status = @project1.statuses.last(:conditions => {:name => 'New'})
248
+ status.should == @brand_new
249
+ end
250
+ end
251
+
252
+ context "with one id" do
253
+ should "work for id in association" do
254
+ @project1.statuses.find(@complete.id).should == @complete
255
+ end
256
+
257
+ should "not work for id not in association" do
258
+ lambda {
259
+ @project1.statuses.find(@archived.id)
260
+ }.should raise_error(MongoMapper::DocumentNotFound)
261
+ end
262
+ end
263
+
264
+ context "with multiple ids" do
265
+ should "work for ids in association" do
266
+ statuses = @project1.statuses.find(@brand_new.id, @complete.id)
267
+ statuses.should == [@brand_new, @complete]
268
+ end
269
+
270
+ should "not work for ids not in association" do
271
+ lambda {
272
+ @project1.statuses.find(@brand_new.id, @complete.id, @archived.id)
273
+ }.should raise_error(MongoMapper::DocumentNotFound)
274
+ end
275
+ end
276
+
277
+ context "with #paginate" do
278
+ setup do
279
+ @statuses = @project2.statuses.paginate(:per_page => 2, :page => 1, :order => 'name asc')
280
+ end
281
+
282
+ should "return total pages" do
283
+ @statuses.total_pages.should == 2
284
+ end
285
+
286
+ should "return total entries" do
287
+ @statuses.total_entries.should == 3
288
+ end
289
+
290
+ should "return the subject" do
291
+ @statuses.collect(&:name).should == %w(Archived Complete)
292
+ end
293
+ end
294
+ end
295
+ end
@@ -0,0 +1,47 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class AssociationsTest < Test::Unit::TestCase
5
+ def setup
6
+ clear_all_collections
7
+ end
8
+
9
+ should "allow changing class names" do
10
+ class AwesomeUser
11
+ include MongoMapper::Document
12
+
13
+ many :posts, :class_name => 'AssociationsTest::AwesomePost', :foreign_key => :creator_id
14
+ end
15
+
16
+ class AwesomeTag
17
+ include MongoMapper::EmbeddedDocument
18
+
19
+ key :name, String
20
+ key :post_id, String
21
+
22
+ belongs_to :post, :class_name => 'AssociationsTest::AwesomeUser'
23
+ end
24
+
25
+ class AwesomePost
26
+ include MongoMapper::Document
27
+
28
+ key :creator_id, String
29
+
30
+ belongs_to :creator, :class_name => 'AssociationsTest::AwesomeUser'
31
+ many :tags, :class_name => 'AssociationsTest::AwesomeTag', :foreign_key => :post_id
32
+ end
33
+
34
+ AwesomeUser.collection.clear
35
+ AwesomePost.collection.clear
36
+
37
+ user = AwesomeUser.create
38
+ tag1 = AwesomeTag.new(:name => 'awesome')
39
+ tag2 = AwesomeTag.new(:name => 'grand')
40
+ post1 = AwesomePost.create(:creator => user, :tags => [tag1])
41
+ post2 = AwesomePost.create(:creator => user, :tags => [tag2])
42
+ user.posts.should == [post1, post2]
43
+
44
+ post1_from_db = AwesomePost.find(post1.id)
45
+ post1_from_db.tags.should == [tag1]
46
+ end
47
+ end
@@ -0,0 +1,85 @@
1
+ require 'test_helper'
2
+
3
+ class CallbacksTest < Test::Unit::TestCase
4
+ context "Defining and running callbacks" do
5
+ setup do
6
+ @document = Class.new do
7
+ include MongoMapper::Document
8
+
9
+ key :name, String
10
+
11
+ [ :before_validation_on_create, :before_validation_on_update,
12
+ :before_validation, :after_validation,
13
+ :before_create, :after_create,
14
+ :before_update, :after_update,
15
+ :before_save, :after_save,
16
+ :before_destroy, :after_destroy].each do |callback|
17
+ callback_method = "#{callback}_callback"
18
+ send(callback, callback_method)
19
+ define_method(callback_method) do
20
+ history << callback.to_sym
21
+ end
22
+ end
23
+
24
+ def history
25
+ @history ||= []
26
+ end
27
+
28
+ def clear_history
29
+ @history = nil
30
+ end
31
+ end
32
+
33
+ clear_all_collections
34
+ end
35
+
36
+ should "get the order right for creating documents" do
37
+ doc = @document.create(:name => 'John Nunemaker')
38
+ doc.history.should == [:before_validation, :before_validation_on_create, :after_validation, :before_save, :before_create, :after_create, :after_save]
39
+ end
40
+
41
+ should "get the order right for updating documents" do
42
+ doc = @document.create(:name => 'John Nunemaker')
43
+ doc.clear_history
44
+ doc.name = 'John'
45
+ doc.save
46
+ doc.history.should == [:before_validation, :before_validation_on_update, :after_validation, :before_save, :before_update, :after_update, :after_save]
47
+ end
48
+
49
+ should "work for before and after validation" do
50
+ doc = @document.new(:name => 'John Nunemaker')
51
+ doc.valid?
52
+ doc.history.should include(:before_validation)
53
+ doc.history.should include(:after_validation)
54
+ end
55
+
56
+ should "work for before and after create" do
57
+ doc = @document.create(:name => 'John Nunemaker')
58
+ doc.history.should include(:before_create)
59
+ doc.history.should include(:after_create)
60
+ end
61
+
62
+ should "work for before and after update" do
63
+ doc = @document.create(:name => 'John Nunemaker')
64
+ doc.name = 'John Doe'
65
+ doc.save
66
+ doc.history.should include(:before_update)
67
+ doc.history.should include(:after_update)
68
+ end
69
+
70
+ should "work for before and after save" do
71
+ doc = @document.new
72
+ doc.name = 'John Doe'
73
+ doc.save
74
+ doc.history.should include(:before_save)
75
+ doc.history.should include(:after_save)
76
+ end
77
+
78
+ should "work for before and after destroy" do
79
+ doc = @document.create(:name => 'John Nunemaker')
80
+ doc.destroy
81
+ doc.history.should include(:before_destroy)
82
+ doc.history.should include(:after_destroy)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,952 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class DocumentTest < Test::Unit::TestCase
5
+ def setup
6
+ @document = Class.new do
7
+ include MongoMapper::Document
8
+ collection 'users'
9
+
10
+ key :first_name, String
11
+ key :last_name, String
12
+ key :age, Integer
13
+ end
14
+
15
+ @document.collection.clear
16
+ end
17
+
18
+ context "Saving a document with a custom id" do
19
+ should "clear custom id flag when saved" do
20
+ doc = @document.new(:id => '1234')
21
+ doc.using_custom_id?.should be_true
22
+ doc.save.should be_true
23
+ doc.using_custom_id?.should be_false
24
+ end
25
+ end
26
+
27
+ context "Loading a document from the database with keys that are not defined" do
28
+ setup do
29
+ @id = XGen::Mongo::Driver::ObjectID.new.to_s
30
+ @document.collection.insert({
31
+ :_id => @id,
32
+ :first_name => 'John',
33
+ :last_name => 'Nunemaker',
34
+ :age => 27,
35
+ :favorite_color => 'red',
36
+ :skills => ['ruby', 'rails', 'javascript', 'xhtml', 'css']
37
+ })
38
+ end
39
+
40
+ should "assign all keys from database" do
41
+ doc = @document.find(@id)
42
+ doc.first_name.should == 'John'
43
+ doc.last_name.should == 'Nunemaker'
44
+ doc.age.should == 27
45
+ doc.favorite_color.should == 'red'
46
+ doc.skills.should == ['ruby', 'rails', 'javascript', 'xhtml', 'css']
47
+ end
48
+ end
49
+
50
+ context "Document Class Methods" do
51
+ context "Using key with type Array" do
52
+ setup do
53
+ @document.key :tags, Array
54
+ end
55
+
56
+ should "give correct default" do
57
+ doc = @document.new
58
+ doc.tags.should == []
59
+ end
60
+
61
+ should "work with assignment" do
62
+ doc = @document.new
63
+ doc.tags = %w(foo bar)
64
+ doc.tags.should == %w(foo bar)
65
+ end
66
+
67
+ should "work with assignment after saving" do
68
+ doc = @document.new
69
+ doc.tags = %w(foo bar)
70
+ doc.save
71
+ doc.tags.should == %w(foo bar)
72
+ @document.find(doc.id).tags.should == %w(foo bar)
73
+ end
74
+
75
+ should "work with assignment then <<" do
76
+ doc = @document.new
77
+ doc.tags = []
78
+ doc.tags << "foo"
79
+ doc.tags.should == ["foo"]
80
+ end
81
+
82
+ should "work with <<" do
83
+ doc = @document.new
84
+ doc.tags << "foo"
85
+ doc.tags.should == ["foo"]
86
+ end
87
+
88
+ should "work with << then save" do
89
+ doc = @document.new
90
+ doc.tags << "foo"
91
+ doc.tags << "bar"
92
+ doc.save
93
+ doc.tags.should == %w(foo bar)
94
+ @document.find(doc.id).tags.should == %w(foo bar)
95
+ end
96
+ end
97
+
98
+ context "Using key with type Hash" do
99
+ setup do
100
+ @document.key :foo, Hash
101
+ end
102
+
103
+ should "give correct default" do
104
+ doc = @document.new
105
+ doc.foo.should == {}
106
+ end
107
+
108
+ should "work with []=" do
109
+ doc = @document.new
110
+ doc.foo["quux"] = "bar"
111
+ doc.foo["quux"].should == "bar"
112
+ doc.foo.should == { "quux" => "bar" }
113
+ end
114
+
115
+ should "work with indifferent access" do
116
+ doc = @document.new
117
+ doc.foo = {:baz => 'bar'}
118
+ doc.foo[:baz].should == 'bar'
119
+ doc.foo['baz'].should == 'bar'
120
+ end
121
+
122
+ should "work with indifferent access after save" do
123
+ doc = @document.new
124
+ doc.foo = {:baz => 'bar'}
125
+ doc.save
126
+
127
+ doc = @document.find(doc.id)
128
+ doc.foo[:baz].should == 'bar'
129
+ doc.foo['baz'].should == 'bar'
130
+ end
131
+ end
132
+
133
+ context "Saving a document with an embedded document" do
134
+ setup do
135
+ @document.class_eval do
136
+ key :foo, Address
137
+ end
138
+ end
139
+
140
+ should "embed embedded document" do
141
+ address = Address.new(:city => 'South Bend', :state => 'IN')
142
+ doc = @document.new(:foo => address)
143
+ doc.save
144
+ doc.foo.city.should == 'South Bend'
145
+ doc.foo.state.should == 'IN'
146
+
147
+ from_db = @document.find(doc.id)
148
+ from_db.foo.city.should == 'South Bend'
149
+ from_db.foo.state.should == 'IN'
150
+ end
151
+ end
152
+
153
+ context "Creating a single document" do
154
+ setup do
155
+ @doc_instance = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
156
+ end
157
+
158
+ should "create a document in correct collection" do
159
+ @document.count.should == 1
160
+ end
161
+
162
+ should "automatically set id" do
163
+ @doc_instance.id.should_not be_nil
164
+ @doc_instance.id.size.should == 24
165
+ end
166
+
167
+ should "no longer be new?" do
168
+ @doc_instance.new?.should be_false
169
+ end
170
+
171
+ should "return instance of document" do
172
+ @doc_instance.should be_instance_of(@document)
173
+ @doc_instance.first_name.should == 'John'
174
+ @doc_instance.last_name.should == 'Nunemaker'
175
+ @doc_instance.age.should == 27
176
+ end
177
+ end
178
+
179
+ context "Creating a document with no attributes provided" do
180
+ setup do
181
+ @document = Class.new do
182
+ include MongoMapper::Document
183
+ end
184
+ @document.collection.clear
185
+ end
186
+
187
+ should "create the document" do
188
+ lambda {
189
+ @document.create
190
+ }.should change { @document.count }.by(1)
191
+ end
192
+ end
193
+
194
+ context "Creating multiple documents" do
195
+ setup do
196
+ @doc_instances = @document.create([
197
+ {:first_name => 'John', :last_name => 'Nunemaker', :age => '27'},
198
+ {:first_name => 'Steve', :last_name => 'Smith', :age => '28'},
199
+ ])
200
+ end
201
+
202
+ should "create multiple documents" do
203
+ @document.count.should == 2
204
+ end
205
+
206
+ should "return an array of doc instances" do
207
+ @doc_instances.map do |doc_instance|
208
+ doc_instance.should be_instance_of(@document)
209
+ end
210
+ end
211
+ end
212
+
213
+ context "Updating a document" do
214
+ setup do
215
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
216
+ @doc_instance = @document.update(doc.id, {:age => 40})
217
+ end
218
+
219
+ should "update attributes provided" do
220
+ @doc_instance.age.should == 40
221
+ end
222
+
223
+ should "not update existing attributes that were not set to update" do
224
+ @doc_instance.first_name.should == 'John'
225
+ @doc_instance.last_name.should == 'Nunemaker'
226
+ end
227
+
228
+ should "not create new document" do
229
+ @document.count.should == 1
230
+ end
231
+ end
232
+
233
+ should "raise error when updating single doc if not provided id and attributes" do
234
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
235
+ lambda { @document.update }.should raise_error(ArgumentError)
236
+ lambda { @document.update(doc.id) }.should raise_error(ArgumentError)
237
+ lambda { @document.update(doc.id, [1]) }.should raise_error(ArgumentError)
238
+ end
239
+
240
+ context "Updating multiple documents" do
241
+ setup do
242
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
243
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
244
+
245
+ @doc_instances = @document.update({
246
+ @doc1.id => {:age => 30},
247
+ @doc2.id => {:age => 30},
248
+ })
249
+ end
250
+
251
+ should "not create any new documents" do
252
+ @document.count.should == 2
253
+ end
254
+
255
+ should "should return an array of doc instances" do
256
+ @doc_instances.map do |doc_instance|
257
+ doc_instance.should be_instance_of(@document)
258
+ end
259
+ end
260
+
261
+ should "update the documents" do
262
+ @document.find(@doc1.id).age.should == 30
263
+ @document.find(@doc2.id).age.should == 30
264
+ end
265
+ end
266
+
267
+ should "raise error when updating multiple documents if not a hash" do
268
+ lambda { @document.update([1, 2]) }.should raise_error(ArgumentError)
269
+ end
270
+
271
+ context "Finding documents" do
272
+ setup do
273
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
274
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
275
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
276
+ end
277
+
278
+ should "raise document not found if nothing provided" do
279
+ lambda { @document.find }.should raise_error(MongoMapper::DocumentNotFound)
280
+ end
281
+
282
+ context "with a single id" do
283
+ should "work" do
284
+ @document.find(@doc1.id).should == @doc1
285
+ end
286
+
287
+ should "raise error if document not found" do
288
+ lambda { @document.find(123) }.should raise_error(MongoMapper::DocumentNotFound)
289
+ end
290
+ end
291
+
292
+ context "with multiple id's" do
293
+ should "work as arguments" do
294
+ @document.find(@doc1.id, @doc2.id).should == [@doc1, @doc2]
295
+ end
296
+
297
+ should "work as array" do
298
+ @document.find([@doc1.id, @doc2.id]).should == [@doc1, @doc2]
299
+ end
300
+ end
301
+
302
+ context "with :all" do
303
+ should "find all documents" do
304
+ @document.find(:all, :order => 'first_name').should == [@doc1, @doc3, @doc2]
305
+ end
306
+
307
+ should "be able to add conditions" do
308
+ @document.find(:all, :conditions => {:first_name => 'John'}).should == [@doc1]
309
+ end
310
+ end
311
+
312
+ context "with #all" do
313
+ should "find all documents based on criteria" do
314
+ @document.all(:order => 'first_name').should == [@doc1, @doc3, @doc2]
315
+ @document.all(:conditions => {:last_name => 'Nunemaker'}, :order => 'age desc').should == [@doc1, @doc3]
316
+ end
317
+ end
318
+
319
+ context "with :first" do
320
+ should "find first document" do
321
+ @document.find(:first, :order => 'first_name').should == @doc1
322
+ end
323
+ end
324
+
325
+ context "with #first" do
326
+ should "find first document based on criteria" do
327
+ @document.first(:order => 'first_name').should == @doc1
328
+ @document.first(:conditions => {:age => 28}).should == @doc2
329
+ end
330
+ end
331
+
332
+ context "with :last" do
333
+ should "find last document" do
334
+ @document.find(:last, :order => 'age').should == @doc2
335
+ end
336
+ end
337
+
338
+ context "with #last" do
339
+ should "find last document based on criteria" do
340
+ @document.last(:order => 'age').should == @doc2
341
+ @document.last(:conditions => {:age => 28}).should == @doc2
342
+ end
343
+ end
344
+
345
+ context "with :find_by" do
346
+ should "find document based on argument" do
347
+ @document.find_by_first_name('John').should == @doc1
348
+ @document.find_by_last_name('Nunemaker', :order => 'age desc').should == @doc1
349
+ @document.find_by_age(27).should == @doc1
350
+ end
351
+
352
+ should "not raise error" do
353
+ @document.find_by_first_name('Mongo').should be_nil
354
+ end
355
+
356
+ should "define a method for each key" do
357
+ @document.methods(false).select { |e| e =~ /^find_by_/ }.size == @document.keys.size
358
+ end
359
+ end
360
+
361
+ context "with dynamic finders" do
362
+ should "find document based on all arguments" do
363
+ @document.find_by_first_name_and_last_name_and_age('John', 'Nunemaker', 27).should == @doc1
364
+ end
365
+
366
+ should "not find the document if an argument is wrong" do
367
+ @document.find_by_first_name_and_last_name_and_age('John', 'Nunemaker', 28).should be_nil
368
+ end
369
+
370
+ should "find all documents based on arguments" do
371
+ docs = @document.find_all_by_last_name('Nunemaker')
372
+ docs.should be_kind_of(Array)
373
+ docs.should include(@doc1)
374
+ docs.should include(@doc3)
375
+ end
376
+
377
+ should "find last document based on arguments" do
378
+ doc = @document.find_last_by_last_name('Nunemaker', :order => 'age')
379
+ doc.should == @doc1
380
+ end
381
+
382
+ should "initialize document with given arguments" do
383
+ doc = @document.find_or_initialize_by_first_name_and_last_name('David', 'Cuadrado')
384
+ doc.should be_new
385
+ doc.first_name.should == 'David'
386
+ end
387
+
388
+ should "not initialize document if document is found" do
389
+ doc = @document.find_or_initialize_by_first_name('John')
390
+ doc.should_not be_new
391
+ end
392
+
393
+ should "create document with given arguments" do
394
+ doc = @document.find_or_create_by_first_name_and_last_name('David', 'Cuadrado')
395
+ doc.should_not be_new
396
+ doc.first_name.should == 'David'
397
+ end
398
+
399
+ should "raise error if document is not found" do
400
+ lambda {@document.find_by_first_name_and_last_name!(1,2)}.should raise_error(MongoMapper::DocumentNotFound)
401
+ end
402
+ end
403
+ end # finding documents
404
+
405
+ context "Finding document by id" do
406
+ setup do
407
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
408
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
409
+ end
410
+
411
+ should "be able to find by id" do
412
+ @document.find_by_id(@doc1.id).should == @doc1
413
+ @document.find_by_id(@doc2.id).should == @doc2
414
+ end
415
+
416
+ should "return nil if document not found" do
417
+ @document.find_by_id(1234).should be(nil)
418
+ end
419
+ end
420
+
421
+ context "Deleting a document" do
422
+ setup do
423
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
424
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
425
+ @document.delete(@doc1.id)
426
+ end
427
+
428
+ should "remove document from collection" do
429
+ @document.count.should == 1
430
+ end
431
+
432
+ should "not remove other documents" do
433
+ @document.find(@doc2.id).should_not be(nil)
434
+ end
435
+ end
436
+
437
+ context "Deleting multiple documents" do
438
+ should "work with multiple arguments" do
439
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
440
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
441
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
442
+ @document.delete(@doc1.id, @doc2.id)
443
+
444
+ @document.count.should == 1
445
+ end
446
+
447
+ should "work with array as argument" do
448
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
449
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
450
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
451
+ @document.delete([@doc1.id, @doc2.id])
452
+
453
+ @document.count.should == 1
454
+ end
455
+ end
456
+
457
+ context "Deleting all documents" do
458
+ setup do
459
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
460
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
461
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
462
+ end
463
+
464
+ should "remove all documents when given no conditions" do
465
+ @document.delete_all
466
+ @document.count.should == 0
467
+ end
468
+
469
+ should "only remove matching documents when given conditions" do
470
+ @document.delete_all({:first_name => 'John'})
471
+ @document.count.should == 2
472
+ end
473
+
474
+ should "convert the conditions to mongo criteria" do
475
+ @document.delete_all(:age => [26, 27])
476
+ @document.count.should == 1
477
+ end
478
+ end
479
+
480
+ context "Destroying a document" do
481
+ setup do
482
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
483
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
484
+ @document.destroy(@doc1.id)
485
+ end
486
+
487
+ should "remove document from collection" do
488
+ @document.count.should == 1
489
+ end
490
+
491
+ should "not remove other documents" do
492
+ @document.find(@doc2.id).should_not be(nil)
493
+ end
494
+ end
495
+
496
+ context "Destroying multiple documents" do
497
+ should "work with multiple arguments" do
498
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
499
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
500
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
501
+ @document.destroy(@doc1.id, @doc2.id)
502
+
503
+ @document.count.should == 1
504
+ end
505
+
506
+ should "work with array as argument" do
507
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
508
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
509
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
510
+ @document.destroy([@doc1.id, @doc2.id])
511
+
512
+ @document.count.should == 1
513
+ end
514
+ end
515
+
516
+ context "Destroying all documents" do
517
+ setup do
518
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
519
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
520
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
521
+ end
522
+
523
+ should "remove all documents when given no conditions" do
524
+ @document.destroy_all
525
+ @document.count.should == 0
526
+ end
527
+
528
+ should "only remove matching documents when given conditions" do
529
+ @document.destroy_all(:first_name => 'John')
530
+ @document.count.should == 2
531
+ @document.destroy_all(:age => 26)
532
+ @document.count.should == 1
533
+ end
534
+
535
+ should "convert the conditions to mongo criteria" do
536
+ @document.destroy_all(:age => [26, 27])
537
+ @document.count.should == 1
538
+ end
539
+ end
540
+
541
+ context ":dependent" do
542
+ setup do
543
+ # FIXME: make use of already defined models
544
+ class ::Property
545
+ include MongoMapper::Document
546
+ end
547
+ Property.delete_all
548
+
549
+ class ::Thing
550
+ include MongoMapper::Document
551
+ key :name, String
552
+ end
553
+ Thing.delete_all
554
+ end
555
+
556
+ teardown do
557
+ Object.send :remove_const, 'Property' if defined?(::Property)
558
+ Object.send :remove_const, 'Thing' if defined?(::Thing)
559
+ end
560
+
561
+ context "many" do
562
+ context "=> destroy" do
563
+ setup do
564
+ Property.key :thing_id, String
565
+ Property.belongs_to :thing, :dependent => :destroy
566
+ Thing.many :properties, :dependent => :destroy
567
+
568
+ @thing = Thing.create(:name => "Tree")
569
+ @property1 = Property.create
570
+ @property2 = Property.create
571
+ @property3 = Property.create
572
+ @thing.properties << @property1
573
+ @thing.properties << @property2
574
+ @thing.properties << @property3
575
+ end
576
+
577
+ should "should destroy the associated documents" do
578
+ @thing.properties.count.should == 3
579
+ @thing.destroy
580
+ @thing.properties.count.should == 0
581
+ Property.count.should == 0
582
+ end
583
+ end
584
+
585
+ context "=> delete_all" do
586
+ setup do
587
+ Property.key :thing_id, String
588
+ Property.belongs_to :thing
589
+ Thing.has_many :properties, :dependent => :delete_all
590
+
591
+ @thing = Thing.create(:name => "Tree")
592
+ @property1 = Property.create
593
+ @property2 = Property.create
594
+ @property3 = Property.create
595
+ @thing.properties << @property1
596
+ @thing.properties << @property2
597
+ @thing.properties << @property3
598
+ end
599
+
600
+ should "should delete associated documents" do
601
+ @thing.properties.count.should == 3
602
+ @thing.destroy
603
+ @thing.properties.count.should == 0
604
+ Property.count.should == 0
605
+ end
606
+ end
607
+
608
+ context "=> nullify" do
609
+ setup do
610
+ Property.key :thing_id, String
611
+ Property.belongs_to :thing
612
+ Thing.has_many :properties, :dependent => :nullify
613
+
614
+ @thing = Thing.create(:name => "Tree")
615
+ @property1 = Property.create
616
+ @property2 = Property.create
617
+ @property3 = Property.create
618
+ @thing.properties << @property1
619
+ @thing.properties << @property2
620
+ @thing.properties << @property3
621
+ end
622
+
623
+ should "should nullify relationship but not destroy associated documents" do
624
+ @thing.properties.count.should == 3
625
+ @thing.destroy
626
+ @thing.properties.count.should == 0
627
+ Property.count.should == 3
628
+ end
629
+ end
630
+ end
631
+
632
+ context "belongs_to" do
633
+ context "=> destroy" do
634
+ setup do
635
+ Property.key :thing_id, String
636
+ Property.belongs_to :thing, :dependent => :destroy
637
+ Thing.has_many :properties
638
+
639
+ @thing = Thing.create(:name => "Tree")
640
+ @property1 = Property.create
641
+ @property2 = Property.create
642
+ @property3 = Property.create
643
+ @thing.properties << @property1
644
+ @thing.properties << @property2
645
+ @thing.properties << @property3
646
+ end
647
+
648
+ should "destroy the thing" do
649
+ Thing.count.should == 1
650
+ @property1.destroy
651
+ Thing.count.should == 0
652
+ @property1.thing.should be_frozen
653
+ end
654
+ end
655
+ end
656
+ end
657
+
658
+ context "Counting documents in collection" do
659
+ setup do
660
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
661
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
662
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
663
+ end
664
+
665
+ should "count all with no arguments" do
666
+ @document.count.should == 3
667
+ end
668
+
669
+ should "return 0 if there are no documents in the collection" do
670
+ @document.delete_all
671
+ @document.count.should == 0
672
+ end
673
+
674
+ should "return 0 if the collection does not exist" do
675
+ klass = Class.new do
676
+ include MongoMapper::Document
677
+ collection 'foobarbazwickdoesnotexist'
678
+ end
679
+
680
+ klass.count.should == 0
681
+ end
682
+
683
+ should "return count for matching documents if conditions provided" do
684
+ @document.count(:age => 27).should == 1
685
+ end
686
+
687
+ should "convert the conditions to mongo criteria" do
688
+ @document.count(:age => [26, 27]).should == 2
689
+ end
690
+ end
691
+
692
+ context "Indexing" do
693
+ setup do
694
+ @document.collection.drop_indexes
695
+ end
696
+
697
+ should "allow creating index for a key" do
698
+ index_name = nil
699
+ lambda {
700
+ index_name = @document.ensure_index :first_name
701
+ }.should change { @document.collection.index_information.size }.by(1)
702
+
703
+ index_name.should == 'first_name_1'
704
+ index = @document.collection.index_information[index_name]
705
+ index.should_not be_nil
706
+ index.should include(['first_name', 1])
707
+ end
708
+
709
+ should "allow creating unique index for a key" do
710
+ @document.collection.expects(:create_index).with(:first_name, true)
711
+ @document.ensure_index :first_name, :unique => true
712
+ end
713
+
714
+ should "allow creating index on multiple keys" do
715
+ index_name = nil
716
+ lambda {
717
+ index_name = @document.ensure_index [[:first_name, 1], [:last_name, -1]]
718
+ }.should change { @document.collection.index_information.size }.by(1)
719
+
720
+ [ 'first_name_1_last_name_-1', 'last_name_-1_first_name_1' ].should include(index_name)
721
+
722
+ index = @document.collection.index_information[index_name]
723
+ index.should_not be_nil
724
+ index.should include(['first_name', 1])
725
+ index.should include(['last_name', -1])
726
+ end
727
+
728
+ should "work with :index shortcut when defining key" do
729
+ @document.expects(:ensure_index).with('father').returns(nil)
730
+ @document.key :father, String, :index => true
731
+ end
732
+ end
733
+ end # Document Class Methods
734
+
735
+ context "Saving a new document" do
736
+ setup do
737
+ @doc = @document.new(:first_name => 'John', :age => '27')
738
+ @doc.save
739
+ end
740
+
741
+ should "insert document into the collection" do
742
+ @document.count.should == 1
743
+ end
744
+
745
+ should "assign an id for the document" do
746
+ @doc.id.should_not be(nil)
747
+ @doc.id.size.should == 24
748
+ end
749
+
750
+ should "save attributes" do
751
+ @doc.first_name.should == 'John'
752
+ @doc.age.should == 27
753
+ end
754
+
755
+ should "update attributes in the database" do
756
+ from_db = @document.find(@doc.id)
757
+ from_db.should == @doc
758
+ from_db.first_name.should == 'John'
759
+ from_db.age.should == 27
760
+ end
761
+
762
+ should "allow to add custom attributes to the document" do
763
+ @doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male', :tags => [1, "2"])
764
+ @doc.save
765
+ from_db = @document.find(@doc.id)
766
+ from_db.gender.should == 'male'
767
+ from_db.tags.should == [1, "2"]
768
+ end
769
+
770
+ should "allow to use custom methods to assign properties" do
771
+ person = RealPerson.new(:realname => "David")
772
+ person.save
773
+ from_db = RealPerson.find(person.id)
774
+ from_db.name.should == "David"
775
+ end
776
+ end
777
+
778
+ context "Saving an existing document" do
779
+ setup do
780
+ @doc = @document.create(:first_name => 'John', :age => '27')
781
+ @doc.first_name = 'Johnny'
782
+ @doc.age = 30
783
+ @doc.save
784
+ end
785
+
786
+ should "not insert document into collection" do
787
+ @document.count.should == 1
788
+ end
789
+
790
+ should "update attributes" do
791
+ @doc.first_name.should == 'Johnny'
792
+ @doc.age.should == 30
793
+ end
794
+
795
+ should "update attributes in the database" do
796
+ from_db = @document.find(@doc.id)
797
+ from_db.first_name.should == 'Johnny'
798
+ from_db.age.should == 30
799
+ end
800
+
801
+ should "allow to update custom attributes" do
802
+ @doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male')
803
+ @doc.gender = 'Male'
804
+ @doc.save
805
+ from_db = @document.find(@doc.id)
806
+ from_db.gender.should == 'Male'
807
+ end
808
+ end
809
+
810
+ context "Calling update attributes on a new document" do
811
+ setup do
812
+ @doc = @document.new(:first_name => 'John', :age => '27')
813
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
814
+ end
815
+
816
+ should "insert document into the collection" do
817
+ @document.count.should == 1
818
+ end
819
+
820
+ should "assign an id for the document" do
821
+ @doc.id.should_not be(nil)
822
+ @doc.id.size.should == 24
823
+ end
824
+
825
+ should "save attributes" do
826
+ @doc.first_name.should == 'Johnny'
827
+ @doc.age.should == 30
828
+ end
829
+
830
+ should "update attributes in the database" do
831
+ from_db = @document.find(@doc.id)
832
+ from_db.should == @doc
833
+ from_db.first_name.should == 'Johnny'
834
+ from_db.age.should == 30
835
+ end
836
+
837
+ should "allow to update custom attributes" do
838
+ @doc.update_attributes(:gender => 'mALe')
839
+ from_db = @document.find(@doc.id)
840
+ from_db.gender.should == 'mALe'
841
+ end
842
+ end
843
+
844
+ context "Updating an existing document using update attributes" do
845
+ setup do
846
+ @doc = @document.create(:first_name => 'John', :age => '27')
847
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
848
+ end
849
+
850
+ should "not insert document into collection" do
851
+ @document.count.should == 1
852
+ end
853
+
854
+ should "update attributes" do
855
+ @doc.first_name.should == 'Johnny'
856
+ @doc.age.should == 30
857
+ end
858
+
859
+ should "update attributes in the database" do
860
+ from_db = @document.find(@doc.id)
861
+ from_db.first_name.should == 'Johnny'
862
+ from_db.age.should == 30
863
+ end
864
+ end
865
+
866
+ context "update_attributes" do
867
+ setup do
868
+ @document.key :foo, String, :required => true
869
+ end
870
+
871
+ should "return true if document valid" do
872
+ @document.new.update_attributes(:foo => 'bar').should be_true
873
+ end
874
+
875
+ should "return false if document not valid" do
876
+ @document.new.update_attributes({}).should be_false
877
+ end
878
+ end
879
+
880
+ context "Destroying a document that exists" do
881
+ setup do
882
+ @doc = @document.create(:first_name => 'John', :age => '27')
883
+ @doc.destroy
884
+ end
885
+
886
+ should "remove the document from the collection" do
887
+ @document.count.should == 0
888
+ end
889
+
890
+ should "raise error if assignment is attempted" do
891
+ lambda { @doc.first_name = 'Foo' }.should raise_error(TypeError)
892
+ end
893
+
894
+ should "do nothing if destroy is called again" do
895
+ @doc.destroy.should be_false
896
+ end
897
+ end
898
+
899
+ context "Destroying a document that is a new" do
900
+ setup do
901
+ setup do
902
+ @doc = @document.new(:first_name => 'John Nunemaker', :age => '27')
903
+ @doc.destroy
904
+ end
905
+
906
+ should "not affect collection count" do
907
+ @document.collection.count.should == 0
908
+ end
909
+
910
+ should "raise error if assignment is attempted" do
911
+ lambda { @doc.first_name = 'Foo' }.should raise_error(TypeError)
912
+ end
913
+ end
914
+ end
915
+
916
+ context "timestamping" do
917
+ setup do
918
+ @document.timestamps!
919
+ end
920
+
921
+ should "set created_at and updated_at on create" do
922
+ doc = @document.new(:first_name => 'John', :age => 27)
923
+ doc.created_at.should be(nil)
924
+ doc.updated_at.should be(nil)
925
+ doc.save
926
+ doc.created_at.should_not be(nil)
927
+ doc.updated_at.should_not be(nil)
928
+ end
929
+
930
+ should "set updated_at on field update but leave created_at alone" do
931
+ doc = @document.create(:first_name => 'John', :age => 27)
932
+ old_created_at = doc.created_at
933
+ old_updated_at = doc.updated_at
934
+ doc.first_name = 'Johnny'
935
+ doc.save
936
+ doc.created_at.should == old_created_at
937
+ doc.updated_at.should_not == old_updated_at
938
+ end
939
+
940
+ should "set updated_at on document update but leave created_at alone" do
941
+ doc = @document.create(:first_name => 'John', :age => 27)
942
+ old_created_at = doc.created_at
943
+ old_updated_at = doc.updated_at
944
+ sleep 1 # this annoys me
945
+ @document.update(doc._id, { :first_name => 'Johnny' })
946
+
947
+ from_db = @document.find(doc.id)
948
+ from_db.created_at.to_i.should == old_created_at.to_i
949
+ from_db.updated_at.to_i.should_not == old_updated_at.to_i
950
+ end
951
+ end
952
+ end