mrkurt-mongo_mapper 0.6.8

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 (77) hide show
  1. data/.gitignore +10 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +38 -0
  4. data/Rakefile +55 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +60 -0
  7. data/lib/mongo_mapper.rb +139 -0
  8. data/lib/mongo_mapper/associations.rb +72 -0
  9. data/lib/mongo_mapper/associations/base.rb +113 -0
  10. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +26 -0
  11. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +21 -0
  12. data/lib/mongo_mapper/associations/collection.rb +19 -0
  13. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +26 -0
  14. data/lib/mongo_mapper/associations/many_documents_proxy.rb +115 -0
  15. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +31 -0
  16. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +54 -0
  17. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
  18. data/lib/mongo_mapper/associations/one_proxy.rb +61 -0
  19. data/lib/mongo_mapper/associations/proxy.rb +111 -0
  20. data/lib/mongo_mapper/callbacks.rb +61 -0
  21. data/lib/mongo_mapper/dirty.rb +117 -0
  22. data/lib/mongo_mapper/document.rb +496 -0
  23. data/lib/mongo_mapper/dynamic_finder.rb +74 -0
  24. data/lib/mongo_mapper/embedded_document.rb +380 -0
  25. data/lib/mongo_mapper/finder_options.rb +145 -0
  26. data/lib/mongo_mapper/key.rb +36 -0
  27. data/lib/mongo_mapper/mongo_mapper.rb +125 -0
  28. data/lib/mongo_mapper/pagination.rb +66 -0
  29. data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
  30. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +28 -0
  31. data/lib/mongo_mapper/serialization.rb +54 -0
  32. data/lib/mongo_mapper/serializers/json_serializer.rb +48 -0
  33. data/lib/mongo_mapper/support.rb +192 -0
  34. data/lib/mongo_mapper/validations.rb +39 -0
  35. data/mongo_mapper.gemspec +173 -0
  36. data/specs.watchr +30 -0
  37. data/test/NOTE_ON_TESTING +1 -0
  38. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
  39. data/test/functional/associations/test_belongs_to_proxy.rb +91 -0
  40. data/test/functional/associations/test_many_documents_as_proxy.rb +246 -0
  41. data/test/functional/associations/test_many_documents_proxy.rb +477 -0
  42. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +156 -0
  43. data/test/functional/associations/test_many_embedded_proxy.rb +192 -0
  44. data/test/functional/associations/test_many_polymorphic_proxy.rb +339 -0
  45. data/test/functional/associations/test_one_proxy.rb +131 -0
  46. data/test/functional/test_associations.rb +44 -0
  47. data/test/functional/test_binary.rb +33 -0
  48. data/test/functional/test_callbacks.rb +85 -0
  49. data/test/functional/test_dirty.rb +159 -0
  50. data/test/functional/test_document.rb +1198 -0
  51. data/test/functional/test_embedded_document.rb +135 -0
  52. data/test/functional/test_logger.rb +20 -0
  53. data/test/functional/test_modifiers.rb +242 -0
  54. data/test/functional/test_pagination.rb +95 -0
  55. data/test/functional/test_rails_compatibility.rb +25 -0
  56. data/test/functional/test_string_id_compatibility.rb +72 -0
  57. data/test/functional/test_validations.rb +361 -0
  58. data/test/models.rb +271 -0
  59. data/test/support/custom_matchers.rb +55 -0
  60. data/test/support/timing.rb +16 -0
  61. data/test/test_helper.rb +27 -0
  62. data/test/unit/associations/test_base.rb +182 -0
  63. data/test/unit/associations/test_proxy.rb +91 -0
  64. data/test/unit/serializers/test_json_serializer.rb +189 -0
  65. data/test/unit/test_document.rb +236 -0
  66. data/test/unit/test_dynamic_finder.rb +125 -0
  67. data/test/unit/test_embedded_document.rb +709 -0
  68. data/test/unit/test_finder_options.rb +325 -0
  69. data/test/unit/test_key.rb +172 -0
  70. data/test/unit/test_mongo_mapper.rb +65 -0
  71. data/test/unit/test_pagination.rb +119 -0
  72. data/test/unit/test_rails_compatibility.rb +52 -0
  73. data/test/unit/test_serializations.rb +52 -0
  74. data/test/unit/test_support.rb +346 -0
  75. data/test/unit/test_time_zones.rb +40 -0
  76. data/test/unit/test_validations.rb +503 -0
  77. metadata +239 -0
@@ -0,0 +1,74 @@
1
+ module MongoMapper
2
+ # @api private
3
+ module Finders
4
+ def dynamic_find(finder, args)
5
+ attributes = {}
6
+ finder.attributes.each_with_index do |attr, index|
7
+ attributes[attr] = args[index]
8
+ end
9
+
10
+ options = args.extract_options!.merge(attributes)
11
+
12
+ if result = find(finder.finder, options)
13
+ result
14
+ else
15
+ if finder.raise?
16
+ raise DocumentNotFound, "Couldn't find Document with #{attributes.inspect} in collection named #{collection.name}"
17
+ end
18
+
19
+ if finder.instantiator
20
+ self.send(finder.instantiator, attributes)
21
+ end
22
+ end
23
+ end
24
+
25
+ protected
26
+ def method_missing(method, *args, &block)
27
+ finder = DynamicFinder.new(method)
28
+
29
+ if finder.found?
30
+ dynamic_find(finder, args)
31
+ else
32
+ super
33
+ end
34
+ end
35
+ end
36
+
37
+ class DynamicFinder
38
+ attr_reader :method, :attributes, :finder, :bang, :instantiator
39
+
40
+ def initialize(method)
41
+ @method = method
42
+ @finder = :first
43
+ @bang = false
44
+ match
45
+ end
46
+
47
+ def found?
48
+ @finder.present?
49
+ end
50
+
51
+ def raise?
52
+ bang == true
53
+ end
54
+
55
+ protected
56
+ def match
57
+ case method.to_s
58
+ when /^find_(all_by|by)_([_a-zA-Z]\w*)$/
59
+ @finder = :all if $1 == 'all_by'
60
+ names = $2
61
+ when /^find_by_([_a-zA-Z]\w*)\!$/
62
+ @bang = true
63
+ names = $1
64
+ when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
65
+ @instantiator = $1 == 'initialize' ? :new : :create
66
+ names = $2
67
+ else
68
+ @finder = nil
69
+ end
70
+
71
+ @attributes = names && names.split('_and_')
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,380 @@
1
+ module MongoMapper
2
+ module EmbeddedDocument
3
+ def self.included(model)
4
+ model.class_eval do
5
+ extend ClassMethods
6
+ include InstanceMethods
7
+
8
+ extend Associations::ClassMethods
9
+ include Associations::InstanceMethods
10
+
11
+ include RailsCompatibility::EmbeddedDocument
12
+ include Validatable
13
+ include Serialization
14
+
15
+ extend Validations::Macros
16
+
17
+ key :_id, ObjectId
18
+ attr_accessor :_root_document
19
+ end
20
+ end
21
+
22
+ 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
+ def embeddable?
68
+ !self.ancestors.include?(Document)
69
+ end
70
+
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
171
+ end
172
+ end
173
+
174
+ 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
292
+ end
293
+
294
+ self[:_id] = value
295
+ end
296
+
297
+ def using_custom_id?
298
+ !!@using_custom_id
299
+ end
300
+
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
+ 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
377
+ end
378
+ end # InstanceMethods
379
+ end # EmbeddedDocument
380
+ end # MongoMapper