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,97 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class EmbeddedDocumentTest < Test::Unit::TestCase
5
+ def setup
6
+ @document = Class.new do
7
+ include MongoMapper::Document
8
+ set_collection_name 'users'
9
+
10
+ key :first_name, String
11
+ key :last_name, String
12
+ end
13
+ @document.collection.clear
14
+ end
15
+
16
+ context "Saving a document with an embedded document" do
17
+ setup do
18
+ @document.class_eval do
19
+ key :foo, Address
20
+ end
21
+
22
+ @address = Address.new(:city => 'South Bend', :state => 'IN')
23
+ @doc = @document.new(:foo => @address)
24
+ end
25
+
26
+ should "embed embedded document" do
27
+ @doc.save
28
+ @doc.foo.city.should == 'South Bend'
29
+ @doc.foo.state.should == 'IN'
30
+
31
+ from_db = @document.find(@doc.id)
32
+ from_db.foo.city.should == 'South Bend'
33
+ from_db.foo.state.should == 'IN'
34
+ end
35
+ end
36
+
37
+ context "new?" do
38
+ setup do
39
+ @document.class_eval do
40
+ key :foo, Address
41
+ end
42
+ end
43
+
44
+ should "be new until document is saved" do
45
+ address = Address.new(:city => 'South Bend', :state => 'IN')
46
+ doc = @document.new(:foo => address)
47
+ address.new?.should == true
48
+ end
49
+
50
+ should "not be new after document is saved" do
51
+ address = Address.new(:city => 'South Bend', :state => 'IN')
52
+ doc = @document.new(:foo => address)
53
+ doc.save
54
+ doc.foo.new?.should == false
55
+ end
56
+
57
+ should "not be new when document is read back" do
58
+ address = Address.new(:city => 'South Bend', :state => 'IN')
59
+ doc = @document.new(:foo => address)
60
+ doc.save
61
+ read_doc = @document.find(doc.id)
62
+ read_doc.foo.new?.should == false
63
+ end
64
+ end
65
+
66
+ context "save" do
67
+ should "save the root document" do
68
+ person = RealPerson.create
69
+
70
+ pet = Pet.new :name => 'sparky'
71
+ person.pets << pet
72
+ pet.save
73
+
74
+ doc = RealPerson.find(person.id)
75
+ doc.pets.first.should == pet
76
+ end
77
+ end
78
+
79
+ context "update_attributes" do
80
+ should "save the root document" do
81
+ person = RealPerson.create
82
+
83
+ pet = Pet.new(:name => 'sparky')
84
+ person.pets << pet
85
+ pet.save
86
+
87
+ doc = RealPerson.find(person.id)
88
+ pet = doc.pets.first
89
+ pet.update_attributes :name => 'koda'
90
+
91
+ doc = RealPerson.find(person.id)
92
+ embedded = doc.pets.first
93
+ embedded.id.should == pet.id
94
+ embedded.name.should == 'koda'
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,87 @@
1
+ require 'test_helper'
2
+
3
+ class PaginationTest < Test::Unit::TestCase
4
+ context "Paginating" do
5
+ setup do
6
+ @document = Class.new do
7
+ include MongoMapper::Document
8
+ set_collection_name 'users'
9
+
10
+ key :first_name, String
11
+ key :last_name, String
12
+ key :age, Integer
13
+
14
+ def self.per_page; 1 end
15
+ end
16
+ @document.collection.clear
17
+
18
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
19
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
20
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
21
+ end
22
+
23
+ should "return the total pages" do
24
+ result = @document.paginate(:per_page => 2, :page => 1)
25
+ result.total_pages.should == 2
26
+ end
27
+
28
+ should "return the total pages when defaulting to the document class per_page" do
29
+ result = @document.paginate(:page => 1)
30
+ result.total_pages.should == 3
31
+ end
32
+
33
+ should "return the total of records" do
34
+ result = @document.paginate(:per_page => 2, :page => 1)
35
+ result.total_entries.should == 3
36
+ end
37
+
38
+ should "return the items" do
39
+ result = @document.paginate(:per_page => 2, :page => 1, :order => 'first_name')
40
+ result.size.should == 2
41
+ result.should == [@doc1, @doc3]
42
+ end
43
+
44
+ should "accept conditions" do
45
+ result = @document.paginate({
46
+ :conditions => {:last_name => 'Nunemaker'},
47
+ :order => "age DESC",
48
+ :per_page => 2,
49
+ :page => 1,
50
+ })
51
+ result.should == [@doc1, @doc3]
52
+ result.first.age.should == 27
53
+ end
54
+
55
+ should "withstand rigor" do
56
+ result = @document.paginate({
57
+ :per_page => 1,
58
+ :page => 1,
59
+ :order => 'age desc',
60
+ :conditions => {:last_name => 'Nunemaker'}
61
+ })
62
+ result.should == [@doc1]
63
+ result.total_entries.should == 2
64
+ result.total_pages.should == 2
65
+
66
+ result = @document.paginate({
67
+ :per_page => 1,
68
+ :page => 2,
69
+ :order => 'age desc',
70
+ :conditions => {:last_name => 'Nunemaker'}
71
+ })
72
+ result.should == [@doc3]
73
+ result.total_entries.should == 2
74
+ result.total_pages.should == 2
75
+
76
+ result = @document.paginate({
77
+ :per_page => 2,
78
+ :page => 1,
79
+ :order => 'age desc',
80
+ :conditions => {:last_name => 'Nunemaker'}
81
+ })
82
+ result.should == [@doc1, @doc3]
83
+ result.total_entries.should == 2
84
+ result.total_pages.should == 1
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,30 @@
1
+ require 'test_helper'
2
+
3
+ class TestRailsCompatibility < Test::Unit::TestCase
4
+ class Item
5
+ include MongoMapper::EmbeddedDocument
6
+ key :for_all, String
7
+ end
8
+
9
+ class Order
10
+ include MongoMapper::Document
11
+ many :items, :class_name => 'TestRailsCompatibility::Item'
12
+ key :order_only, String
13
+ end
14
+
15
+ context "Document" do
16
+ setup do
17
+ Order.collection.clear
18
+ end
19
+
20
+ should "have to_param that returns id" do
21
+ instance = Order.create('_id' => 1234)
22
+ instance.to_param.should == '1234'
23
+ end
24
+
25
+ should "alias new to new_record?" do
26
+ instance = Order.new
27
+ instance.new_record?.should == instance.new?
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,279 @@
1
+ require 'test_helper'
2
+
3
+ class ValidationsTest < Test::Unit::TestCase
4
+ context "Saving a new document that is invalid" do
5
+ setup do
6
+ @document = Class.new do
7
+ include MongoMapper::Document
8
+ set_collection_name 'test'
9
+ key :name, String, :required => true
10
+ end
11
+ @document.collection.clear
12
+ end
13
+
14
+ should "not insert document" do
15
+ doc = @document.new
16
+ doc.save
17
+ @document.count.should == 0
18
+ end
19
+
20
+ should "populate document's errors" do
21
+ doc = @document.new
22
+ doc.errors.size.should == 0
23
+ doc.save
24
+ doc.errors.full_messages.should == ["Name can't be empty"]
25
+ end
26
+ end
27
+
28
+ context "Saving a document that is invalid (destructive)" do
29
+ setup do
30
+ @document = Class.new do
31
+ include MongoMapper::Document
32
+ set_collection_name 'test'
33
+ key :name, String, :required => true
34
+ end
35
+ @document.collection.clear
36
+ end
37
+
38
+ should "raise error" do
39
+ doc = @document.new
40
+ lambda { doc.save! }.should raise_error(MongoMapper::DocumentNotValid)
41
+ end
42
+ end
43
+
44
+ context "Saving an existing document that is invalid" do
45
+ setup do
46
+ @document = Class.new do
47
+ include MongoMapper::Document
48
+ set_collection_name 'test'
49
+ key :name, String, :required => true
50
+ end
51
+ @document.collection.clear
52
+
53
+ @doc = @document.create(:name => 'John Nunemaker')
54
+ end
55
+
56
+ should "not update document" do
57
+ @doc.name = nil
58
+ @doc.save
59
+ @document.find(@doc.id).name.should == 'John Nunemaker'
60
+ end
61
+
62
+ should "populate document's errors" do
63
+ @doc.name = nil
64
+ @doc.save
65
+ @doc.errors.full_messages.should == ["Name can't be empty"]
66
+ end
67
+ end
68
+
69
+ context "Adding validation errors" do
70
+ setup do
71
+ @document = Class.new do
72
+ include MongoMapper::Document
73
+ set_collection_name 'test'
74
+
75
+ key :action, String
76
+ def action_present
77
+ errors.add(:action, 'is invalid') if action.blank?
78
+ end
79
+ end
80
+ @document.collection.clear
81
+ end
82
+
83
+ should "work with validate_on_create callback" do
84
+ @document.validate_on_create :action_present
85
+
86
+ doc = @document.new
87
+ doc.action = nil
88
+ doc.should have_error_on(:action)
89
+
90
+ doc.action = 'kick'
91
+ doc.should_not have_error_on(:action)
92
+ doc.save
93
+
94
+ doc.action = nil
95
+ doc.should_not have_error_on(:action)
96
+ end
97
+
98
+ should "work with validate_on_update callback" do
99
+ @document.validate_on_update :action_present
100
+
101
+ doc = @document.new
102
+ doc.action = nil
103
+ doc.should_not have_error_on(:action)
104
+ doc.save
105
+
106
+ doc.action = nil
107
+ doc.should have_error_on(:action)
108
+
109
+ doc.action = 'kick'
110
+ doc.should_not have_error_on(:action)
111
+ end
112
+ end
113
+
114
+ context "validating uniqueness of" do
115
+ setup do
116
+ @document = Class.new do
117
+ include MongoMapper::Document
118
+ set_collection_name 'test'
119
+
120
+ key :name, String
121
+ validates_uniqueness_of :name
122
+ end
123
+ @document.collection.clear
124
+ end
125
+
126
+ should "not fail if object is new" do
127
+ doc = @document.new
128
+ doc.should_not have_error_on(:name)
129
+ end
130
+
131
+ should "not fail when new object is out of scope" do
132
+ document = Class.new do
133
+ include MongoMapper::Document
134
+ set_collection_name 'test'
135
+
136
+ key :name
137
+ key :adult
138
+ validates_uniqueness_of :name, :scope => :adult
139
+ end
140
+ doc = document.new("name" => "joe", :adult => true)
141
+ doc.save.should be_true
142
+
143
+ doc2 = document.new("name" => "joe", :adult => false)
144
+ doc2.should be_valid
145
+
146
+ end
147
+
148
+ should "allow to update an object" do
149
+ doc = @document.new("name" => "joe")
150
+ doc.save.should be_true
151
+
152
+ @document \
153
+ .stubs(:find) \
154
+ .with(:first, :conditions => {:name => 'joe'}, :limit => 1) \
155
+ .returns(doc)
156
+
157
+ doc.name = "joe"
158
+ doc.valid?.should be_true
159
+ doc.should_not have_error_on(:name)
160
+ end
161
+
162
+ should "fail if object name is not unique" do
163
+ doc = @document.new("name" => "joe")
164
+ doc.save.should be_true
165
+
166
+ @document \
167
+ .stubs(:find) \
168
+ .with(:first, :conditions => {:name => 'joe'}, :limit => 1) \
169
+ .returns(doc)
170
+
171
+ doc2 = @document.new("name" => "joe")
172
+ doc2.should have_error_on(:name)
173
+ end
174
+
175
+ context "scoped by a single attribute" do
176
+ setup do
177
+ @document = Class.new do
178
+ include MongoMapper::Document
179
+ set_collection_name 'test'
180
+
181
+ key :name, String
182
+ key :scope, String
183
+ validates_uniqueness_of :name, :scope => :scope
184
+ end
185
+ @document.collection.clear
186
+ end
187
+
188
+ should "fail if the same name exists in the scope" do
189
+ doc = @document.new("name" => "joe", "scope" => "one")
190
+ doc.save.should be_true
191
+
192
+ @document \
193
+ .stubs(:find) \
194
+ .with(:first, :conditions => {:name => 'joe', :scope => "one"}, :limit => 1) \
195
+ .returns(doc)
196
+
197
+ doc2 = @document.new("name" => "joe", "scope" => "one")
198
+ doc2.should have_error_on(:name)
199
+ end
200
+
201
+ should "pass if the same name exists in a different scope" do
202
+ doc = @document.new("name" => "joe", "scope" => "one")
203
+ doc.save.should be_true
204
+
205
+ @document \
206
+ .stubs(:find) \
207
+ .with(:first, :conditions => {:name => 'joe', :scope => "two"}, :limit => 1) \
208
+ .returns(nil)
209
+
210
+ doc2 = @document.new("name" => "joe", "scope" => "two")
211
+ doc2.should_not have_error_on(:name)
212
+ end
213
+ end
214
+
215
+ context "scoped by a multiple attributes" do
216
+ setup do
217
+ @document = Class.new do
218
+ include MongoMapper::Document
219
+ set_collection_name 'test'
220
+
221
+ key :name, String
222
+ key :first_scope, String
223
+ key :second_scope, String
224
+ validates_uniqueness_of :name, :scope => [:first_scope, :second_scope]
225
+ end
226
+ @document.collection.clear
227
+ end
228
+
229
+ should "fail if the same name exists in the scope" do
230
+ doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
231
+ doc.save.should be_true
232
+
233
+ @document \
234
+ .stubs(:find) \
235
+ .with(:first, :conditions => {:name => 'joe', :first_scope => "one", :second_scope => "two"}, :limit => 1) \
236
+ .returns(doc)
237
+
238
+ doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
239
+ doc2.should have_error_on(:name)
240
+ end
241
+
242
+ should "pass if the same name exists in a different scope" do
243
+ doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
244
+ doc.save.should be_true
245
+
246
+ @document \
247
+ .stubs(:find) \
248
+ .with(:first, :conditions => {:name => 'joe', :first_scope => "one", :second_scope => "one"}, :limit => 1) \
249
+ .returns(nil)
250
+
251
+ doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "one")
252
+ doc2.should_not have_error_on(:name)
253
+ end
254
+ end
255
+ end
256
+
257
+ context "validates uniqueness of with :unique shortcut" do
258
+ should "work" do
259
+ @document = Class.new do
260
+ include MongoMapper::Document
261
+ set_collection_name 'test'
262
+
263
+ key :name, String, :unique => true
264
+ end
265
+ @document.collection.clear
266
+
267
+ doc = @document.create(:name => 'John')
268
+ doc.should_not have_error_on(:name)
269
+
270
+ @document \
271
+ .stubs(:find) \
272
+ .with(:first, :conditions => {:name => 'John'}, :limit => 1) \
273
+ .returns(doc)
274
+
275
+ second_john = @document.create(:name => 'John')
276
+ second_john.should have_error_on(:name, 'has already been taken')
277
+ end
278
+ end
279
+ end