mongo_mapper 0.6.10 → 0.7.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 (106) hide show
  1. data/README.rdoc +5 -14
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/lib/mongo_mapper.rb +48 -56
  5. data/lib/mongo_mapper/document.rb +136 -164
  6. data/lib/mongo_mapper/embedded_document.rb +29 -354
  7. data/lib/mongo_mapper/plugins.rb +31 -0
  8. data/lib/mongo_mapper/plugins/associations.rb +105 -0
  9. data/lib/mongo_mapper/plugins/associations/base.rb +123 -0
  10. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +30 -0
  11. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +25 -0
  12. data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
  13. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +50 -0
  14. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +139 -0
  15. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  16. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +117 -0
  17. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
  18. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
  19. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
  20. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  21. data/lib/mongo_mapper/plugins/associations/proxy.rb +118 -0
  22. data/lib/mongo_mapper/plugins/callbacks.rb +65 -0
  23. data/lib/mongo_mapper/plugins/clone.rb +13 -0
  24. data/lib/mongo_mapper/plugins/descendants.rb +16 -0
  25. data/lib/mongo_mapper/plugins/dirty.rb +119 -0
  26. data/lib/mongo_mapper/plugins/equality.rb +23 -0
  27. data/lib/mongo_mapper/plugins/identity_map.rb +122 -0
  28. data/lib/mongo_mapper/plugins/inspect.rb +14 -0
  29. data/lib/mongo_mapper/plugins/keys.rb +324 -0
  30. data/lib/mongo_mapper/plugins/logger.rb +17 -0
  31. data/lib/mongo_mapper/plugins/pagination.rb +24 -0
  32. data/lib/mongo_mapper/plugins/pagination/proxy.rb +68 -0
  33. data/lib/mongo_mapper/plugins/protected.rb +45 -0
  34. data/lib/mongo_mapper/plugins/rails.rb +45 -0
  35. data/lib/mongo_mapper/plugins/serialization.rb +105 -0
  36. data/lib/mongo_mapper/plugins/validations.rb +46 -0
  37. data/lib/mongo_mapper/query.rb +130 -0
  38. data/lib/mongo_mapper/support.rb +40 -17
  39. data/lib/mongo_mapper/support/descendant_appends.rb +46 -0
  40. data/lib/mongo_mapper/support/find.rb +77 -0
  41. data/mongo_mapper.gemspec +55 -38
  42. data/performance/read_write.rb +52 -0
  43. data/specs.watchr +23 -2
  44. data/test/functional/associations/test_belongs_to_proxy.rb +12 -10
  45. data/test/functional/associations/test_many_documents_as_proxy.rb +4 -21
  46. data/test/functional/associations/test_many_documents_proxy.rb +2 -8
  47. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +59 -39
  48. data/test/functional/associations/test_many_embedded_proxy.rb +145 -81
  49. data/test/functional/associations/test_many_polymorphic_proxy.rb +2 -40
  50. data/test/functional/associations/test_one_proxy.rb +25 -10
  51. data/test/functional/test_binary.rb +2 -8
  52. data/test/functional/test_callbacks.rb +1 -5
  53. data/test/functional/test_dirty.rb +27 -23
  54. data/test/functional/test_document.rb +224 -165
  55. data/test/functional/test_embedded_document.rb +72 -82
  56. data/test/functional/test_identity_map.rb +508 -0
  57. data/test/functional/test_modifiers.rb +15 -5
  58. data/test/functional/test_pagination.rb +1 -3
  59. data/test/functional/test_protected.rb +155 -0
  60. data/test/functional/test_string_id_compatibility.rb +7 -12
  61. data/test/functional/test_validations.rb +26 -58
  62. data/test/models.rb +0 -39
  63. data/test/test_helper.rb +37 -3
  64. data/test/unit/associations/test_base.rb +5 -5
  65. data/test/unit/associations/test_proxy.rb +8 -6
  66. data/test/unit/test_descendant_appends.rb +71 -0
  67. data/test/unit/test_document.rb +71 -76
  68. data/test/unit/test_dynamic_finder.rb +27 -29
  69. data/test/unit/test_embedded_document.rb +555 -601
  70. data/test/unit/{test_key.rb → test_keys.rb} +2 -5
  71. data/test/unit/test_mongo_mapper.rb +69 -9
  72. data/test/unit/test_pagination.rb +40 -32
  73. data/test/unit/test_plugins.rb +50 -0
  74. data/test/unit/{test_finder_options.rb → test_query.rb} +74 -65
  75. data/test/unit/test_rails.rb +123 -0
  76. data/test/unit/{test_serializations.rb → test_serialization.rb} +1 -2
  77. data/test/unit/test_support.rb +23 -7
  78. data/test/unit/test_time_zones.rb +3 -4
  79. data/test/unit/test_validations.rb +58 -17
  80. metadata +53 -36
  81. data/lib/mongo_mapper/associations.rb +0 -78
  82. data/lib/mongo_mapper/associations/base.rb +0 -119
  83. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +0 -26
  84. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +0 -21
  85. data/lib/mongo_mapper/associations/collection.rb +0 -19
  86. data/lib/mongo_mapper/associations/in_array_proxy.rb +0 -137
  87. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +0 -26
  88. data/lib/mongo_mapper/associations/many_documents_proxy.rb +0 -115
  89. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +0 -31
  90. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +0 -54
  91. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +0 -11
  92. data/lib/mongo_mapper/associations/one_proxy.rb +0 -64
  93. data/lib/mongo_mapper/associations/proxy.rb +0 -116
  94. data/lib/mongo_mapper/callbacks.rb +0 -61
  95. data/lib/mongo_mapper/dirty.rb +0 -117
  96. data/lib/mongo_mapper/dynamic_finder.rb +0 -74
  97. data/lib/mongo_mapper/finder_options.rb +0 -145
  98. data/lib/mongo_mapper/key.rb +0 -36
  99. data/lib/mongo_mapper/mongo_mapper.rb +0 -125
  100. data/lib/mongo_mapper/pagination.rb +0 -66
  101. data/lib/mongo_mapper/rails_compatibility/document.rb +0 -15
  102. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +0 -28
  103. data/lib/mongo_mapper/serialization.rb +0 -54
  104. data/lib/mongo_mapper/serializers/json_serializer.rb +0 -48
  105. data/lib/mongo_mapper/validations.rb +0 -39
  106. data/test/functional/test_rails_compatibility.rb +0 -25
@@ -1,380 +1,55 @@
1
1
  module MongoMapper
2
2
  module EmbeddedDocument
3
+ extend Support::DescendantAppends
4
+
3
5
  def self.included(model)
4
6
  model.class_eval do
5
- extend ClassMethods
6
7
  include InstanceMethods
7
-
8
- extend Associations::ClassMethods
9
- include Associations::InstanceMethods
10
-
11
- include RailsCompatibility::EmbeddedDocument
12
- include Validatable
13
- include Serialization
8
+ extend ClassMethods
9
+ extend Plugins
14
10
 
15
- extend Validations::Macros
11
+ plugin Plugins::Associations
12
+ plugin Plugins::Clone
13
+ plugin Plugins::Descendants
14
+ plugin Plugins::Equality
15
+ plugin Plugins::Inspect
16
+ plugin Plugins::Keys
17
+ plugin Plugins::Logger
18
+ plugin Plugins::Protected
19
+ plugin Plugins::Rails
20
+ plugin Plugins::Serialization
21
+ plugin Plugins::Validations
16
22
 
17
- key :_id, ObjectId
18
- attr_accessor :_root_document
23
+ attr_accessor :_root_document, :_parent_document
19
24
  end
25
+
26
+ super
20
27
  end
21
28
 
22
29
  module ClassMethods
23
- def logger
24
- MongoMapper.logger
25
- end
26
-
27
- def inherited(subclass)
28
- unless subclass.embeddable?
29
- subclass.set_collection_name(collection_name)
30
- end
31
-
32
- (@subclasses ||= []) << subclass
33
- end
34
-
35
- def subclasses
36
- @subclasses
37
- end
38
-
39
- def keys
40
- @keys ||= if parent = parent_model
41
- parent.keys.dup
42
- else
43
- HashWithIndifferentAccess.new
44
- end
45
- end
46
-
47
- def key(*args)
48
- key = Key.new(*args)
49
- keys[key.name] = key
50
-
51
- create_accessors_for(key)
52
- create_key_in_subclasses(*args)
53
- create_validations_for(key)
54
-
55
- key
56
- end
57
-
58
- def using_object_id?
59
- object_id_key?(:_id)
60
- end
61
-
62
- def object_id_key?(name)
63
- key = keys[name.to_s]
64
- key && key.type == ObjectId
65
- end
66
-
67
30
  def embeddable?
68
- !self.ancestors.include?(Document)
31
+ true
69
32
  end
70
33
 
71
- def parent_model
72
- (ancestors - [self,EmbeddedDocument]).find do |parent_class|
73
- parent_class.ancestors.include?(EmbeddedDocument)
74
- end
75
- end
76
-
77
- def to_mongo(instance)
78
- return nil if instance.nil?
79
- instance.to_mongo
80
- end
81
-
82
- def from_mongo(value)
83
- return nil if value.nil?
84
- value.is_a?(self) ? value : initialize_doc(value)
85
- end
86
-
87
- private
88
- def initialize_doc(doc)
89
- begin
90
- klass = doc['_type'].present? ? doc['_type'].constantize : self
91
- klass.new(doc)
92
- rescue NameError
93
- new(doc)
94
- end
95
- end
96
-
97
- def accessors_module
98
- module_defined = if method(:const_defined?).arity == 1 # Ruby 1.9 compat check
99
- const_defined?('MongoMapperKeys')
100
- else
101
- const_defined?('MongoMapperKeys', false)
102
- end
103
-
104
- if module_defined
105
- const_get 'MongoMapperKeys'
106
- else
107
- const_set 'MongoMapperKeys', Module.new
108
- end
109
- end
110
-
111
- def create_accessors_for(key)
112
- accessors_module.module_eval <<-end_eval
113
- def #{key.name}
114
- read_attribute(:'#{key.name}')
115
- end
116
-
117
- def #{key.name}_before_typecast
118
- read_attribute_before_typecast(:'#{key.name}')
119
- end
120
-
121
- def #{key.name}=(value)
122
- write_attribute(:'#{key.name}', value)
123
- end
124
-
125
- def #{key.name}?
126
- read_attribute(:#{key.name}).present?
127
- end
128
- end_eval
129
- include accessors_module
130
- end
131
-
132
- def create_key_in_subclasses(*args)
133
- return if subclasses.blank?
134
-
135
- subclasses.each do |subclass|
136
- subclass.key(*args)
137
- end
138
- end
139
-
140
- def create_validations_for(key)
141
- attribute = key.name.to_sym
142
-
143
- if key.options[:required]
144
- validates_presence_of(attribute)
145
- end
146
-
147
- if key.options[:unique]
148
- validates_uniqueness_of(attribute)
149
- end
150
-
151
- if key.options[:numeric]
152
- number_options = key.type == Integer ? {:only_integer => true} : {}
153
- validates_numericality_of(attribute, number_options)
154
- end
155
-
156
- if key.options[:format]
157
- validates_format_of(attribute, :with => key.options[:format])
158
- end
159
-
160
- if key.options[:length]
161
- length_options = case key.options[:length]
162
- when Integer
163
- {:minimum => 0, :maximum => key.options[:length]}
164
- when Range
165
- {:within => key.options[:length]}
166
- when Hash
167
- key.options[:length]
168
- end
169
- validates_length_of(attribute, length_options)
170
- end
34
+ def embedded_in(owner_name)
35
+ define_method(owner_name) { _parent_document }
171
36
  end
172
37
  end
173
38
 
174
39
  module InstanceMethods
175
- def initialize(attrs={})
176
- unless attrs.nil?
177
- associations.each do |name, association|
178
- if collection = attrs.delete(name)
179
- if association.many? && association.klass.embeddable?
180
- root_document = attrs[:_root_document] || self
181
- collection.each do |doc|
182
- doc[:_root_document] = root_document
183
- end
184
- end
185
- send("#{association.name}=", collection)
186
- end
187
- end
188
-
189
- self.attributes = attrs
190
-
191
- if respond_to?(:_type=) && self['_type'].blank?
192
- self._type = self.class.name
193
- end
194
- end
195
-
196
- if self.class.embeddable?
197
- if read_attribute(:_id).blank?
198
- write_attribute :_id, Mongo::ObjectID.new
199
- @new_document = true
200
- else
201
- @new_document = false
202
- end
203
- end
204
- end
205
-
206
- def new?
207
- !!@new_document
208
- end
209
-
210
- def to_param
211
- id.to_s
212
- end
213
-
214
- def attributes=(attrs)
215
- return if attrs.blank?
216
- attrs.each_pair do |name, value|
217
- writer_method = "#{name}="
218
-
219
- if respond_to?(writer_method)
220
- self.send(writer_method, value)
221
- else
222
- self[name.to_s] = value
223
- end
224
- end
225
- end
226
-
227
- def attributes
228
- attrs = HashWithIndifferentAccess.new
229
-
230
- embedded_keys.each do |key|
231
- attrs[key.name] = read_attribute(key.name).try(:attributes)
232
- end
233
-
234
- non_embedded_keys.each do |key|
235
- attrs[key.name] = read_attribute(key.name)
236
- end
237
-
238
- embedded_associations.each do |association|
239
- documents = instance_variable_get(association.ivar)
240
- next if documents.nil?
241
- attrs[association.name] = documents.collect { |doc| doc.attributes }
242
- end
243
-
244
- attrs
245
- end
246
-
247
- def to_mongo
248
- attrs = HashWithIndifferentAccess.new
249
-
250
- _keys.each_pair do |name, key|
251
- value = key.set(read_attribute(key.name))
252
- attrs[name] = value unless value.nil?
253
- end
254
-
255
- embedded_associations.each do |association|
256
- if documents = instance_variable_get(association.ivar)
257
- attrs[association.name] = documents.map { |document| document.to_mongo }
258
- end
259
- end
260
-
261
- attrs
262
- end
263
-
264
- def clone
265
- clone_attributes = self.attributes
266
- clone_attributes.delete("_id")
267
- self.class.new(clone_attributes)
268
- end
269
-
270
- def [](name)
271
- read_attribute(name)
272
- end
273
-
274
- def []=(name, value)
275
- ensure_key_exists(name)
276
- write_attribute(name, value)
277
- end
278
-
279
- def ==(other)
280
- other.is_a?(self.class) && _id == other._id
281
- end
282
-
283
- def id
284
- self[:_id]
285
- end
286
-
287
- def id=(value)
288
- if self.class.using_object_id?
289
- value = MongoMapper.normalize_object_id(value)
290
- else
291
- @using_custom_id = true
40
+ def save(options={})
41
+ if result = _root_document.try(:save, options)
42
+ @new = false
292
43
  end
293
-
294
- self[:_id] = value
295
- end
296
-
297
- def using_custom_id?
298
- !!@using_custom_id
44
+ result
299
45
  end
300
46
 
301
- def inspect
302
- attributes_as_nice_string = key_names.collect do |name|
303
- "#{name}: #{read_attribute(name).inspect}"
304
- end.join(", ")
305
- "#<#{self.class} #{attributes_as_nice_string}>"
306
- end
307
-
308
- def save(options={})
309
- _root_document.try(:save, options)
310
- end
311
-
312
47
  def save!(options={})
313
- _root_document.try(:save!, options)
314
- end
315
-
316
- def update_attributes(attrs={})
317
- self.attributes = attrs
318
- save
319
- end
320
-
321
- def update_attributes!(attrs={})
322
- self.attributes = attrs
323
- save!
324
- end
325
-
326
- def logger
327
- self.class.logger
328
- end
329
-
330
- private
331
- def _keys
332
- self.metaclass.keys
333
- end
334
-
335
- def key_names
336
- _keys.keys
337
- end
338
-
339
- def non_embedded_keys
340
- _keys.values.select { |key| !key.embeddable? }
341
- end
342
-
343
- def embedded_keys
344
- _keys.values.select { |key| key.embeddable? }
345
- end
346
-
347
- def ensure_key_exists(name)
348
- self.metaclass.key(name) unless respond_to?("#{name}=")
349
- end
350
-
351
- def read_attribute(name)
352
- if key = _keys[name]
353
- value = key.get(instance_variable_get("@#{name}"))
354
- instance_variable_set "@#{name}", value
355
- value
356
- else
357
- raise KeyNotFound, "Could not find key: #{name.inspect}"
358
- end
359
- end
360
-
361
- def read_attribute_before_typecast(name)
362
- instance_variable_get("@#{name}_before_typecast")
363
- end
364
-
365
- def write_attribute(name, value)
366
- key = _keys[name]
367
- instance_variable_set "@#{name}_before_typecast", value
368
- instance_variable_set "@#{name}", key.set(value)
369
- end
370
-
371
- def embedded_associations
372
- associations.select do |name, association|
373
- association.embeddable?
374
- end.map do |name, association|
375
- association
376
- end
48
+ if result = _root_document.try(:save!, options)
49
+ @new = false
377
50
  end
51
+ result
52
+ end
378
53
  end # InstanceMethods
379
54
  end # EmbeddedDocument
380
55
  end # MongoMapper
@@ -0,0 +1,31 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ def plugins
4
+ @plugins ||= []
5
+ end
6
+
7
+ def plugin(mod)
8
+ extend mod::ClassMethods if mod.const_defined?(:ClassMethods)
9
+ include mod::InstanceMethods if mod.const_defined?(:InstanceMethods)
10
+ mod.configure(self) if mod.respond_to?(:configure)
11
+ plugins << mod
12
+ end
13
+
14
+ autoload :Callbacks, 'mongo_mapper/plugins/callbacks'
15
+ autoload :Clone, 'mongo_mapper/plugins/clone'
16
+ autoload :Descendants, 'mongo_mapper/plugins/descendants'
17
+ autoload :Dirty, 'mongo_mapper/plugins/dirty'
18
+ autoload :Equality, 'mongo_mapper/plugins/equality'
19
+ autoload :IdentityMap, 'mongo_mapper/plugins/identity_map'
20
+ autoload :Inspect, 'mongo_mapper/plugins/inspect'
21
+ autoload :Keys, 'mongo_mapper/plugins/keys'
22
+ autoload :Logger, 'mongo_mapper/plugins/logger'
23
+ autoload :Protected, 'mongo_mapper/plugins/protected'
24
+ autoload :Rails, 'mongo_mapper/plugins/rails'
25
+ autoload :Serialization, 'mongo_mapper/plugins/serialization'
26
+ autoload :Validations, 'mongo_mapper/plugins/validations'
27
+ end
28
+ end
29
+
30
+ require 'mongo_mapper/plugins/associations'
31
+ require 'mongo_mapper/plugins/pagination'
@@ -0,0 +1,105 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Associations
4
+ module ClassMethods
5
+ def belongs_to(association_id, options={}, &extension)
6
+ create_association(:belongs_to, association_id, options, &extension)
7
+ end
8
+
9
+ def many(association_id, options={}, &extension)
10
+ create_association(:many, association_id, options, &extension)
11
+ end
12
+
13
+ def one(association_id, options={}, &extension)
14
+ create_association(:one, association_id, options, &extension)
15
+ end
16
+
17
+ def associations
18
+ @associations ||= HashWithIndifferentAccess.new
19
+ end
20
+
21
+ def associations=(hash)
22
+ @associations = hash
23
+ end
24
+
25
+ def inherited(subclass)
26
+ subclass.associations = associations.dup
27
+ super
28
+ end
29
+
30
+ private
31
+ def create_association(type, name, options, &extension)
32
+ association = Associations::Base.new(type, name, options, &extension)
33
+ associations[association.name] = association
34
+
35
+ define_method(association.name) do
36
+ get_proxy(association)
37
+ end
38
+
39
+ define_method("#{association.name}=") do |value|
40
+ get_proxy(association).replace(value)
41
+ value
42
+ end
43
+
44
+ if association.one? || association.belongs_to?
45
+ define_method("#{association.name}?") do
46
+ get_proxy(association).present?
47
+ end
48
+ end
49
+
50
+ if association.options[:dependent] && association.many? && !association.embeddable?
51
+ after_destroy do |doc|
52
+ case association.options[:dependent]
53
+ when :destroy
54
+ doc.get_proxy(association).destroy_all
55
+ when :delete_all
56
+ doc.get_proxy(association).delete_all
57
+ when :nullify
58
+ doc.get_proxy(association).nullify
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ module InstanceMethods
66
+ def associations
67
+ self.class.associations
68
+ end
69
+
70
+ # @api private?
71
+ def embedded_associations
72
+ associations.select do |name, association|
73
+ association.embeddable?
74
+ end.map do |name, association|
75
+ association
76
+ end
77
+ end
78
+
79
+ def get_proxy(association)
80
+ unless proxy = self.instance_variable_get(association.ivar)
81
+ proxy = association.proxy_class.new(self, association)
82
+ self.instance_variable_set(association.ivar, proxy)
83
+ end
84
+
85
+ proxy
86
+ end
87
+ end
88
+
89
+ autoload :Base, 'mongo_mapper/plugins/associations/base'
90
+ autoload :Collection, 'mongo_mapper/plugins/associations/collection'
91
+ autoload :EmbeddedCollection, 'mongo_mapper/plugins/associations/embedded_collection'
92
+ autoload :ManyDocumentsProxy, 'mongo_mapper/plugins/associations/many_documents_proxy'
93
+ autoload :BelongsToProxy, 'mongo_mapper/plugins/associations/belongs_to_proxy'
94
+ autoload :BelongsToPolymorphicProxy, 'mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy'
95
+ autoload :ManyPolymorphicProxy, 'mongo_mapper/plugins/associations/many_polymorphic_proxy'
96
+ autoload :ManyEmbeddedProxy, 'mongo_mapper/plugins/associations/many_embedded_proxy'
97
+ autoload :ManyEmbeddedPolymorphicProxy, 'mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy'
98
+ autoload :ManyDocumentsAsProxy, 'mongo_mapper/plugins/associations/many_documents_as_proxy'
99
+ autoload :OneProxy, 'mongo_mapper/plugins/associations/one_proxy'
100
+ autoload :InArrayProxy, 'mongo_mapper/plugins/associations/in_array_proxy'
101
+ end
102
+ end
103
+ end
104
+
105
+ require 'mongo_mapper/plugins/associations/proxy'