mrkurt-mongo_mapper 0.6.8

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