mongo_mapper 0.6.10 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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'