mongo_mapper 0.8.6 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. data/UPGRADES +10 -0
  2. data/bin/mmconsole +0 -1
  3. data/examples/identity_map/automatic.rb +1 -7
  4. data/examples/plugins.rb +9 -9
  5. data/examples/safe.rb +43 -0
  6. data/lib/mongo_mapper.rb +46 -33
  7. data/lib/mongo_mapper/document.rb +33 -32
  8. data/lib/mongo_mapper/embedded_document.rb +22 -22
  9. data/lib/mongo_mapper/locale/en.yml +5 -0
  10. data/lib/mongo_mapper/middleware/identity_map.rb +16 -0
  11. data/lib/mongo_mapper/plugins.rb +16 -3
  12. data/lib/mongo_mapper/plugins/accessible.rb +2 -0
  13. data/lib/mongo_mapper/plugins/active_model.rb +18 -0
  14. data/lib/mongo_mapper/plugins/associations.rb +37 -42
  15. data/lib/mongo_mapper/plugins/associations/base.rb +14 -50
  16. data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +58 -0
  17. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +6 -1
  18. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +30 -2
  19. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +4 -0
  20. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +12 -6
  21. data/lib/mongo_mapper/plugins/associations/many_association.rb +67 -0
  22. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +5 -5
  23. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +1 -1
  24. data/lib/mongo_mapper/plugins/associations/one_association.rb +20 -0
  25. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +5 -0
  26. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +7 -7
  27. data/lib/mongo_mapper/plugins/associations/proxy.rb +2 -2
  28. data/lib/mongo_mapper/plugins/caching.rb +3 -1
  29. data/lib/mongo_mapper/plugins/callbacks.rb +12 -221
  30. data/lib/mongo_mapper/plugins/clone.rb +3 -1
  31. data/lib/mongo_mapper/plugins/dirty.rb +38 -91
  32. data/lib/mongo_mapper/plugins/document.rb +4 -2
  33. data/lib/mongo_mapper/plugins/dynamic_querying.rb +2 -0
  34. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +43 -0
  35. data/lib/mongo_mapper/plugins/embedded_document.rb +16 -9
  36. data/lib/mongo_mapper/plugins/equality.rb +2 -0
  37. data/lib/mongo_mapper/plugins/identity_map.rb +4 -2
  38. data/lib/mongo_mapper/plugins/indexes.rb +2 -0
  39. data/lib/mongo_mapper/plugins/inspect.rb +3 -1
  40. data/lib/mongo_mapper/plugins/keys.rb +28 -22
  41. data/lib/mongo_mapper/plugins/keys/key.rb +12 -6
  42. data/lib/mongo_mapper/plugins/logger.rb +2 -0
  43. data/lib/mongo_mapper/plugins/modifiers.rb +3 -1
  44. data/lib/mongo_mapper/plugins/pagination.rb +2 -0
  45. data/lib/mongo_mapper/plugins/persistence.rb +2 -0
  46. data/lib/mongo_mapper/plugins/protected.rb +2 -0
  47. data/lib/mongo_mapper/plugins/querying.rb +5 -4
  48. data/lib/mongo_mapper/plugins/rails.rb +3 -5
  49. data/lib/mongo_mapper/plugins/safe.rb +2 -0
  50. data/lib/mongo_mapper/plugins/sci.rb +2 -0
  51. data/lib/mongo_mapper/plugins/scopes.rb +2 -0
  52. data/lib/mongo_mapper/plugins/serialization.rb +67 -46
  53. data/lib/mongo_mapper/plugins/timestamps.rb +3 -1
  54. data/lib/mongo_mapper/plugins/userstamps.rb +2 -0
  55. data/lib/mongo_mapper/plugins/validations.rb +40 -24
  56. data/lib/mongo_mapper/railtie.rb +49 -0
  57. data/lib/mongo_mapper/railtie/database.rake +60 -0
  58. data/lib/mongo_mapper/support/descendant_appends.rb +11 -11
  59. data/lib/mongo_mapper/translation.rb +10 -0
  60. data/lib/mongo_mapper/version.rb +1 -1
  61. data/lib/rails/generators/mongo_mapper/config/config_generator.rb +24 -0
  62. data/lib/rails/generators/mongo_mapper/config/templates/mongo.yml +18 -0
  63. data/lib/rails/generators/mongo_mapper/model/model_generator.rb +23 -0
  64. data/lib/rails/generators/mongo_mapper/model/templates/model.rb +11 -0
  65. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +1 -0
  66. data/test/functional/associations/test_belongs_to_proxy.rb +131 -1
  67. data/test/functional/associations/test_in_array_proxy.rb +30 -0
  68. data/test/functional/associations/test_many_documents_proxy.rb +30 -2
  69. data/test/functional/associations/test_many_embedded_proxy.rb +33 -0
  70. data/test/functional/associations/test_many_polymorphic_proxy.rb +1 -0
  71. data/test/functional/associations/test_one_embedded_proxy.rb +21 -2
  72. data/test/functional/associations/test_one_proxy.rb +49 -9
  73. data/test/functional/test_associations.rb +2 -0
  74. data/test/functional/test_caching.rb +3 -2
  75. data/test/functional/test_callbacks.rb +25 -18
  76. data/test/functional/test_dirty.rb +123 -1
  77. data/test/functional/test_document.rb +26 -2
  78. data/test/functional/test_embedded_document.rb +68 -2
  79. data/test/functional/test_identity_map.rb +3 -4
  80. data/test/functional/test_querying.rb +11 -0
  81. data/test/functional/test_userstamps.rb +2 -2
  82. data/test/functional/test_validations.rb +31 -29
  83. data/test/models.rb +10 -0
  84. data/test/test_active_model_lint.rb +1 -1
  85. data/test/test_helper.rb +9 -10
  86. data/test/unit/associations/test_base.rb +24 -100
  87. data/test/unit/associations/test_belongs_to_association.rb +29 -0
  88. data/test/unit/associations/test_many_association.rb +63 -0
  89. data/test/unit/associations/test_one_association.rb +18 -0
  90. data/test/unit/serializers/test_json_serializer.rb +0 -1
  91. data/test/unit/test_descendant_appends.rb +8 -16
  92. data/test/unit/test_document.rb +4 -9
  93. data/test/unit/test_dynamic_finder.rb +1 -1
  94. data/test/unit/test_embedded_document.rb +51 -18
  95. data/test/unit/test_identity_map_middleware.rb +34 -0
  96. data/test/unit/test_inspect.rb +22 -0
  97. data/test/unit/test_key.rb +21 -1
  98. data/test/unit/test_keys.rb +0 -2
  99. data/test/unit/test_plugins.rb +106 -20
  100. data/test/unit/test_rails.rb +8 -8
  101. data/test/unit/test_serialization.rb +116 -1
  102. data/test/unit/test_translation.rb +27 -0
  103. data/test/unit/test_validations.rb +66 -81
  104. metadata +103 -43
  105. data/examples/identity_map/middleware.rb +0 -14
  106. data/lib/mongo_mapper/plugins/descendants.rb +0 -17
  107. data/rails/init.rb +0 -19
@@ -138,6 +138,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
138
138
  @hm2 = Message.create(:body => 'Hall the king!', :position => 2)
139
139
  @hall.messages = [@hm1, @hm2, @hm3]
140
140
  @hall.save
141
+ @hall.reload
141
142
  end
142
143
 
143
144
  context "dynamic finders" do
@@ -20,7 +20,7 @@ class OneEmbeddedProxyTest < Test::Unit::TestCase
20
20
  @post_class.one :author, :class => @author_class
21
21
 
22
22
  post = @post_class.create
23
- author = post.author.build(:name => "John")
23
+ author = post.build_author(:name => "John")
24
24
  post.author.should be_instance_of(@author_class)
25
25
  post.author.should be_new
26
26
  post.author.name.should == 'John'
@@ -48,7 +48,7 @@ class OneEmbeddedProxyTest < Test::Unit::TestCase
48
48
  should "not have problem loading root document if embedded one is nil" do
49
49
  @post_class.one :author, :class => @author_class
50
50
  post = @post_class.create
51
-
51
+
52
52
  lambda {
53
53
  @post_class.find(post.id)
54
54
  }.should_not raise_error
@@ -78,4 +78,23 @@ class OneEmbeddedProxyTest < Test::Unit::TestCase
78
78
  post.author?.should be_true
79
79
  end
80
80
 
81
+ should "initialize id for nested embedded document created from hash" do
82
+ @address_class = EDoc('Address') do
83
+ key :city, String
84
+ key :state, String
85
+ end
86
+ @author_class.one(:address, :class => @address_class)
87
+ @post_class.one(:author, :class => @author_class)
88
+
89
+ post = @post_class.create(:title => 'Post Title', :author => {
90
+ :name => 'Frank',
91
+ :address => {
92
+ :city => 'Boston',
93
+ :state => 'MA'
94
+ }
95
+ })
96
+
97
+ post.author.address.id.should_not be_nil
98
+ end
99
+
81
100
  end
@@ -1,4 +1,5 @@
1
1
  require 'test_helper'
2
+ require 'models'
2
3
 
3
4
  class OneProxyTest < Test::Unit::TestCase
4
5
  def setup
@@ -13,6 +14,11 @@ class OneProxyTest < Test::Unit::TestCase
13
14
  @post_class.new.author.nil?.should be_true
14
15
  end
15
16
 
17
+ should "return nil instead of a proxy" do
18
+ @post_class.one :author, :class => @author_class
19
+ nil.should === @post_class.new.author
20
+ end
21
+
16
22
  should "allow assignment of associated document using a hash" do
17
23
  @post_class.one :author, :class => @author_class
18
24
 
@@ -42,6 +48,26 @@ class OneProxyTest < Test::Unit::TestCase
42
48
  post.author = new_author
43
49
  post.author.should == new_author
44
50
  end
51
+
52
+ should "generate a new proxy instead of modifying the existing one" do
53
+ @post_class.one :author, :class => @author_class
54
+
55
+ post = @post_class.new
56
+ author = @author_class.new(:name => 'Frank')
57
+ post.author = author
58
+ post.reload
59
+
60
+ post.author.should == author
61
+ post.author.nil?.should be_false
62
+
63
+ original_author = post.author
64
+ original_author.name.should == 'Frank'
65
+ new_author = @author_class.new(:name => 'Emily')
66
+ post.author = new_author
67
+ post.author.should == new_author
68
+
69
+ original_author.name.should == 'Frank'
70
+ end
45
71
  end
46
72
 
47
73
  context "with a Hash" do
@@ -85,13 +111,12 @@ class OneProxyTest < Test::Unit::TestCase
85
111
 
86
112
  should "unset the association" do
87
113
  @post_class.one :author, :class => @author_class
88
- post = @post_class.new
89
- author = @author_class.new
90
- post.author = author
114
+ post = @post_class.create
115
+ author = @author_class.create
116
+ post.update_attributes!(:author => author)
91
117
  post.reload
92
-
93
118
  post.author = nil
94
- post.author.nil?.should be_false
119
+ post.author.nil?.should be_true
95
120
  end
96
121
 
97
122
  should "work with :dependent delete" do
@@ -136,7 +161,7 @@ class OneProxyTest < Test::Unit::TestCase
136
161
  @post_class.one :author, :class => @author_class
137
162
 
138
163
  post = @post_class.create
139
- author = post.author.build(:name => 'John')
164
+ author = post.build_author(:name => 'John')
140
165
  post.author.should be_instance_of(@author_class)
141
166
  post.author.should be_new
142
167
  post.author.name.should == 'John'
@@ -148,7 +173,7 @@ class OneProxyTest < Test::Unit::TestCase
148
173
  @post_class.one :author, :class => @author_class
149
174
 
150
175
  post = @post_class.create
151
- author = post.author.create(:name => 'John')
176
+ author = post.create_author(:name => 'John')
152
177
  post.author.should be_instance_of(@author_class)
153
178
  post.author.should_not be_new
154
179
  post.author.name.should == 'John'
@@ -165,13 +190,13 @@ class OneProxyTest < Test::Unit::TestCase
165
190
  should "raise exception if invalid" do
166
191
  post = @post_class.create
167
192
  assert_raises(MongoMapper::DocumentNotValid) do
168
- post.author.create!
193
+ post.create_author!
169
194
  end
170
195
  end
171
196
 
172
197
  should "work if valid" do
173
198
  post = @post_class.create
174
- author = post.author.create!(:name => 'John')
199
+ author = post.create_author!(:name => 'John')
175
200
  post.author.should be_instance_of(@author_class)
176
201
  post.author.should_not be_new
177
202
  post.author.name.should == 'John'
@@ -179,4 +204,19 @@ class OneProxyTest < Test::Unit::TestCase
179
204
  post.author.post_id.should == post.id
180
205
  end
181
206
  end
207
+
208
+ context "namespaced foreign keys" do
209
+ setup do
210
+ News::Paper.one :article, :class_name => 'News::Article'
211
+ News::Article.belongs_to :paper, :class_name => 'News::Paper'
212
+
213
+ @paper = News::Paper.create
214
+ end
215
+
216
+ should "properly infer the foreign key" do
217
+ article = @paper.create_article
218
+ article.should respond_to(:paper_id)
219
+ article.paper_id.should == @paper.id
220
+ end
221
+ end
182
222
  end
@@ -36,6 +36,8 @@ class AssociationsTest < Test::Unit::TestCase
36
36
  tag2 = AwesomeTag.new(:name => 'grand')
37
37
  post1 = AwesomePost.create(:creator => user, :tags => [tag1])
38
38
  post2 = AwesomePost.create(:creator => user, :tags => [tag2])
39
+
40
+ user.reload
39
41
  user.posts.should == [post1, post2]
40
42
 
41
43
  post1 = post1.reload
@@ -8,6 +8,7 @@ class CachingTest < Test::Unit::TestCase
8
8
  plugin MongoMapper::Plugins::Caching
9
9
  end
10
10
  @klass.stubs(:name).returns('Post')
11
+ @klass.any_instance.stubs(:persisted?).returns(true)
11
12
  @klass.any_instance.stubs(:[]).returns(nil)
12
13
  @klass.any_instance.stubs(:[]=).returns(nil)
13
14
  end
@@ -15,7 +16,7 @@ class CachingTest < Test::Unit::TestCase
15
16
  context "new" do
16
17
  setup do
17
18
  @doc = @klass.new
18
- @doc.stubs(:new?).returns(true)
19
+ @doc.stubs(:persisted?).returns(false)
19
20
  end
20
21
 
21
22
  should "be class/new" do
@@ -35,7 +36,7 @@ class CachingTest < Test::Unit::TestCase
35
36
  setup do
36
37
  @object_id = BSON::ObjectId.new
37
38
  @doc = @klass.new
38
- @doc.stubs(:new?).returns(false)
39
+ @doc.stubs(:persisted).returns(true)
39
40
  @doc.stubs(:id).returns(@object_id)
40
41
  end
41
42
 
@@ -4,15 +4,13 @@ module CallbacksSupport
4
4
  def self.included base
5
5
  base.key :name, String
6
6
 
7
- [ :before_validation_on_create, :before_validation_on_update,
8
- :before_validation, :after_validation,
7
+ [ :before_validation, :after_validation,
9
8
  :before_create, :after_create,
10
9
  :before_update, :after_update,
11
10
  :before_save, :after_save,
12
- :before_destroy, :after_destroy].each do |callback|
13
- callback_method = "#{callback}_callback"
14
- base.send(callback, callback_method)
15
- define_method(callback_method) do
11
+ :before_destroy, :after_destroy
12
+ ].each do |callback|
13
+ base.send(callback) do
16
14
  history << callback.to_sym
17
15
  end
18
16
  end
@@ -29,8 +27,23 @@ module CallbacksSupport
29
27
  end
30
28
 
31
29
  class CallbacksTest < Test::Unit::TestCase
32
- CreateCallbackOrder = [:before_validation, :before_validation_on_create, :after_validation, :before_save, :before_create, :after_create, :after_save]
33
- UpdateCallbackOrder = [:before_validation, :before_validation_on_update, :after_validation, :before_save, :before_update, :after_update, :after_save]
30
+ CreateCallbackOrder = [
31
+ :before_validation,
32
+ :after_validation,
33
+ :before_save,
34
+ :before_create,
35
+ :after_create,
36
+ :after_save
37
+ ]
38
+
39
+ UpdateCallbackOrder = [
40
+ :before_validation,
41
+ :after_validation,
42
+ :before_save,
43
+ :before_update,
44
+ :after_update,
45
+ :after_save
46
+ ]
34
47
 
35
48
  context "Defining and running callbacks on documents" do
36
49
  setup do
@@ -102,11 +115,8 @@ class CallbacksTest < Test::Unit::TestCase
102
115
  child = @child_class.new(:name => 'Child', :children => [grand])
103
116
  root = @root_class.create(:name => 'Parent', :children => [child])
104
117
 
105
- child = root.children.first
106
- child.history.should == CreateCallbackOrder
107
-
108
- grand = root.children.first.children.first
109
- grand.history.should == CreateCallbackOrder
118
+ root.children.first.history.should == CreateCallbackOrder
119
+ root.children.first.children.first.history.should == CreateCallbackOrder
110
120
  end
111
121
 
112
122
  should "get the order right based on root document updating" do
@@ -116,11 +126,8 @@ class CallbacksTest < Test::Unit::TestCase
116
126
  root.clear_history
117
127
  root.update_attributes(:name => 'Updated Parent')
118
128
 
119
- child = root.children.first
120
- child.history.should == UpdateCallbackOrder
121
-
122
- grand = root.children.first.children.first
123
- grand.history.should == UpdateCallbackOrder
129
+ root.children.first.history.should == UpdateCallbackOrder
130
+ root.children.first.children.first.history.should == UpdateCallbackOrder
124
131
  end
125
132
 
126
133
  should "work for before and after destroy" do
@@ -160,4 +160,126 @@ class DirtyTest < Test::Unit::TestCase
160
160
  milestone.changed.should == %w(project_id)
161
161
  end
162
162
  end
163
- end
163
+
164
+ context "save with an invalid document" do
165
+ should "not clear changes" do
166
+ validated_class = Doc do
167
+ key :name, String
168
+ key :required, String, :required=>true
169
+ end
170
+ validated_doc = validated_class.new
171
+ validated_doc.name = "I'm a changin"
172
+ validated_doc.save
173
+ validated_doc.changed?.should be_true
174
+
175
+ validated_doc.required = 1
176
+ validated_doc.save
177
+ validated_doc.changed?.should be_false
178
+ end
179
+ end
180
+
181
+ context "changing an already changed attribute" do
182
+ should "preserve the original value" do
183
+ doc = @document.create(:a=>"b")
184
+ doc.a = "c"
185
+ doc.a_change.should == ["b","c"]
186
+ doc.a = "d"
187
+ doc.a_change.should == ["b","d"]
188
+ end
189
+ should "reset changes when set back to the original value" do
190
+ doc = @document.create(:a=>"b")
191
+ doc.a = "c"
192
+ doc.a = "b"
193
+ doc.changed?.should be_false
194
+ end
195
+ end
196
+
197
+ context "reset_attribute!" do
198
+ should "reset the attribute back to the previous value" do
199
+ doc = @document.create(:a=>"b")
200
+ doc.a = "c"
201
+ doc.reset_a!
202
+ doc.changed?.should be_false
203
+ doc.a.should == "b"
204
+ end
205
+ should "reset the attribute back to the original value after several changes" do
206
+ doc = @document.create(:a=>"b")
207
+ doc.a = "c"
208
+ doc.a = "d"
209
+ doc.a = "e"
210
+ doc.reset_a!
211
+ doc.changed?.should be_false
212
+ doc.a.should == "b"
213
+ end
214
+ end
215
+
216
+ context "previous_changes" do
217
+ should "reflect previously committed change" do
218
+ doc = @document.create(:a=>"b")
219
+ doc.a = "c"
220
+ changes = doc.changes
221
+ doc.save!
222
+ doc.previous_changes.should == changes
223
+ end
224
+
225
+ should "not include attributes loaded from db" do
226
+ doc = @document.create(:a => "b")
227
+ @document.find(doc.id).previous_changes.should be_blank
228
+ end
229
+ end
230
+
231
+ context "Embedded documents" do
232
+ setup do
233
+ @edoc = EDoc('Duck') { key :name, String }
234
+ @edoc.plugin MongoMapper::Plugins::Dirty
235
+ @document = Doc('Long') { key :name, String }
236
+ @document.many :ducks, :class=>@edoc
237
+ @doc = @document.new
238
+ @duck = @doc.ducks.build
239
+ end
240
+
241
+ should "track changes" do
242
+ @duck.name = "hi"
243
+ @duck.changed?.should be_true
244
+ end
245
+
246
+ should "clear changes when saved" do
247
+ @duck.name = "hi"
248
+ @duck.changed?.should be_true
249
+ @duck.save!
250
+ @duck.changed?.should_not be_true
251
+ end
252
+
253
+ should "clear changes when the parent is saved" do
254
+ @duck.name = "hi"
255
+ @duck.changed?.should be_true
256
+ @doc.save!
257
+ @duck.changed?.should_not be_true
258
+ end
259
+
260
+ context "with nested embedded documents" do
261
+ setup do
262
+ @inner_edoc = EDoc('Dong') {key :name, String}
263
+ @inner_edoc.plugin MongoMapper::Plugins::Dirty
264
+ @edoc.many :dongs, :class=>@inner_edoc
265
+ @dong = @duck.dongs.build
266
+ end
267
+
268
+ should "track changes" do
269
+ @dong.name = "hi"
270
+ @dong.changed?.should be_true
271
+ end
272
+
273
+ should "clear changes when the root saves" do
274
+ @dong.name = "hi"
275
+ @dong.changed?.should be_true
276
+ @doc.save!
277
+ @dong.changed?.should be_false
278
+ end
279
+
280
+ end
281
+
282
+ end
283
+
284
+
285
+ end
@@ -192,11 +192,15 @@ class DocumentTest < Test::Unit::TestCase
192
192
 
193
193
  @document.many :foos, :class => @foo_class
194
194
  @document.many :bars, :class => @bar_class
195
+ @document.belongs_to :foo, :class => @foo_class
196
+ @document.one :bar, :class => @bar_class
195
197
 
196
198
  @instance = @document.create({
197
- :age => 39,
199
+ :age => 39,
198
200
  :foos => [@foo_class.new(:name => '1')],
199
201
  :bars => [@bar_class.new(:name => '1')],
202
+ :foo => @foo_class.new(:name => '2'),
203
+ :bar => @bar_class.new(:name => '2')
200
204
  })
201
205
  end
202
206
 
@@ -207,12 +211,27 @@ class DocumentTest < Test::Unit::TestCase
207
211
  @instance.age.should == 39
208
212
  end
209
213
 
210
- should "reset all associations" do
214
+ should "reset many associations" do
211
215
  @instance.foos.expects(:reset).at_least_once
212
216
  @instance.bars.expects(:reset).at_least_once
213
217
  @instance.reload
214
218
  end
215
219
 
220
+ should "reset belongs_to association" do
221
+ @instance.foo = nil
222
+ @instance.reload
223
+ @instance.foo.should_not be_nil
224
+ end
225
+
226
+ should "reset one association" do
227
+ @instance.bar = nil
228
+ @instance.reload
229
+ @instance.bar.should_not be_nil
230
+ end
231
+
232
+ should "reset nil one association" do
233
+ end
234
+
216
235
  should "reinstantiate embedded associations" do
217
236
  @instance.reload
218
237
  @instance.bars.first.name.should == '1'
@@ -250,4 +269,9 @@ class DocumentTest < Test::Unit::TestCase
250
269
  doc.skills.should == ['ruby', 'rails', 'javascript', 'xhtml', 'css']
251
270
  end
252
271
  end
272
+
273
+ should "not walk ObjectSpace when creating a model" do
274
+ ObjectSpace.expects(:each_object).never
275
+ Doc()
276
+ end
253
277
  end