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
@@ -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