mongo_mapper_ign 0.7.4

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