djsun-mongomapper 0.3.5.5 → 0.4.1.2

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 (44) hide show
  1. data/README.rdoc +38 -38
  2. data/Rakefile +87 -73
  3. data/VERSION +1 -1
  4. data/lib/mongomapper.rb +67 -71
  5. data/lib/mongomapper/associations.rb +86 -84
  6. data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +34 -34
  7. data/lib/mongomapper/associations/many_embedded_proxy.rb +67 -17
  8. data/lib/mongomapper/associations/proxy.rb +74 -73
  9. data/lib/mongomapper/document.rb +342 -348
  10. data/lib/mongomapper/embedded_document.rb +354 -274
  11. data/lib/mongomapper/finder_options.rb +84 -84
  12. data/lib/mongomapper/key.rb +32 -76
  13. data/lib/mongomapper/rails_compatibility/document.rb +14 -14
  14. data/lib/mongomapper/rails_compatibility/embedded_document.rb +26 -24
  15. data/lib/mongomapper/support.rb +156 -29
  16. data/lib/mongomapper/validations.rb +69 -47
  17. data/test/custom_matchers.rb +48 -0
  18. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +53 -56
  19. data/test/functional/associations/test_belongs_to_proxy.rb +48 -49
  20. data/test/functional/associations/test_many_documents_as_proxy.rb +208 -253
  21. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +130 -130
  22. data/test/functional/associations/test_many_embedded_proxy.rb +168 -106
  23. data/test/functional/associations/test_many_polymorphic_proxy.rb +261 -262
  24. data/test/functional/test_binary.rb +21 -0
  25. data/test/functional/test_document.rb +946 -952
  26. data/test/functional/test_embedded_document.rb +98 -0
  27. data/test/functional/test_pagination.rb +87 -80
  28. data/test/functional/test_rails_compatibility.rb +29 -29
  29. data/test/functional/test_validations.rb +262 -172
  30. data/test/models.rb +169 -169
  31. data/test/test_helper.rb +28 -66
  32. data/test/unit/serializers/test_json_serializer.rb +193 -193
  33. data/test/unit/test_document.rb +161 -123
  34. data/test/unit/test_embedded_document.rb +643 -547
  35. data/test/unit/test_finder_options.rb +183 -183
  36. data/test/unit/test_key.rb +175 -247
  37. data/test/unit/test_rails_compatibility.rb +38 -33
  38. data/test/unit/test_serializations.rb +52 -52
  39. data/test/unit/test_support.rb +268 -0
  40. data/test/unit/test_time_zones.rb +40 -0
  41. data/test/unit/test_validations.rb +499 -258
  42. metadata +22 -12
  43. data/History +0 -76
  44. data/mongomapper.gemspec +0 -145
@@ -1,274 +1,354 @@
1
- require 'observer'
2
-
3
- module MongoMapper
4
- module EmbeddedDocument
5
- def self.included(model)
6
- model.class_eval do
7
- extend ClassMethods
8
- include InstanceMethods
9
-
10
- extend Associations::ClassMethods
11
- include Associations::InstanceMethods
12
-
13
- include RailsCompatibility::EmbeddedDocument
14
- include Validatable
15
- include Serialization
16
-
17
- key :_id, String
18
- end
19
- end
20
-
21
- module ClassMethods
22
- def inherited(subclass)
23
- unless subclass.embeddable?
24
- subclass.collection(self.collection.name)
25
- end
26
-
27
- (@subclasses ||= []) << subclass
28
- end
29
-
30
- def subclasses
31
- @subclasses
32
- end
33
-
34
- def keys
35
- @keys ||= if parent = parent_model
36
- parent.keys.dup
37
- else
38
- HashWithIndifferentAccess.new
39
- end
40
- end
41
-
42
- def key(*args)
43
- key = Key.new(*args)
44
-
45
- if keys[key.name].blank?
46
- keys[key.name] = key
47
-
48
- create_accessors_for(key)
49
- add_to_subclasses(*args)
50
- apply_validations_for(key)
51
- create_indexes_for(key)
52
-
53
- key
54
- end
55
- end
56
-
57
- def add_to_subclasses(*args)
58
- return if subclasses.blank?
59
-
60
- subclasses.each do |subclass|
61
- subclass.key(*args)
62
- end
63
- end
64
-
65
- def ensure_index(name_or_array, options={})
66
- keys_to_index = if name_or_array.is_a?(Array)
67
- name_or_array.map { |pair| [pair[0], pair[1]] }
68
- else
69
- name_or_array
70
- end
71
-
72
- collection.create_index(keys_to_index, options.delete(:unique))
73
- end
74
-
75
- def embeddable?
76
- !self.ancestors.include?(Document)
77
- end
78
-
79
- def parent_model
80
- (ancestors - [self,EmbeddedDocument]).find do |parent_class|
81
- parent_class.ancestors.include?(EmbeddedDocument)
82
- end
83
- end
84
-
85
- private
86
- def accessors_module
87
- if const_defined?('MongoMapperKeys')
88
- const_get 'MongoMapperKeys'
89
- else
90
- const_set 'MongoMapperKeys', Module.new
91
- end
92
- end
93
-
94
- def create_accessors_for(key)
95
- accessors_module.module_eval <<-end_eval
96
- def #{key.name}
97
- read_attribute(:'#{key.name}')
98
- end
99
-
100
- def #{key.name}_before_typecast
101
- read_attribute_before_typecast(:'#{key.name}')
102
- end
103
-
104
- def #{key.name}=(value)
105
- write_attribute(:'#{key.name}', value)
106
- end
107
-
108
- def #{key.name}?
109
- read_attribute(:#{key.name}).present?
110
- end
111
- end_eval
112
- include accessors_module
113
- end
114
-
115
- def create_indexes_for(key)
116
- ensure_index key.name if key.options[:index]
117
- end
118
-
119
- def apply_validations_for(key)
120
- attribute = key.name.to_sym
121
-
122
- if key.options[:required]
123
- validates_presence_of(attribute)
124
- end
125
-
126
- if key.options[:unique]
127
- validates_uniqueness_of(attribute)
128
- end
129
-
130
- if key.options[:numeric]
131
- number_options = key.type == Integer ? {:only_integer => true} : {}
132
- validates_numericality_of(attribute, number_options)
133
- end
134
-
135
- if key.options[:format]
136
- validates_format_of(attribute, :with => key.options[:format])
137
- end
138
-
139
- if key.options[:length]
140
- length_options = case key.options[:length]
141
- when Integer
142
- {:minimum => 0, :maximum => key.options[:length]}
143
- when Range
144
- {:within => key.options[:length]}
145
- when Hash
146
- key.options[:length]
147
- end
148
- validates_length_of(attribute, length_options)
149
- end
150
- end
151
- end
152
-
153
- module InstanceMethods
154
- def initialize(attrs={})
155
- unless attrs.nil?
156
- self.class.associations.each_pair do |name, association|
157
- if collection = attrs.delete(name)
158
- send("#{association.name}=", collection)
159
- end
160
- end
161
-
162
- self.attributes = attrs
163
-
164
- if respond_to?(:_type=) && self['_type'].blank?
165
- self._type = self.class.name
166
- end
167
- end
168
-
169
- if self.class.embeddable? && read_attribute(:_id).blank?
170
- write_attribute :_id, Mongo::ObjectID.new.to_s
171
- end
172
- end
173
-
174
- def attributes=(attrs)
175
- return if attrs.blank?
176
- attrs.each_pair do |name, value|
177
- writer_method = "#{name}="
178
-
179
- if respond_to?(writer_method)
180
- self.send(writer_method, value)
181
- else
182
- self[name.to_s] = value
183
- end
184
- end
185
- end
186
-
187
- def attributes
188
- attrs = HashWithIndifferentAccess.new
189
- self.class.keys.each_pair do |name, key|
190
- value =
191
- if key.native?
192
- read_attribute(key.name)
193
- else
194
- if embedded_document = read_attribute(key.name)
195
- embedded_document.attributes
196
- end
197
- end
198
-
199
- attrs[name] = value
200
- end
201
- attrs.merge!(embedded_association_attributes)
202
- end
203
-
204
- # all attributes, even ones with null values
205
- def all_attributes
206
-
207
- end
208
-
209
- def [](name)
210
- read_attribute(name)
211
- end
212
-
213
- def []=(name, value)
214
- ensure_key_exists(name)
215
- write_attribute(name, value)
216
- end
217
-
218
- def ==(other)
219
- other.is_a?(self.class) && id == other.id
220
- end
221
-
222
- def id
223
- read_attribute(:_id)
224
- end
225
-
226
- def id=(value)
227
- @using_custom_id = true
228
- write_attribute :_id, value
229
- end
230
-
231
- def using_custom_id?
232
- !!@using_custom_id
233
- end
234
-
235
- def inspect
236
- attributes_as_nice_string = self.class.keys.keys.collect do |name|
237
- "#{name}: #{read_attribute(name)}"
238
- end.join(", ")
239
- "#<#{self.class} #{attributes_as_nice_string}>"
240
- end
241
-
242
- private
243
- def ensure_key_exists(name)
244
- self.class.key(name) unless respond_to?("#{name}=")
245
- end
246
-
247
- def read_attribute(name)
248
- value = self.class.keys[name].get(instance_variable_get("@#{name}"))
249
- instance_variable_set "@#{name}", value if !frozen?
250
- value
251
- end
252
-
253
- def read_attribute_before_typecast(name)
254
- instance_variable_get("@#{name}_before_typecast")
255
- end
256
-
257
- def write_attribute(name, value)
258
- instance_variable_set "@#{name}_before_typecast", value
259
- instance_variable_set "@#{name}", self.class.keys[name].set(value)
260
- end
261
-
262
- def embedded_association_attributes
263
- returning HashWithIndifferentAccess.new do |attrs|
264
- self.class.associations.each_pair do |name, association|
265
- next unless association.embeddable?
266
- next unless documents = instance_variable_get(association.ivar)
267
-
268
- attrs[name] = documents.collect { |doc| doc.attributes }
269
- end
270
- end
271
- end
272
- end # InstanceMethods
273
- end # EmbeddedDocument
274
- end # MongoMapper
1
+ require 'observer'
2
+
3
+ module MongoMapper
4
+ module EmbeddedDocument
5
+ def self.included(model)
6
+ model.class_eval do
7
+ extend ClassMethods
8
+ include InstanceMethods
9
+
10
+ extend Associations::ClassMethods
11
+ include Associations::InstanceMethods
12
+
13
+ include RailsCompatibility::EmbeddedDocument
14
+ include Validatable
15
+ include Serialization
16
+
17
+ extend Validations::Macros
18
+
19
+ key :_id, String
20
+ attr_accessor :_root_document
21
+ end
22
+ end
23
+
24
+ module ClassMethods
25
+ def inherited(subclass)
26
+ unless subclass.embeddable?
27
+ subclass.set_collection_name(collection_name)
28
+ end
29
+
30
+ (@subclasses ||= []) << subclass
31
+ end
32
+
33
+ def subclasses
34
+ @subclasses
35
+ end
36
+
37
+ def keys
38
+ @keys ||= if parent = parent_model
39
+ parent.keys.dup
40
+ else
41
+ HashWithIndifferentAccess.new
42
+ end
43
+ end
44
+
45
+ def key(*args)
46
+ key = Key.new(*args)
47
+
48
+ if keys[key.name].blank?
49
+ keys[key.name] = key
50
+
51
+ create_accessors_for(key)
52
+ add_to_subclasses(*args)
53
+ apply_validations_for(key)
54
+ create_indexes_for(key)
55
+
56
+ key
57
+ end
58
+ end
59
+
60
+ def add_to_subclasses(*args)
61
+ return if subclasses.blank?
62
+
63
+ subclasses.each do |subclass|
64
+ subclass.key(*args)
65
+ end
66
+ end
67
+
68
+ def ensure_index(name_or_array, options={})
69
+ keys_to_index = if name_or_array.is_a?(Array)
70
+ name_or_array.map { |pair| [pair[0], pair[1]] }
71
+ else
72
+ name_or_array
73
+ end
74
+
75
+ collection.create_index(keys_to_index, options.delete(:unique))
76
+ end
77
+
78
+ def embeddable?
79
+ !self.ancestors.include?(Document)
80
+ end
81
+
82
+ def parent_model
83
+ (ancestors - [self,EmbeddedDocument]).find do |parent_class|
84
+ parent_class.ancestors.include?(EmbeddedDocument)
85
+ end
86
+ end
87
+
88
+ def to_mongo(instance)
89
+ return nil if instance.nil?
90
+ instance.to_mongo
91
+ end
92
+
93
+ def from_mongo(instance_or_hash)
94
+ return nil if instance_or_hash.nil?
95
+
96
+ if instance_or_hash.is_a?(self)
97
+ instance_or_hash
98
+ else
99
+ new(instance_or_hash)
100
+ end
101
+ end
102
+
103
+ private
104
+ def accessors_module
105
+ if const_defined?('MongoMapperKeys')
106
+ const_get 'MongoMapperKeys'
107
+ else
108
+ const_set 'MongoMapperKeys', Module.new
109
+ end
110
+ end
111
+
112
+ def create_accessors_for(key)
113
+ accessors_module.module_eval <<-end_eval
114
+ def #{key.name}
115
+ read_attribute(:'#{key.name}')
116
+ end
117
+
118
+ def #{key.name}_before_typecast
119
+ read_attribute_before_typecast(:'#{key.name}')
120
+ end
121
+
122
+ def #{key.name}=(value)
123
+ write_attribute(:'#{key.name}', value)
124
+ end
125
+
126
+ def #{key.name}?
127
+ read_attribute(:#{key.name}).present?
128
+ end
129
+ end_eval
130
+ include accessors_module
131
+ end
132
+
133
+ def create_indexes_for(key)
134
+ ensure_index key.name if key.options[:index]
135
+ end
136
+
137
+ def apply_validations_for(key)
138
+ attribute = key.name.to_sym
139
+
140
+ if key.options[:required]
141
+ validates_presence_of(attribute)
142
+ end
143
+
144
+ if key.options[:unique]
145
+ validates_uniqueness_of(attribute)
146
+ end
147
+
148
+ if key.options[:numeric]
149
+ number_options = key.type == Integer ? {:only_integer => true} : {}
150
+ validates_numericality_of(attribute, number_options)
151
+ end
152
+
153
+ if key.options[:format]
154
+ validates_format_of(attribute, :with => key.options[:format])
155
+ end
156
+
157
+ if key.options[:length]
158
+ length_options = case key.options[:length]
159
+ when Integer
160
+ {:minimum => 0, :maximum => key.options[:length]}
161
+ when Range
162
+ {:within => key.options[:length]}
163
+ when Hash
164
+ key.options[:length]
165
+ end
166
+ validates_length_of(attribute, length_options)
167
+ end
168
+ end
169
+ end
170
+
171
+ module InstanceMethods
172
+ def initialize(attrs={})
173
+ unless attrs.nil?
174
+ self.class.associations.each_pair do |name, association|
175
+ if collection = attrs.delete(name)
176
+ if association.many? && association.klass.embeddable?
177
+ root_document = attrs[:_root_document] || self
178
+ collection.each do |doc|
179
+ doc[:_root_document] = root_document
180
+ end
181
+ end
182
+ send("#{association.name}=", collection)
183
+ end
184
+ end
185
+
186
+ self.attributes = attrs
187
+
188
+ if respond_to?(:_type=) && self['_type'].blank?
189
+ self._type = self.class.name
190
+ end
191
+ end
192
+
193
+ if self.class.embeddable?
194
+ if read_attribute(:_id).blank?
195
+ write_attribute :_id, Mongo::ObjectID.new.to_s
196
+ @new_document = true
197
+ else
198
+ @new_document = false
199
+ end
200
+ end
201
+ end
202
+
203
+ def new?
204
+ !!@new_document
205
+ end
206
+
207
+ def attributes=(attrs)
208
+ return if attrs.blank?
209
+ attrs.each_pair do |name, value|
210
+ writer_method = "#{name}="
211
+
212
+ if respond_to?(writer_method)
213
+ self.send(writer_method, value)
214
+ else
215
+ self[name.to_s] = value
216
+ end
217
+ end
218
+ end
219
+
220
+ def attributes
221
+ attrs = HashWithIndifferentAccess.new
222
+
223
+ embedded_keys.each do |key|
224
+ puts key.inspect
225
+ attrs[key.name] = read_attribute(key.name).try(:attributes)
226
+ end
227
+
228
+ non_embedded_keys.each do |key|
229
+ attrs[key.name] = read_attribute(key.name)
230
+ end
231
+
232
+ embedded_associations.each do |association|
233
+ documents = instance_variable_get(association.ivar)
234
+ next if documents.nil?
235
+ attrs[association.name] = documents.collect { |doc| doc.attributes }
236
+ end
237
+
238
+ attrs
239
+ end
240
+
241
+ def to_mongo
242
+ attrs = HashWithIndifferentAccess.new
243
+
244
+ _keys.each_pair do |name, key|
245
+ value = key.set(read_attribute(key.name))
246
+ attrs[name] = value
247
+ end
248
+
249
+ embedded_associations.each do |association|
250
+ if documents = instance_variable_get(association.ivar)
251
+ attrs[association.name] = documents.map { |document| document.to_mongo }
252
+ end
253
+ end
254
+
255
+ attrs
256
+ end
257
+
258
+ def clone
259
+ clone_attributes = self.attributes
260
+ clone_attributes.delete("_id")
261
+ self.class.new(clone_attributes)
262
+ end
263
+
264
+ def [](name)
265
+ read_attribute(name)
266
+ end
267
+
268
+ def []=(name, value)
269
+ ensure_key_exists(name)
270
+ write_attribute(name, value)
271
+ end
272
+
273
+ def ==(other)
274
+ other.is_a?(self.class) && id == other.id
275
+ end
276
+
277
+ def id
278
+ read_attribute(:_id)
279
+ end
280
+
281
+ def id=(value)
282
+ @using_custom_id = true
283
+ write_attribute :_id, value
284
+ end
285
+
286
+ def using_custom_id?
287
+ !!@using_custom_id
288
+ end
289
+
290
+ def inspect
291
+ attributes_as_nice_string = key_names.collect do |name|
292
+ "#{name}: #{read_attribute(name).inspect}"
293
+ end.join(", ")
294
+ "#<#{self.class} #{attributes_as_nice_string}>"
295
+ end
296
+
297
+ def save
298
+ if _root_document
299
+ _root_document.save
300
+ end
301
+ end
302
+
303
+ def update_attributes(attrs={})
304
+ self.attributes = attrs
305
+ save
306
+ end
307
+
308
+ private
309
+ def _keys
310
+ self.class.keys
311
+ end
312
+
313
+ def key_names
314
+ _keys.keys
315
+ end
316
+
317
+ def non_embedded_keys
318
+ _keys.values.select { |key| !key.embeddable? }
319
+ end
320
+
321
+ def embedded_keys
322
+ _keys.values.select { |key| key.embeddable? }
323
+ end
324
+
325
+ def ensure_key_exists(name)
326
+ self.class.key(name) unless respond_to?("#{name}=")
327
+ end
328
+
329
+ def read_attribute(name)
330
+ value = _keys[name].get(instance_variable_get("@#{name}"))
331
+ instance_variable_set "@#{name}", value if !frozen?
332
+ value
333
+ end
334
+
335
+ def read_attribute_before_typecast(name)
336
+ instance_variable_get("@#{name}_before_typecast")
337
+ end
338
+
339
+ def write_attribute(name, value)
340
+ key = _keys[name]
341
+ instance_variable_set "@#{name}_before_typecast", value
342
+ instance_variable_set "@#{name}", key.set(value)
343
+ end
344
+
345
+ def embedded_associations
346
+ self.class.associations.select do |name, association|
347
+ association.embeddable?
348
+ end.map do |name, association|
349
+ association
350
+ end
351
+ end
352
+ end # InstanceMethods
353
+ end # EmbeddedDocument
354
+ end # MongoMapper