hashrocket-mongomapper 0.3.3

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.
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 +70 -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 +337 -0
  21. data/lib/mongomapper/dynamic_finder.rb +38 -0
  22. data/lib/mongomapper/embedded_document.rb +267 -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 +61 -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 +500 -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