drogus-mongo_mapper 0.6.10

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 (91) hide show
  1. data/.gitignore +10 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +29 -0
  4. data/Rakefile +55 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +60 -0
  7. data/lib/mongo_mapper.rb +131 -0
  8. data/lib/mongo_mapper/document.rb +417 -0
  9. data/lib/mongo_mapper/embedded_document.rb +55 -0
  10. data/lib/mongo_mapper/finder_options.rb +127 -0
  11. data/lib/mongo_mapper/plugins.rb +30 -0
  12. data/lib/mongo_mapper/plugins/associations.rb +104 -0
  13. data/lib/mongo_mapper/plugins/associations/base.rb +121 -0
  14. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +30 -0
  15. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +25 -0
  16. data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
  17. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +50 -0
  18. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +139 -0
  19. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  20. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +117 -0
  21. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
  22. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
  23. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
  24. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  25. data/lib/mongo_mapper/plugins/associations/proxy.rb +118 -0
  26. data/lib/mongo_mapper/plugins/callbacks.rb +134 -0
  27. data/lib/mongo_mapper/plugins/clone.rb +13 -0
  28. data/lib/mongo_mapper/plugins/descendants.rb +16 -0
  29. data/lib/mongo_mapper/plugins/dirty.rb +119 -0
  30. data/lib/mongo_mapper/plugins/equality.rb +23 -0
  31. data/lib/mongo_mapper/plugins/identity_map.rb +122 -0
  32. data/lib/mongo_mapper/plugins/inspect.rb +14 -0
  33. data/lib/mongo_mapper/plugins/keys.rb +324 -0
  34. data/lib/mongo_mapper/plugins/logger.rb +17 -0
  35. data/lib/mongo_mapper/plugins/pagination.rb +85 -0
  36. data/lib/mongo_mapper/plugins/protected.rb +45 -0
  37. data/lib/mongo_mapper/plugins/rails.rb +45 -0
  38. data/lib/mongo_mapper/plugins/serialization.rb +105 -0
  39. data/lib/mongo_mapper/plugins/validations.rb +57 -0
  40. data/lib/mongo_mapper/support.rb +217 -0
  41. data/lib/mongo_mapper/support/descendant_appends.rb +46 -0
  42. data/lib/mongo_mapper/support/find.rb +77 -0
  43. data/mongo_mapper.gemspec +195 -0
  44. data/performance/read_write.rb +52 -0
  45. data/specs.watchr +51 -0
  46. data/test/NOTE_ON_TESTING +1 -0
  47. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
  48. data/test/functional/associations/test_belongs_to_proxy.rb +101 -0
  49. data/test/functional/associations/test_in_array_proxy.rb +309 -0
  50. data/test/functional/associations/test_many_documents_as_proxy.rb +229 -0
  51. data/test/functional/associations/test_many_documents_proxy.rb +431 -0
  52. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +176 -0
  53. data/test/functional/associations/test_many_embedded_proxy.rb +256 -0
  54. data/test/functional/associations/test_many_polymorphic_proxy.rb +302 -0
  55. data/test/functional/associations/test_one_proxy.rb +161 -0
  56. data/test/functional/test_associations.rb +44 -0
  57. data/test/functional/test_binary.rb +27 -0
  58. data/test/functional/test_callbacks.rb +81 -0
  59. data/test/functional/test_dirty.rb +163 -0
  60. data/test/functional/test_document.rb +1264 -0
  61. data/test/functional/test_embedded_document.rb +125 -0
  62. data/test/functional/test_identity_map.rb +508 -0
  63. data/test/functional/test_logger.rb +20 -0
  64. data/test/functional/test_modifiers.rb +252 -0
  65. data/test/functional/test_pagination.rb +93 -0
  66. data/test/functional/test_protected.rb +155 -0
  67. data/test/functional/test_string_id_compatibility.rb +67 -0
  68. data/test/functional/test_validations.rb +329 -0
  69. data/test/models.rb +232 -0
  70. data/test/support/custom_matchers.rb +55 -0
  71. data/test/support/timing.rb +16 -0
  72. data/test/test_helper.rb +60 -0
  73. data/test/unit/associations/test_base.rb +207 -0
  74. data/test/unit/associations/test_proxy.rb +105 -0
  75. data/test/unit/serializers/test_json_serializer.rb +189 -0
  76. data/test/unit/test_descendant_appends.rb +71 -0
  77. data/test/unit/test_document.rb +231 -0
  78. data/test/unit/test_dynamic_finder.rb +123 -0
  79. data/test/unit/test_embedded_document.rb +663 -0
  80. data/test/unit/test_finder_options.rb +329 -0
  81. data/test/unit/test_keys.rb +169 -0
  82. data/test/unit/test_mongo_mapper.rb +65 -0
  83. data/test/unit/test_pagination.rb +127 -0
  84. data/test/unit/test_plugins.rb +50 -0
  85. data/test/unit/test_rails.rb +123 -0
  86. data/test/unit/test_rails_compatibility.rb +52 -0
  87. data/test/unit/test_serialization.rb +51 -0
  88. data/test/unit/test_support.rb +354 -0
  89. data/test/unit/test_time_zones.rb +39 -0
  90. data/test/unit/test_validations.rb +544 -0
  91. metadata +290 -0
@@ -0,0 +1,23 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Equality
4
+ module InstanceMethods
5
+ def ==(other)
6
+ other.is_a?(self.class) && _id == other._id
7
+ end
8
+
9
+ def eql?(other)
10
+ self == other
11
+ end
12
+
13
+ def equal?(other)
14
+ object_id === other.object_id
15
+ end
16
+
17
+ def hash
18
+ _id.hash
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,122 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module IdentityMap
4
+ def self.models
5
+ @models ||= Set.new
6
+ end
7
+
8
+ def self.clear
9
+ models.each { |m| m.identity_map.clear }
10
+ end
11
+
12
+ def self.configure(model)
13
+ IdentityMap.models << model
14
+ end
15
+
16
+ module ClassMethods
17
+ def inherited(descendant)
18
+ descendant.identity_map = identity_map
19
+ super
20
+ end
21
+
22
+ def identity_map
23
+ @identity_map ||= {}
24
+ end
25
+
26
+ def identity_map=(v)
27
+ @identity_map = v
28
+ end
29
+
30
+ def find_one(options={})
31
+ criteria, finder_options = to_finder_options(options)
32
+
33
+ if simple_find?(criteria) && identity_map.key?(criteria[:_id])
34
+ identity_map[criteria[:_id]]
35
+ else
36
+ super.tap do |document|
37
+ remove_documents_from_map(document) if selecting_fields?(finder_options)
38
+ end
39
+ end
40
+ end
41
+
42
+ def find_many(options)
43
+ criteria, finder_options = to_finder_options(options)
44
+ super.tap do |documents|
45
+ remove_documents_from_map(documents) if selecting_fields?(finder_options)
46
+ end
47
+ end
48
+
49
+ def load(attrs)
50
+ document = identity_map[attrs['_id']]
51
+
52
+ if document.nil? || identity_map_off?
53
+ document = super
54
+ identity_map[document._id] = document if identity_map_on?
55
+ end
56
+
57
+ document
58
+ end
59
+
60
+ def identity_map_status
61
+ defined?(@identity_map_status) ? @identity_map_status : true
62
+ end
63
+
64
+ def identity_map_on
65
+ @identity_map_status = true
66
+ end
67
+
68
+ def identity_map_off
69
+ @identity_map_status = false
70
+ end
71
+
72
+ def identity_map_on?
73
+ identity_map_status
74
+ end
75
+
76
+ def identity_map_off?
77
+ !identity_map_on?
78
+ end
79
+
80
+ def without_identity_map(&block)
81
+ identity_map_off
82
+ yield
83
+ ensure
84
+ identity_map_on
85
+ end
86
+
87
+ private
88
+ def remove_documents_from_map(*documents)
89
+ documents.flatten.compact.each do |document|
90
+ identity_map.delete(document._id)
91
+ end
92
+ end
93
+
94
+ def simple_find?(criteria)
95
+ criteria.keys == [:_id] || criteria.keys.to_set == [:_id, :_type].to_set
96
+ end
97
+
98
+ def selecting_fields?(options)
99
+ !options[:fields].nil?
100
+ end
101
+ end
102
+
103
+ module InstanceMethods
104
+ def identity_map
105
+ self.class.identity_map
106
+ end
107
+
108
+ def save(*args)
109
+ if result = super
110
+ identity_map[_id] = self if self.class.identity_map_on?
111
+ end
112
+ result
113
+ end
114
+
115
+ def delete
116
+ identity_map.delete(_id) if self.class.identity_map_on?
117
+ super
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,14 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Inspect
4
+ module InstanceMethods
5
+ def inspect
6
+ attributes_as_nice_string = key_names.collect do |name|
7
+ "#{name}: #{self[name].inspect}"
8
+ end.join(", ")
9
+ "#<#{self.class} #{attributes_as_nice_string}>"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,324 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Keys
4
+ def self.configure(model)
5
+ model.key :_id, ObjectId
6
+ end
7
+
8
+ module ClassMethods
9
+ def inherited(descendant)
10
+ descendant.instance_variable_set(:@keys, keys.dup)
11
+ super
12
+ end
13
+
14
+ def keys
15
+ @keys ||= defined?(HashWithIndifferentAccess) ? HashWithIndifferentAccess.new : ActiveSupport::HashWithIndifferentAccess.new
16
+ end
17
+
18
+ def key(*args)
19
+ key = Key.new(*args)
20
+ keys[key.name] = key
21
+
22
+ create_accessors_for(key)
23
+ create_key_in_descendants(*args)
24
+ create_indexes_for(key)
25
+ create_validations_for(key)
26
+
27
+ key
28
+ end
29
+
30
+ def using_object_id?
31
+ object_id_key?(:_id)
32
+ end
33
+
34
+ def object_id_key?(name)
35
+ key = keys[name.to_s]
36
+ key && key.type == ObjectId
37
+ end
38
+
39
+ def to_mongo(instance)
40
+ return nil if instance.nil?
41
+ instance.to_mongo
42
+ end
43
+
44
+ def from_mongo(value)
45
+ return nil if value.nil?
46
+ value.is_a?(self) ? value : load(value)
47
+ end
48
+
49
+ # load is overridden in identity map to ensure same objects are loaded
50
+ def load(attrs)
51
+ begin
52
+ klass = attrs['_type'].present? ? attrs['_type'].constantize : self
53
+ klass.new(attrs, true)
54
+ rescue NameError
55
+ new(attrs, true)
56
+ end
57
+ end
58
+
59
+ private
60
+ def accessors_module
61
+ module_defined = if method(:const_defined?).arity == 1 # Ruby 1.9 compat check
62
+ const_defined?('MongoMapperKeys')
63
+ else
64
+ const_defined?('MongoMapperKeys', false)
65
+ end
66
+
67
+ if module_defined
68
+ const_get 'MongoMapperKeys'
69
+ else
70
+ const_set 'MongoMapperKeys', Module.new
71
+ end
72
+ end
73
+
74
+ def create_accessors_for(key)
75
+ accessors_module.module_eval <<-end_eval
76
+ def #{key.name}
77
+ read_key(:#{key.name})
78
+ end
79
+
80
+ def #{key.name}_before_typecast
81
+ read_key_before_typecast(:#{key.name})
82
+ end
83
+
84
+ def #{key.name}=(value)
85
+ write_key(:#{key.name}, value)
86
+ end
87
+
88
+ def #{key.name}?
89
+ read_key(:#{key.name}).present?
90
+ end
91
+ end_eval
92
+
93
+ include accessors_module
94
+ end
95
+
96
+ def create_key_in_descendants(*args)
97
+ return if descendants.blank?
98
+ descendants.each { |descendant| descendant.key(*args) }
99
+ end
100
+
101
+ def create_indexes_for(key)
102
+ ensure_index key.name if key.options[:index] && !key.embeddable?
103
+ end
104
+
105
+ def create_validations_for(key)
106
+ attribute = key.name.to_sym
107
+
108
+ if key.options[:required]
109
+ validates_presence_of(attribute)
110
+ end
111
+
112
+ if key.options[:unique]
113
+ validates_uniqueness_of(attribute)
114
+ end
115
+
116
+ if key.options[:numeric]
117
+ number_options = key.type == Integer ? {:only_integer => true} : {}
118
+ validates_numericality_of(attribute, number_options)
119
+ end
120
+
121
+ if key.options[:format]
122
+ validates_format_of(attribute, :with => key.options[:format])
123
+ end
124
+
125
+ if key.options[:in]
126
+ validates_inclusion_of(attribute, :within => key.options[:in])
127
+ end
128
+
129
+ if key.options[:not_in]
130
+ validates_exclusion_of(attribute, :within => key.options[:not_in])
131
+ end
132
+
133
+ if key.options[:length]
134
+ length_options = case key.options[:length]
135
+ when Integer
136
+ {:minimum => 0, :maximum => key.options[:length]}
137
+ when Range
138
+ {:within => key.options[:length]}
139
+ when Hash
140
+ key.options[:length]
141
+ end
142
+ validates_length_of(attribute, length_options)
143
+ end
144
+ end
145
+ end
146
+
147
+ module InstanceMethods
148
+ def initialize(attrs={}, from_database=false)
149
+ unless attrs.nil?
150
+ provided_keys = attrs.keys.map { |k| k.to_s }
151
+ unless provided_keys.include?('_id') || provided_keys.include?('id')
152
+ write_key :_id, Mongo::ObjectID.new
153
+ end
154
+ end
155
+
156
+ assign_type_if_present
157
+
158
+ if from_database
159
+ @new = false
160
+ self.attributes = attrs
161
+ else
162
+ @new = true
163
+ assign(attrs)
164
+ end
165
+ end
166
+
167
+ def new?
168
+ @new
169
+ end
170
+
171
+ def attributes=(attrs)
172
+ return if attrs.blank?
173
+
174
+ attrs.each_pair do |name, value|
175
+ writer_method = "#{name}="
176
+
177
+ if respond_to?(writer_method)
178
+ self.send(writer_method, value)
179
+ else
180
+ self[name.to_s] = value
181
+ end
182
+ end
183
+ end
184
+
185
+ def attributes
186
+ attrs = defined?(HashWithIndifferentAccess) ? HashWithIndifferentAccess.new : ActiveSupport::HashWithIndifferentAccess.new
187
+
188
+ keys.each_pair do |name, key|
189
+ value = key.set(self[key.name])
190
+ attrs[name] = value
191
+ end
192
+
193
+ embedded_associations.each do |association|
194
+ if documents = instance_variable_get(association.ivar)
195
+ attrs[association.name] = documents.map { |document| document.to_mongo }
196
+ end
197
+ end
198
+
199
+ attrs
200
+ end
201
+ alias :to_mongo :attributes
202
+
203
+ def assign(attrs={})
204
+ self.attributes = attrs
205
+ end
206
+
207
+ def update_attributes(attrs={})
208
+ assign(attrs)
209
+ save
210
+ end
211
+
212
+ def update_attributes!(attrs={})
213
+ assign(attrs)
214
+ save!
215
+ end
216
+
217
+ def id
218
+ _id
219
+ end
220
+
221
+ def id=(value)
222
+ if self.class.using_object_id?
223
+ value = MongoMapper.normalize_object_id(value)
224
+ end
225
+
226
+ self[:_id] = value
227
+ end
228
+
229
+ def [](name)
230
+ read_key(name)
231
+ end
232
+
233
+ def []=(name, value)
234
+ ensure_key_exists(name)
235
+ write_key(name, value)
236
+ end
237
+
238
+ # @api public
239
+ def keys
240
+ self.class.keys
241
+ end
242
+
243
+ # @api private?
244
+ def key_names
245
+ keys.keys
246
+ end
247
+
248
+ # @api private?
249
+ def non_embedded_keys
250
+ keys.values.select { |key| !key.embeddable? }
251
+ end
252
+
253
+ # @api private?
254
+ def embedded_keys
255
+ keys.values.select { |key| key.embeddable? }
256
+ end
257
+
258
+ private
259
+ def assign_type_if_present
260
+ self._type = self.class.name if respond_to?(:_type=)
261
+ end
262
+
263
+ def ensure_key_exists(name)
264
+ self.class.key(name) unless respond_to?("#{name}=")
265
+ end
266
+
267
+ def read_key(name)
268
+ if key = keys[name]
269
+ var_name = "@#{name}"
270
+ value = key.get(instance_variable_get(var_name))
271
+ instance_variable_set(var_name, value)
272
+ else
273
+ raise KeyNotFound, "Could not find key: #{name.inspect}"
274
+ end
275
+ end
276
+
277
+ def read_key_before_typecast(name)
278
+ instance_variable_get("@#{name}_before_typecast")
279
+ end
280
+
281
+ def write_key(name, value)
282
+ key = keys[name]
283
+ instance_variable_set "@#{name}_before_typecast", value
284
+ instance_variable_set "@#{name}", key.set(value)
285
+ end
286
+ end
287
+
288
+ class Key
289
+ attr_accessor :name, :type, :options, :default_value
290
+
291
+ def initialize(*args)
292
+ options = args.extract_options!
293
+ @name, @type = args.shift.to_s, args.shift
294
+ self.options = (options || {}).symbolize_keys
295
+ self.default_value = self.options.delete(:default)
296
+ end
297
+
298
+ def ==(other)
299
+ @name == other.name && @type == other.type
300
+ end
301
+
302
+ def embeddable?
303
+ type.respond_to?(:embeddable?) && type.embeddable? ? true : false
304
+ end
305
+
306
+ def number?
307
+ [Integer, Float].include?(type)
308
+ end
309
+
310
+ def get(value)
311
+ if value.nil? && !default_value.nil?
312
+ return default_value
313
+ end
314
+
315
+ type.from_mongo(value)
316
+ end
317
+
318
+ def set(value)
319
+ type.to_mongo(value)
320
+ end
321
+ end
322
+ end
323
+ end
324
+ end