mongo_mapper 0.8.6 → 0.9.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 (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
@@ -2,6 +2,8 @@
2
2
  module MongoMapper
3
3
  module Plugins
4
4
  module Document
5
+ extend ActiveSupport::Concern
6
+
5
7
  module ClassMethods
6
8
  def embeddable?
7
9
  false
@@ -20,8 +22,8 @@ module MongoMapper
20
22
  def reload
21
23
  if doc = collection.find_one(:_id => id)
22
24
  tap do |instance|
23
- instance.class.associations.each_key do |association_name|
24
- send(association_name).reset if respond_to?(association_name)
25
+ instance.class.associations.each_value do |association|
26
+ get_proxy(association).reset
25
27
  end
26
28
  instance.attributes = doc
27
29
  end
@@ -4,6 +4,8 @@ require 'mongo_mapper/plugins/dynamic_querying/dynamic_finder'
4
4
  module MongoMapper
5
5
  module Plugins
6
6
  module DynamicQuerying
7
+ extend ActiveSupport::Concern
8
+
7
9
  module ClassMethods
8
10
  def dynamic_find(finder, args)
9
11
  attributes = {}
@@ -0,0 +1,43 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module EmbeddedCallbacks
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ extend ::ActiveModel::Callbacks
9
+ include ::ActiveModel::Validations::Callbacks
10
+
11
+ define_model_callbacks :validation, :save, :create, :update, :destroy, :only => [:before, :after]
12
+ end
13
+
14
+ module InstanceMethods
15
+ def run_callbacks(callback, &block)
16
+ embedded_docs = []
17
+
18
+ embedded_associations.each do |association|
19
+ embedded_docs += Array(get_proxy(association).send(:load_target))
20
+ end
21
+
22
+ block = embedded_docs.inject(block) do |chain, doc|
23
+ if doc.class.respond_to?("_#{callback}_callbacks")
24
+ lambda { doc.run_callbacks(callback, &chain) }
25
+ else
26
+ chain
27
+ end
28
+ end
29
+ super callback, &block
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ # Need to monkey patch ActiveModel for now since it uses the internal
37
+ # _run_validation_callbacks, which is impossible to override due to the
38
+ # way ActiveSupport::Callbacks is implemented.
39
+ ActiveModel::Validations::Callbacks.class_eval do
40
+ def run_validations!
41
+ run_callbacks(:validation) { super }
42
+ end
43
+ end
@@ -2,10 +2,10 @@
2
2
  module MongoMapper
3
3
  module Plugins
4
4
  module EmbeddedDocument
5
- def self.configure(model)
6
- model.class_eval do
7
- attr_accessor :_parent_document
8
- end
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ attr_accessor :_parent_document
9
9
  end
10
10
 
11
11
  module ClassMethods
@@ -14,7 +14,7 @@ module MongoMapper
14
14
  end
15
15
 
16
16
  def embedded_in(owner_name)
17
- define_method(owner_name) { _parent_document }
17
+ alias_method owner_name, :_parent_document
18
18
  end
19
19
  end
20
20
 
@@ -29,20 +29,27 @@ module MongoMapper
29
29
 
30
30
  def save(options={})
31
31
  _root_document.try(:save, options).tap do |result|
32
- @_new = false if result
32
+ persist(options) if result
33
33
  end
34
34
  end
35
35
 
36
36
  def save!(options={})
37
- _root_document.try(:save, options).tap do |result|
38
- @_new = false if result
37
+ valid? || raise(DocumentNotValid.new(self))
38
+ _root_document.try(:save!, options).tap do |result|
39
+ persist(options) if result
39
40
  end
40
41
  end
41
42
 
43
+ def persist(options={})
44
+ @_new = false
45
+ clear_changes if respond_to?(:clear_changes)
46
+ save_to_collection(options)
47
+ end
48
+
42
49
  def _root_document
43
50
  @_root_document ||= _parent_document.try(:_root_document)
44
51
  end
45
52
  end
46
53
  end
47
54
  end
48
- end
55
+ end
@@ -2,6 +2,8 @@
2
2
  module MongoMapper
3
3
  module Plugins
4
4
  module Equality
5
+ extend ActiveSupport::Concern
6
+
5
7
  module InstanceMethods
6
8
  def eql?(other)
7
9
  other.is_a?(self.class) && _id == other._id
@@ -4,6 +4,8 @@ require 'set'
4
4
  module MongoMapper
5
5
  module Plugins
6
6
  module IdentityMap
7
+ extend ActiveSupport::Concern
8
+
7
9
  def self.models
8
10
  @models ||= Set.new
9
11
  end
@@ -12,8 +14,8 @@ module MongoMapper
12
14
  models.each { |m| m.identity_map.clear }
13
15
  end
14
16
 
15
- def self.configure(model)
16
- IdentityMap.models << model
17
+ included do
18
+ IdentityMap.models << self
17
19
  end
18
20
 
19
21
  module ClassMethods
@@ -2,6 +2,8 @@
2
2
  module MongoMapper
3
3
  module Plugins
4
4
  module Indexes
5
+ extend ActiveSupport::Concern
6
+
5
7
  module ClassMethods
6
8
  def ensure_index(spec, options={})
7
9
  collection.create_index(spec, options)
@@ -2,9 +2,11 @@
2
2
  module MongoMapper
3
3
  module Plugins
4
4
  module Inspect
5
+ extend ActiveSupport::Concern
6
+
5
7
  module InstanceMethods
6
8
  def inspect
7
- attributes_as_nice_string = key_names.collect do |name|
9
+ attributes_as_nice_string = key_names.sort.collect do |name|
8
10
  "#{name}: #{self[name].inspect}"
9
11
  end.join(", ")
10
12
  "#<#{self.class} #{attributes_as_nice_string}>"
@@ -4,8 +4,11 @@ require 'mongo_mapper/plugins/keys/key'
4
4
  module MongoMapper
5
5
  module Plugins
6
6
  module Keys
7
- def self.configure(model)
8
- model.key :_id, ObjectId
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ extend ActiveSupport::DescendantsTracker
11
+ key :_id, ObjectId
9
12
  end
10
13
 
11
14
  module ClassMethods
@@ -15,7 +18,7 @@ module MongoMapper
15
18
  end
16
19
 
17
20
  def keys
18
- @keys ||= HashWithIndifferentAccess.new
21
+ @keys ||= {}
19
22
  end
20
23
 
21
24
  def key(*args)
@@ -37,7 +40,7 @@ module MongoMapper
37
40
  end
38
41
 
39
42
  def object_id_keys
40
- keys.keys.select { |key| keys[key].type == ObjectId }.map(&:to_sym)
43
+ keys.keys.select { |key| keys[key].type == ObjectId }.map { |k| k.to_sym }
41
44
  end
42
45
 
43
46
  def object_id_key?(name)
@@ -92,8 +95,8 @@ module MongoMapper
92
95
  read_key(:#{key.name})
93
96
  end
94
97
 
95
- def #{key.name}_before_typecast
96
- read_key_before_typecast(:#{key.name})
98
+ def #{key.name}_before_type_cast
99
+ read_key_before_type_cast(:#{key.name})
97
100
  end
98
101
 
99
102
  def #{key.name}=(value)
@@ -137,11 +140,11 @@ module MongoMapper
137
140
  end
138
141
 
139
142
  if key.options[:in]
140
- validates_inclusion_of(attribute, :within => key.options[:in])
143
+ validates_inclusion_of(attribute, :in => key.options[:in])
141
144
  end
142
145
 
143
146
  if key.options[:not_in]
144
- validates_exclusion_of(attribute, :within => key.options[:not_in])
147
+ validates_exclusion_of(attribute, :in => key.options[:not_in])
145
148
  end
146
149
 
147
150
  if key.options[:length]
@@ -168,6 +171,7 @@ module MongoMapper
168
171
  def initialize_from_database(attrs={})
169
172
  @_new = false
170
173
  load_from_database(attrs)
174
+ default_id_value(attrs)
171
175
  self
172
176
  end
173
177
 
@@ -196,7 +200,7 @@ module MongoMapper
196
200
 
197
201
  embedded_associations.each do |association|
198
202
  if documents = instance_variable_get(association.ivar)
199
- if association.one?
203
+ if association.is_a?(Associations::OneAssociation)
200
204
  attrs[association.name] = documents.to_mongo
201
205
  else
202
206
  attrs[association.name] = documents.map { |document| document.to_mongo }
@@ -221,6 +225,10 @@ module MongoMapper
221
225
  save!
222
226
  end
223
227
 
228
+ def update_attribute(name, value)
229
+ update_attributes(name.to_sym => value)
230
+ end
231
+
224
232
  def id
225
233
  _id
226
234
  end
@@ -258,6 +266,13 @@ module MongoMapper
258
266
  keys.values.select { |key| key.embeddable? }
259
267
  end
260
268
 
269
+ def default_id_value(attrs={})
270
+ id_provided = !attrs.nil? && attrs.keys.map { |k| k.to_s }.detect { |k| k == 'id' || k == '_id' }
271
+ if !id_provided && self.class.can_default_id?
272
+ write_key :_id, BSON::ObjectId.new
273
+ end
274
+ end
275
+
261
276
  private
262
277
  def load_from_database(attrs)
263
278
  return if attrs.blank?
@@ -270,15 +285,6 @@ module MongoMapper
270
285
  end
271
286
  end
272
287
 
273
- def default_id_value(attrs)
274
- unless attrs.nil?
275
- id_provided = attrs.keys.map { |k| k.to_s }.detect { |k| k == 'id' || k == '_id' }
276
- if !id_provided && self.class.can_default_id?
277
- write_key :_id, BSON::ObjectId.new
278
- end
279
- end
280
- end
281
-
282
288
  def ensure_key_exists(name)
283
289
  self.class.key(name) unless respond_to?("#{name}=")
284
290
  end
@@ -290,21 +296,21 @@ module MongoMapper
290
296
  end
291
297
 
292
298
  def read_key(key_name)
293
- if key = keys[key_name]
299
+ if key = keys[key_name.to_s]
294
300
  value = key.get(instance_variable_get(:"@#{key_name}"))
295
301
  set_parent_document(key, value)
296
302
  instance_variable_set(:"@#{key_name}", value)
297
303
  end
298
304
  end
299
305
 
300
- def read_key_before_typecast(name)
301
- instance_variable_get(:"@#{name}_before_typecast")
306
+ def read_key_before_type_cast(name)
307
+ instance_variable_get(:"@#{name}_before_type_cast")
302
308
  end
303
309
 
304
310
  def write_key(name, value)
305
311
  key = keys[name.to_s]
306
312
  set_parent_document(key, value)
307
- instance_variable_set :"@#{name}_before_typecast", value
313
+ instance_variable_set :"@#{name}_before_type_cast", value
308
314
  instance_variable_set :"@#{name}", key.set(value)
309
315
  end
310
316
  end
@@ -22,11 +22,11 @@ module MongoMapper
22
22
  end
23
23
 
24
24
  def can_default_id?
25
- type && [ObjectId, BSON::ObjectId, String].include?(type)
25
+ type && (type == ObjectId || type == BSON::ObjectId || type == String)
26
26
  end
27
27
 
28
28
  def number?
29
- [Integer, Float].include?(type)
29
+ type == Integer || type == Float
30
30
  end
31
31
 
32
32
  def get(value)
@@ -34,11 +34,17 @@ module MongoMapper
34
34
  if default_value.respond_to?(:call)
35
35
  return default_value.call
36
36
  else
37
- return default_value
37
+ # Using Marshal is easiest way to get a copy of mutable objects
38
+ # without getting an error on immutable objects
39
+ return Marshal.load(Marshal.dump(default_value))
38
40
  end
39
41
  end
40
42
 
41
- type.from_mongo(value)
43
+ if options[:typecast].present?
44
+ type.from_mongo(value).map! { |v| typecast_class.from_mongo(v) }
45
+ else
46
+ type.from_mongo(value)
47
+ end
42
48
  end
43
49
 
44
50
  def set(value)
@@ -48,7 +54,7 @@ module MongoMapper
48
54
  end
49
55
  end
50
56
  end
51
-
57
+
52
58
  private
53
59
  def typecast_class
54
60
  @typecast_class ||= options[:typecast].constantize
@@ -56,4 +62,4 @@ module MongoMapper
56
62
  end
57
63
  end
58
64
  end
59
- end
65
+ end
@@ -2,6 +2,8 @@
2
2
  module MongoMapper
3
3
  module Plugins
4
4
  module Logger
5
+ extend ActiveSupport::Concern
6
+
5
7
  module ClassMethods
6
8
  def logger
7
9
  MongoMapper.logger
@@ -2,6 +2,8 @@
2
2
  module MongoMapper
3
3
  module Plugins
4
4
  module Modifiers
5
+ extend ActiveSupport::Concern
6
+
5
7
  module ClassMethods
6
8
  def increment(*args)
7
9
  modifier_update('$inc', args)
@@ -17,7 +19,7 @@ module MongoMapper
17
19
  def set(*args)
18
20
  criteria, updates = criteria_and_keys_from_args(args)
19
21
  updates.each do |key, value|
20
- updates[key] = keys[key].set(value) if key?(key)
22
+ updates[key] = keys[key.to_s].set(value) if key?(key)
21
23
  end
22
24
  collection.update(criteria, {'$set' => updates}, :multi => true)
23
25
  end
@@ -2,6 +2,8 @@
2
2
  module MongoMapper
3
3
  module Plugins
4
4
  module Pagination
5
+ extend ActiveSupport::Concern
6
+
5
7
  module ClassMethods
6
8
  def per_page; 25 end
7
9
 
@@ -2,6 +2,8 @@
2
2
  module MongoMapper
3
3
  module Plugins
4
4
  module Persistence
5
+ extend ActiveSupport::Concern
6
+
5
7
  module ClassMethods
6
8
  def connection(mongo_connection=nil)
7
9
  assert_supported
@@ -4,6 +4,8 @@ require 'set'
4
4
  module MongoMapper
5
5
  module Plugins
6
6
  module Protected
7
+ extend ActiveSupport::Concern
8
+
7
9
  module ClassMethods
8
10
  def attr_protected(*attrs)
9
11
  raise AccessibleOrProtected.new(name) if try(:accessible_attributes?)
@@ -5,6 +5,8 @@ require 'mongo_mapper/plugins/querying/plucky_methods'
5
5
  module MongoMapper
6
6
  module Plugins
7
7
  module Querying
8
+ extend ActiveSupport::Concern
9
+
8
10
  module ClassMethods
9
11
  include PluckyMethods
10
12
 
@@ -50,7 +52,7 @@ module MongoMapper
50
52
  end
51
53
 
52
54
  def destroy(*ids)
53
- find_some!(ids.flatten).each(&:destroy)
55
+ find_some!(ids.flatten).each { |doc| doc.destroy }
54
56
  end
55
57
 
56
58
  def destroy_all(options={})
@@ -148,13 +150,12 @@ module MongoMapper
148
150
  end
149
151
 
150
152
  def delete
151
- @_destroyed = true
152
- self.class.delete(id) unless new?
153
+ self.class.delete(id).tap { @_destroyed = true } if persisted?
153
154
  end
154
155
 
155
156
  private
156
157
  def create_or_update(options={})
157
- result = new? ? create(options) : update(options)
158
+ result = persisted? ? update(options) : create(options)
158
159
  result != false
159
160
  end
160
161