danielharan-mongo_mapper 0.6.5

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 (74) hide show
  1. data/.gitignore +10 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +53 -0
  4. data/Rakefile +55 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +60 -0
  7. data/lib/mongo_mapper.rb +134 -0
  8. data/lib/mongo_mapper/associations.rb +183 -0
  9. data/lib/mongo_mapper/associations/base.rb +110 -0
  10. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
  11. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +22 -0
  12. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +25 -0
  13. data/lib/mongo_mapper/associations/many_documents_proxy.rb +127 -0
  14. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
  15. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +53 -0
  16. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
  17. data/lib/mongo_mapper/associations/many_proxy.rb +6 -0
  18. data/lib/mongo_mapper/associations/proxy.rb +80 -0
  19. data/lib/mongo_mapper/callbacks.rb +109 -0
  20. data/lib/mongo_mapper/dirty.rb +136 -0
  21. data/lib/mongo_mapper/document.rb +481 -0
  22. data/lib/mongo_mapper/dynamic_finder.rb +35 -0
  23. data/lib/mongo_mapper/embedded_document.rb +386 -0
  24. data/lib/mongo_mapper/finder_options.rb +133 -0
  25. data/lib/mongo_mapper/key.rb +36 -0
  26. data/lib/mongo_mapper/observing.rb +50 -0
  27. data/lib/mongo_mapper/pagination.rb +53 -0
  28. data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
  29. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +27 -0
  30. data/lib/mongo_mapper/serialization.rb +54 -0
  31. data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
  32. data/lib/mongo_mapper/support.rb +193 -0
  33. data/lib/mongo_mapper/validations.rb +41 -0
  34. data/mongo_mapper.gemspec +171 -0
  35. data/specs.watchr +32 -0
  36. data/test/NOTE_ON_TESTING +1 -0
  37. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
  38. data/test/functional/associations/test_belongs_to_proxy.rb +48 -0
  39. data/test/functional/associations/test_many_documents_as_proxy.rb +246 -0
  40. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +156 -0
  41. data/test/functional/associations/test_many_embedded_proxy.rb +196 -0
  42. data/test/functional/associations/test_many_polymorphic_proxy.rb +339 -0
  43. data/test/functional/associations/test_many_proxy.rb +384 -0
  44. data/test/functional/test_associations.rb +44 -0
  45. data/test/functional/test_binary.rb +18 -0
  46. data/test/functional/test_callbacks.rb +85 -0
  47. data/test/functional/test_dirty.rb +159 -0
  48. data/test/functional/test_document.rb +1180 -0
  49. data/test/functional/test_embedded_document.rb +125 -0
  50. data/test/functional/test_logger.rb +20 -0
  51. data/test/functional/test_pagination.rb +95 -0
  52. data/test/functional/test_rails_compatibility.rb +25 -0
  53. data/test/functional/test_string_id_compatibility.rb +72 -0
  54. data/test/functional/test_validations.rb +369 -0
  55. data/test/models.rb +271 -0
  56. data/test/support/custom_matchers.rb +55 -0
  57. data/test/support/timing.rb +16 -0
  58. data/test/test_helper.rb +27 -0
  59. data/test/unit/serializers/test_json_serializer.rb +189 -0
  60. data/test/unit/test_association_base.rb +166 -0
  61. data/test/unit/test_document.rb +204 -0
  62. data/test/unit/test_dynamic_finder.rb +125 -0
  63. data/test/unit/test_embedded_document.rb +718 -0
  64. data/test/unit/test_finder_options.rb +296 -0
  65. data/test/unit/test_key.rb +172 -0
  66. data/test/unit/test_mongo_mapper.rb +65 -0
  67. data/test/unit/test_observing.rb +101 -0
  68. data/test/unit/test_pagination.rb +113 -0
  69. data/test/unit/test_rails_compatibility.rb +49 -0
  70. data/test/unit/test_serializations.rb +52 -0
  71. data/test/unit/test_support.rb +342 -0
  72. data/test/unit/test_time_zones.rb +40 -0
  73. data/test/unit/test_validations.rb +503 -0
  74. metadata +233 -0
@@ -0,0 +1,384 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class ManyProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ Project.collection.remove
7
+ Status.collection.remove
8
+ end
9
+
10
+ should "default reader to empty array" do
11
+ project = Project.new
12
+ project.statuses.should == []
13
+ end
14
+
15
+ should "allow adding to association like it was an array" do
16
+ project = Project.new
17
+ project.statuses << Status.new(:name => 'Foo1!')
18
+ project.statuses.push Status.new(:name => 'Foo2!')
19
+ project.statuses.concat Status.new(:name => 'Foo3!')
20
+ project.statuses.size.should == 3
21
+ end
22
+
23
+ should "be able to replace the association" do
24
+ project = Project.new
25
+ project.statuses = [Status.new("name" => "ready")]
26
+ project.save.should be_true
27
+
28
+ project = project.reload
29
+ project.statuses.size.should == 1
30
+ project.statuses[0].name.should == "ready"
31
+ end
32
+
33
+ should "correctly assign foreign key when using <<, push and concat" do
34
+ project = Project.new
35
+ project.statuses << Status.new(:name => '<<')
36
+ project.statuses.push Status.new(:name => 'push')
37
+ project.statuses.concat Status.new(:name => 'concat')
38
+
39
+ project = project.reload
40
+ project.statuses[0].project_id.should == project._id
41
+ project.statuses[1].project_id.should == project._id
42
+ project.statuses[2].project_id.should == project._id
43
+ end
44
+
45
+ context "build" do
46
+ should "assign foreign key" do
47
+ project = Project.create
48
+ status = project.statuses.build
49
+ status.project_id.should == project._id
50
+ end
51
+
52
+ should "allow assigning attributes" do
53
+ project = Project.create
54
+ status = project.statuses.build(:name => 'Foo')
55
+ status.name.should == 'Foo'
56
+ end
57
+ end
58
+
59
+ context "create" do
60
+ should "assign foreign key" do
61
+ project = Project.create
62
+ status = project.statuses.create(:name => 'Foo!')
63
+ status.project_id.should == project._id
64
+ end
65
+
66
+ should "save record" do
67
+ project = Project.create
68
+ lambda {
69
+ project.statuses.create(:name => 'Foo!')
70
+ }.should change { Status.count }
71
+ end
72
+
73
+ should "allow passing attributes" do
74
+ project = Project.create
75
+ status = project.statuses.create(:name => 'Foo!')
76
+ status.name.should == 'Foo!'
77
+ end
78
+ end
79
+
80
+ context "create!" do
81
+ should "assign foreign key" do
82
+ project = Project.create
83
+ status = project.statuses.create!(:name => 'Foo!')
84
+ status.project_id.should == project._id
85
+ end
86
+
87
+ should "save record" do
88
+ project = Project.create
89
+ lambda {
90
+ project.statuses.create!(:name => 'Foo!')
91
+ }.should change { Status.count }
92
+ end
93
+
94
+ should "allow passing attributes" do
95
+ project = Project.create
96
+ status = project.statuses.create!(:name => 'Foo!')
97
+ status.name.should == 'Foo!'
98
+ end
99
+
100
+ should "raise exception if not valid" do
101
+ project = Project.create
102
+ lambda {
103
+ project.statuses.create!(:name => nil)
104
+ }.should raise_error(MongoMapper::DocumentNotValid)
105
+ end
106
+ end
107
+
108
+ context "count" do
109
+ should "work scoped to association" do
110
+ project = Project.create
111
+ 3.times { project.statuses.create(:name => 'Foo!') }
112
+
113
+ other_project = Project.create
114
+ 2.times { other_project.statuses.create(:name => 'Foo!') }
115
+
116
+ project.statuses.count.should == 3
117
+ other_project.statuses.count.should == 2
118
+ end
119
+
120
+ should "work with conditions" do
121
+ project = Project.create
122
+ project.statuses.create(:name => 'Foo')
123
+ project.statuses.create(:name => 'Other 1')
124
+ project.statuses.create(:name => 'Other 2')
125
+
126
+ project.statuses.count(:name => 'Foo').should == 1
127
+ end
128
+ end
129
+
130
+ context "Unassociating documents" do
131
+ setup do
132
+ @project = Project.create
133
+ @project.statuses << Status.create(:name => '1')
134
+ @project.statuses << Status.create(:name => '2')
135
+
136
+ @project2 = Project.create
137
+ @project2.statuses << Status.create(:name => '1')
138
+ @project2.statuses << Status.create(:name => '2')
139
+ end
140
+
141
+ should "work with destroy all" do
142
+ @project.statuses.count.should == 2
143
+ @project.statuses.destroy_all
144
+ @project.statuses.count.should == 0
145
+
146
+ @project2.statuses.count.should == 2
147
+ Status.count.should == 2
148
+ end
149
+
150
+ should "work with destroy all and conditions" do
151
+ @project.statuses.count.should == 2
152
+ @project.statuses.destroy_all(:name => '1')
153
+ @project.statuses.count.should == 1
154
+
155
+ @project2.statuses.count.should == 2
156
+ Status.count.should == 3
157
+ end
158
+
159
+ should "work with delete all" do
160
+ @project.statuses.count.should == 2
161
+ @project.statuses.delete_all
162
+ @project.statuses.count.should == 0
163
+
164
+ @project2.statuses.count.should == 2
165
+ Status.count.should == 2
166
+ end
167
+
168
+ should "work with delete all and conditions" do
169
+ @project.statuses.count.should == 2
170
+ @project.statuses.delete_all(:name => '1')
171
+ @project.statuses.count.should == 1
172
+
173
+ @project2.statuses.count.should == 2
174
+ Status.count.should == 3
175
+ end
176
+
177
+ should "work with nullify" do
178
+ @project.statuses.count.should == 2
179
+ @project.statuses.nullify
180
+ @project.statuses.count.should == 0
181
+
182
+ @project2.statuses.count.should == 2
183
+ Status.count.should == 4
184
+ Status.count(:name => '1').should == 2
185
+ Status.count(:name => '2').should == 2
186
+ end
187
+ end
188
+
189
+ context "Finding scoped to association" do
190
+ setup do
191
+ @project1 = Project.new(:name => 'Project 1')
192
+ @brand_new = Status.create(:name => 'New', :position => 1 )
193
+ @complete = Status.create(:name => 'Complete', :position => 2)
194
+ @project1.statuses = [@brand_new, @complete]
195
+ @project1.save
196
+
197
+ @project2 = Project.create(:name => 'Project 2')
198
+ @in_progress = Status.create(:name => 'In Progress')
199
+ @archived = Status.create(:name => 'Archived')
200
+ @another_complete = Status.create(:name => 'Complete')
201
+ @project2.statuses = [@in_progress, @archived, @another_complete]
202
+ @project2.save
203
+ end
204
+
205
+ context "dynamic finders" do
206
+ should "work with single key" do
207
+ @project1.statuses.find_by_name('New').should == @brand_new
208
+ @project2.statuses.find_by_name('In Progress').should == @in_progress
209
+ end
210
+
211
+ should "work with multiple keys" do
212
+ @project1.statuses.find_by_name_and_position('New', 1).should == @brand_new
213
+ @project1.statuses.find_by_name_and_position('New', 2).should be_nil
214
+ end
215
+
216
+ should "raise error when using !" do
217
+ lambda {
218
+ @project1.statuses.find_by_name!('Fake')
219
+ }.should raise_error(MongoMapper::DocumentNotFound)
220
+ end
221
+
222
+ context "find_or_create_by" do
223
+ should "not create document if found" do
224
+ lambda {
225
+ status = @project1.statuses.find_or_create_by_name('New')
226
+ status.project.should == @project1
227
+ status.should == @brand_new
228
+ }.should_not change { Status.count }
229
+ end
230
+
231
+ should "create document if not found" do
232
+ lambda {
233
+ status = @project1.statuses.find_or_create_by_name('Delivered')
234
+ status.project.should == @project1
235
+ }.should change { Status.count }.by(1)
236
+ end
237
+ end
238
+ end
239
+
240
+ context "with :all" do
241
+ should "work" do
242
+ @project1.statuses.find(:all, :order => "position asc").should == [@brand_new, @complete]
243
+ end
244
+
245
+ should "work with conditions" do
246
+ statuses = @project1.statuses.find(:all, :name => 'Complete')
247
+ statuses.should == [@complete]
248
+ end
249
+
250
+ should "work with order" do
251
+ statuses = @project1.statuses.find(:all, :order => 'name asc')
252
+ statuses.should == [@complete, @brand_new]
253
+ end
254
+ end
255
+
256
+ context "with #all" do
257
+ should "work" do
258
+ @project1.statuses.all(:order => "position asc").should == [@brand_new, @complete]
259
+ end
260
+
261
+ should "work with conditions" do
262
+ statuses = @project1.statuses.all(:name => 'Complete')
263
+ statuses.should == [@complete]
264
+ end
265
+
266
+ should "work with order" do
267
+ statuses = @project1.statuses.all(:order => 'name asc')
268
+ statuses.should == [@complete, @brand_new]
269
+ end
270
+ end
271
+
272
+ context "with :first" do
273
+ should "work" do
274
+ @project1.statuses.find(:first, :order => 'name').should == @complete
275
+ end
276
+
277
+ should "work with conditions" do
278
+ status = @project1.statuses.find(:first, :name => 'Complete')
279
+ status.should == @complete
280
+ end
281
+ end
282
+
283
+ context "with #first" do
284
+ should "work" do
285
+ @project1.statuses.first(:order => 'name').should == @complete
286
+ end
287
+
288
+ should "work with conditions" do
289
+ status = @project1.statuses.first(:name => 'Complete')
290
+ status.should == @complete
291
+ end
292
+ end
293
+
294
+ context "with :last" do
295
+ should "work" do
296
+ @project1.statuses.find(:last, :order => "position asc").should == @complete
297
+ end
298
+
299
+ should "work with conditions" do
300
+ status = @project1.statuses.find(:last, :order => 'position', :name => 'New')
301
+ status.should == @brand_new
302
+ end
303
+ end
304
+
305
+ context "with #last" do
306
+ should "work" do
307
+ @project1.statuses.last(:order => "position asc").should == @complete
308
+ end
309
+
310
+ should "work with conditions" do
311
+ status = @project1.statuses.last(:order => 'position', :name => 'New')
312
+ status.should == @brand_new
313
+ end
314
+ end
315
+
316
+ context "with one id" do
317
+ should "work for id in association" do
318
+ @project1.statuses.find(@complete._id).should == @complete
319
+ end
320
+
321
+ should "not work for id not in association" do
322
+ lambda {
323
+ @project1.statuses.find!(@archived._id)
324
+ }.should raise_error(MongoMapper::DocumentNotFound)
325
+ end
326
+ end
327
+
328
+ context "with multiple ids" do
329
+ should "work for ids in association" do
330
+ statuses = @project1.statuses.find(@brand_new._id, @complete._id)
331
+ statuses.should == [@brand_new, @complete]
332
+ end
333
+
334
+ should "not work for ids not in association" do
335
+ lambda {
336
+ @project1.statuses.find!(@brand_new._id, @complete._id, @archived._id)
337
+ }.should raise_error(MongoMapper::DocumentNotFound)
338
+ end
339
+ end
340
+
341
+ context "with #paginate" do
342
+ setup do
343
+ @statuses = @project2.statuses.paginate(:per_page => 2, :page => 1, :order => 'name asc')
344
+ end
345
+
346
+ should "return total pages" do
347
+ @statuses.total_pages.should == 2
348
+ end
349
+
350
+ should "return total entries" do
351
+ @statuses.total_entries.should == 3
352
+ end
353
+
354
+ should "return the subject" do
355
+ @statuses.collect(&:name).should == %w(Archived Complete)
356
+ end
357
+ end
358
+ end
359
+
360
+ context "extending the association" do
361
+ should "work using a block passed to many" do
362
+ project = Project.new(:name => "Some Project")
363
+ status1 = Status.new(:name => "New")
364
+ status2 = Status.new(:name => "Assigned")
365
+ status3 = Status.new(:name => "Closed")
366
+ project.statuses = [status1, status2, status3]
367
+ project.save
368
+
369
+ open_statuses = project.statuses.open
370
+ open_statuses.should include(status1)
371
+ open_statuses.should include(status2)
372
+ open_statuses.should_not include(status3)
373
+ end
374
+
375
+ should "work using many's :extend option" do
376
+ project = Project.new(:name => "Some Project")
377
+ collaborator1 = Collaborator.new(:name => "zing")
378
+ collaborator2 = Collaborator.new(:name => "zang")
379
+ project.collaborators = [collaborator1, collaborator2]
380
+ project.save
381
+ project.collaborators.top.should == collaborator1
382
+ end
383
+ end
384
+ end
@@ -0,0 +1,44 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class AssociationsTest < Test::Unit::TestCase
5
+ should "allow changing class names" do
6
+ class AwesomeUser
7
+ include MongoMapper::Document
8
+
9
+ many :posts, :class_name => 'AssociationsTest::AwesomePost', :foreign_key => :creator_id
10
+ end
11
+ AwesomeUser.collection.remove
12
+
13
+ class AwesomeTag
14
+ include MongoMapper::EmbeddedDocument
15
+
16
+ key :name, String
17
+ key :post_id, ObjectId
18
+
19
+ belongs_to :post, :class_name => 'AssociationsTest::AwesomeUser'
20
+ end
21
+
22
+ class AwesomePost
23
+ include MongoMapper::Document
24
+
25
+ key :creator_id, ObjectId
26
+
27
+ belongs_to :creator, :class_name => 'AssociationsTest::AwesomeUser'
28
+ many :tags, :class_name => 'AssociationsTest::AwesomeTag', :foreign_key => :post_id
29
+ end
30
+
31
+ AwesomeUser.collection.remove
32
+ AwesomePost.collection.remove
33
+
34
+ user = AwesomeUser.create
35
+ tag1 = AwesomeTag.new(:name => 'awesome')
36
+ tag2 = AwesomeTag.new(:name => 'grand')
37
+ post1 = AwesomePost.create(:creator => user, :tags => [tag1])
38
+ post2 = AwesomePost.create(:creator => user, :tags => [tag2])
39
+ user.posts.should == [post1, post2]
40
+
41
+ post1 = post1.reload
42
+ post1.tags.should == [tag1]
43
+ end
44
+ end
@@ -0,0 +1,18 @@
1
+ require 'test_helper'
2
+
3
+ class BinaryTest < Test::Unit::TestCase
4
+ should "serialize and deserialize correctly" do
5
+ klass = Class.new do
6
+ include MongoMapper::Document
7
+ set_collection_name 'test'
8
+ key :contents, Binary
9
+ end
10
+ klass.collection.remove
11
+
12
+ doc = klass.new(:contents => '010101')
13
+ doc.save
14
+
15
+ doc = doc.reload
16
+ doc.contents.to_s.should == ByteBuffer.new('010101').to_s
17
+ end
18
+ 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
+ set_collection_name 'test'
9
+
10
+ key :name, String
11
+
12
+ [ :before_validation_on_create, :before_validation_on_update,
13
+ :before_validation, :after_validation,
14
+ :before_create, :after_create,
15
+ :before_update, :after_update,
16
+ :before_save, :after_save,
17
+ :before_destroy, :after_destroy].each do |callback|
18
+ callback_method = "#{callback}_callback"
19
+ send(callback, callback_method)
20
+ define_method(callback_method) do
21
+ history << callback.to_sym
22
+ end
23
+ end
24
+
25
+ def history
26
+ @history ||= []
27
+ end
28
+
29
+ def clear_history
30
+ @history = nil
31
+ end
32
+ end
33
+ @document.collection.remove
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