mongo_mapper 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. data/.gitignore +7 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +39 -0
  4. data/Rakefile +87 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +55 -0
  7. data/lib/mongo_mapper.rb +92 -0
  8. data/lib/mongo_mapper/associations.rb +86 -0
  9. data/lib/mongo_mapper/associations/base.rb +83 -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 +27 -0
  13. data/lib/mongo_mapper/associations/many_documents_proxy.rb +116 -0
  14. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
  15. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +67 -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 +64 -0
  19. data/lib/mongo_mapper/callbacks.rb +106 -0
  20. data/lib/mongo_mapper/document.rb +317 -0
  21. data/lib/mongo_mapper/dynamic_finder.rb +35 -0
  22. data/lib/mongo_mapper/embedded_document.rb +354 -0
  23. data/lib/mongo_mapper/finder_options.rb +94 -0
  24. data/lib/mongo_mapper/key.rb +32 -0
  25. data/lib/mongo_mapper/observing.rb +50 -0
  26. data/lib/mongo_mapper/pagination.rb +51 -0
  27. data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
  28. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +27 -0
  29. data/lib/mongo_mapper/save_with_validation.rb +19 -0
  30. data/lib/mongo_mapper/serialization.rb +55 -0
  31. data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
  32. data/lib/mongo_mapper/support.rb +157 -0
  33. data/lib/mongo_mapper/validations.rb +69 -0
  34. data/mongo_mapper.gemspec +156 -0
  35. data/test/NOTE_ON_TESTING +1 -0
  36. data/test/custom_matchers.rb +48 -0
  37. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +54 -0
  38. data/test/functional/associations/test_belongs_to_proxy.rb +46 -0
  39. data/test/functional/associations/test_many_documents_as_proxy.rb +244 -0
  40. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +132 -0
  41. data/test/functional/associations/test_many_embedded_proxy.rb +174 -0
  42. data/test/functional/associations/test_many_polymorphic_proxy.rb +297 -0
  43. data/test/functional/associations/test_many_proxy.rb +331 -0
  44. data/test/functional/test_associations.rb +48 -0
  45. data/test/functional/test_binary.rb +18 -0
  46. data/test/functional/test_callbacks.rb +85 -0
  47. data/test/functional/test_document.rb +951 -0
  48. data/test/functional/test_embedded_document.rb +97 -0
  49. data/test/functional/test_pagination.rb +87 -0
  50. data/test/functional/test_rails_compatibility.rb +30 -0
  51. data/test/functional/test_validations.rb +279 -0
  52. data/test/models.rb +169 -0
  53. data/test/test_helper.rb +29 -0
  54. data/test/unit/serializers/test_json_serializer.rb +189 -0
  55. data/test/unit/test_association_base.rb +144 -0
  56. data/test/unit/test_document.rb +165 -0
  57. data/test/unit/test_dynamic_finder.rb +125 -0
  58. data/test/unit/test_embedded_document.rb +645 -0
  59. data/test/unit/test_finder_options.rb +193 -0
  60. data/test/unit/test_key.rb +163 -0
  61. data/test/unit/test_mongomapper.rb +28 -0
  62. data/test/unit/test_observing.rb +101 -0
  63. data/test/unit/test_pagination.rb +109 -0
  64. data/test/unit/test_rails_compatibility.rb +39 -0
  65. data/test/unit/test_serializations.rb +52 -0
  66. data/test/unit/test_support.rb +272 -0
  67. data/test/unit/test_time_zones.rb +40 -0
  68. data/test/unit/test_validations.rb +503 -0
  69. metadata +204 -0
@@ -0,0 +1,331 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class ManyProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ Project.collection.clear
7
+ Status.collection.clear
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
18
+ project.statuses.push Status.new
19
+ project.statuses.concat Status.new
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
+ from_db = Project.find(project.id)
29
+ from_db.statuses.size.should == 1
30
+ from_db.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
+ from_db = Project.find(project.id)
40
+ from_db.statuses[0].project_id.should == project.id
41
+ from_db.statuses[1].project_id.should == project.id
42
+ from_db.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
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
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 "count" do
81
+ should "work scoped to association" do
82
+ project = Project.create
83
+ 3.times { project.statuses.create }
84
+
85
+ other_project = Project.create
86
+ 2.times { other_project.statuses.create }
87
+
88
+ project.statuses.count.should == 3
89
+ other_project.statuses.count.should == 2
90
+ end
91
+
92
+ should "work with conditions" do
93
+ project = Project.create
94
+ project.statuses.create(:name => 'Foo')
95
+ project.statuses.create(:name => 'Other 1')
96
+ project.statuses.create(:name => 'Other 2')
97
+
98
+ project.statuses.count(:name => 'Foo').should == 1
99
+ end
100
+ end
101
+
102
+ context "Unassociating documents" do
103
+ setup do
104
+ @project = Project.create
105
+ @project.statuses << Status.create(:name => '1')
106
+ @project.statuses << Status.create(:name => '2')
107
+
108
+ @project2 = Project.create
109
+ @project2.statuses << Status.create(:name => '1')
110
+ @project2.statuses << Status.create(:name => '2')
111
+ end
112
+
113
+ should "work with destroy all" do
114
+ @project.statuses.count.should == 2
115
+ @project.statuses.destroy_all
116
+ @project.statuses.count.should == 0
117
+
118
+ @project2.statuses.count.should == 2
119
+ Status.count.should == 2
120
+ end
121
+
122
+ should "work with destroy all and conditions" do
123
+ @project.statuses.count.should == 2
124
+ @project.statuses.destroy_all(:name => '1')
125
+ @project.statuses.count.should == 1
126
+
127
+ @project2.statuses.count.should == 2
128
+ Status.count.should == 3
129
+ end
130
+
131
+ should "work with delete all" do
132
+ @project.statuses.count.should == 2
133
+ @project.statuses.delete_all
134
+ @project.statuses.count.should == 0
135
+
136
+ @project2.statuses.count.should == 2
137
+ Status.count.should == 2
138
+ end
139
+
140
+ should "work with delete all and conditions" do
141
+ @project.statuses.count.should == 2
142
+ @project.statuses.delete_all(:name => '1')
143
+ @project.statuses.count.should == 1
144
+
145
+ @project2.statuses.count.should == 2
146
+ Status.count.should == 3
147
+ end
148
+
149
+ should "work with nullify" do
150
+ @project.statuses.count.should == 2
151
+ @project.statuses.nullify
152
+ @project.statuses.count.should == 0
153
+
154
+ @project2.statuses.count.should == 2
155
+ Status.count.should == 4
156
+ Status.count(:name => '1').should == 2
157
+ Status.count(:name => '2').should == 2
158
+ end
159
+ end
160
+
161
+ context "Finding scoped to association" do
162
+ setup do
163
+ @project1 = Project.new(:name => 'Project 1')
164
+ @brand_new = Status.create(:name => 'New', :position => 1 )
165
+ @complete = Status.create(:name => 'Complete', :position => 2)
166
+ @project1.statuses = [@brand_new, @complete]
167
+ @project1.save
168
+
169
+ @project2 = Project.create(:name => 'Project 2')
170
+ @in_progress = Status.create(:name => 'In Progress')
171
+ @archived = Status.create(:name => 'Archived')
172
+ @another_complete = Status.create(:name => 'Complete')
173
+ @project2.statuses = [@in_progress, @archived, @another_complete]
174
+ @project2.save
175
+ end
176
+
177
+ context "dynamic finders" do
178
+ should "work with single key" do
179
+ @project1.statuses.find_by_name('New').should == @brand_new
180
+ @project2.statuses.find_by_name('In Progress').should == @in_progress
181
+ end
182
+
183
+ should "work with multiple keys" do
184
+ @project1.statuses.find_by_name_and_position('New', 1).should == @brand_new
185
+ @project1.statuses.find_by_name_and_position('New', 2).should be_nil
186
+ end
187
+
188
+ should "raise error when using !" do
189
+ lambda {
190
+ @project1.statuses.find_by_name!('Fake')
191
+ }.should raise_error(MongoMapper::DocumentNotFound)
192
+ end
193
+
194
+ context "find_or_create_by" do
195
+ should "not create document if found" do
196
+ lambda {
197
+ status = @project1.statuses.find_or_create_by_name('New')
198
+ status.project.should == @project1
199
+ status.should == @brand_new
200
+ }.should_not change { Status.count }
201
+ end
202
+
203
+ should "create document if not found" do
204
+ lambda {
205
+ status = @project1.statuses.find_or_create_by_name('Delivered')
206
+ status.project.should == @project1
207
+ }.should change { Status.count }.by(1)
208
+ end
209
+ end
210
+ end
211
+
212
+ context "with :all" do
213
+ should "work" do
214
+ @project1.statuses.find(:all, :order => "position asc").should == [@brand_new, @complete]
215
+ end
216
+
217
+ should "work with conditions" do
218
+ statuses = @project1.statuses.find(:all, :conditions => {'name' => 'Complete'})
219
+ statuses.should == [@complete]
220
+ end
221
+
222
+ should "work with order" do
223
+ statuses = @project1.statuses.find(:all, :order => 'name asc')
224
+ statuses.should == [@complete, @brand_new]
225
+ end
226
+ end
227
+
228
+ context "with #all" do
229
+ should "work" do
230
+ @project1.statuses.all(:order => "position asc").should == [@brand_new, @complete]
231
+ end
232
+
233
+ should "work with conditions" do
234
+ statuses = @project1.statuses.all(:conditions => {'name' => 'Complete'})
235
+ statuses.should == [@complete]
236
+ end
237
+
238
+ should "work with order" do
239
+ statuses = @project1.statuses.all(:order => 'name asc')
240
+ statuses.should == [@complete, @brand_new]
241
+ end
242
+ end
243
+
244
+ context "with :first" do
245
+ should "work" do
246
+ @project1.statuses.find(:first, :order => 'name').should == @complete
247
+ end
248
+
249
+ should "work with conditions" do
250
+ status = @project1.statuses.find(:first, :conditions => {:name => 'Complete'})
251
+ status.should == @complete
252
+ end
253
+ end
254
+
255
+ context "with #first" do
256
+ should "work" do
257
+ @project1.statuses.first(:order => 'name').should == @complete
258
+ end
259
+
260
+ should "work with conditions" do
261
+ status = @project1.statuses.first(:conditions => {:name => 'Complete'})
262
+ status.should == @complete
263
+ end
264
+ end
265
+
266
+ context "with :last" do
267
+ should "work" do
268
+ @project1.statuses.find(:last, :order => "position asc").should == @complete
269
+ end
270
+
271
+ should "work with conditions" do
272
+ status = @project1.statuses.find(:last, :conditions => {:name => 'New'})
273
+ status.should == @brand_new
274
+ end
275
+ end
276
+
277
+ context "with #last" do
278
+ should "work" do
279
+ @project1.statuses.last(:order => "position asc").should == @complete
280
+ end
281
+
282
+ should "work with conditions" do
283
+ status = @project1.statuses.last(:conditions => {:name => 'New'})
284
+ status.should == @brand_new
285
+ end
286
+ end
287
+
288
+ context "with one id" do
289
+ should "work for id in association" do
290
+ @project1.statuses.find(@complete.id).should == @complete
291
+ end
292
+
293
+ should "not work for id not in association" do
294
+ lambda {
295
+ @project1.statuses.find(@archived.id)
296
+ }.should raise_error(MongoMapper::DocumentNotFound)
297
+ end
298
+ end
299
+
300
+ context "with multiple ids" do
301
+ should "work for ids in association" do
302
+ statuses = @project1.statuses.find(@brand_new.id, @complete.id)
303
+ statuses.should == [@brand_new, @complete]
304
+ end
305
+
306
+ should "not work for ids not in association" do
307
+ lambda {
308
+ @project1.statuses.find(@brand_new.id, @complete.id, @archived.id)
309
+ }.should raise_error(MongoMapper::DocumentNotFound)
310
+ end
311
+ end
312
+
313
+ context "with #paginate" do
314
+ setup do
315
+ @statuses = @project2.statuses.paginate(:per_page => 2, :page => 1, :order => 'name asc')
316
+ end
317
+
318
+ should "return total pages" do
319
+ @statuses.total_pages.should == 2
320
+ end
321
+
322
+ should "return total entries" do
323
+ @statuses.total_entries.should == 3
324
+ end
325
+
326
+ should "return the subject" do
327
+ @statuses.collect(&:name).should == %w(Archived Complete)
328
+ end
329
+ end
330
+ end
331
+ end
@@ -0,0 +1,48 @@
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
+ AwesomeUser.collection.clear
16
+
17
+ class AwesomeTag
18
+ include MongoMapper::EmbeddedDocument
19
+
20
+ key :name, String
21
+ key :post_id, String
22
+
23
+ belongs_to :post, :class_name => 'AssociationsTest::AwesomeUser'
24
+ end
25
+
26
+ class AwesomePost
27
+ include MongoMapper::Document
28
+
29
+ key :creator_id, String
30
+
31
+ belongs_to :creator, :class_name => 'AssociationsTest::AwesomeUser'
32
+ many :tags, :class_name => 'AssociationsTest::AwesomeTag', :foreign_key => :post_id
33
+ end
34
+
35
+ AwesomeUser.collection.clear
36
+ AwesomePost.collection.clear
37
+
38
+ user = AwesomeUser.create
39
+ tag1 = AwesomeTag.new(:name => 'awesome')
40
+ tag2 = AwesomeTag.new(:name => 'grand')
41
+ post1 = AwesomePost.create(:creator => user, :tags => [tag1])
42
+ post2 = AwesomePost.create(:creator => user, :tags => [tag2])
43
+ user.posts.should == [post1, post2]
44
+
45
+ post1_from_db = AwesomePost.find(post1.id)
46
+ post1_from_db.tags.should == [tag1]
47
+ end
48
+ 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.clear
11
+
12
+ doc = klass.new(:contents => '010101')
13
+ doc.save
14
+
15
+ doc = klass.find(doc.id)
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.clear
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