novelys_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 +38 -0
  4. data/Rakefile +54 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +60 -0
  7. data/lib/mongo_mapper.rb +124 -0
  8. data/lib/mongo_mapper/descendant_appends.rb +44 -0
  9. data/lib/mongo_mapper/document.rb +423 -0
  10. data/lib/mongo_mapper/dynamic_finder.rb +74 -0
  11. data/lib/mongo_mapper/embedded_document.rb +67 -0
  12. data/lib/mongo_mapper/finder_options.rb +127 -0
  13. data/lib/mongo_mapper/plugins.rb +14 -0
  14. data/lib/mongo_mapper/plugins/associations.rb +104 -0
  15. data/lib/mongo_mapper/plugins/associations/base.rb +121 -0
  16. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +30 -0
  17. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +25 -0
  18. data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
  19. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +50 -0
  20. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +139 -0
  21. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  22. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +117 -0
  23. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
  24. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
  25. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
  26. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  27. data/lib/mongo_mapper/plugins/associations/proxy.rb +118 -0
  28. data/lib/mongo_mapper/plugins/callbacks.rb +76 -0
  29. data/lib/mongo_mapper/plugins/clone.rb +13 -0
  30. data/lib/mongo_mapper/plugins/descendants.rb +16 -0
  31. data/lib/mongo_mapper/plugins/dirty.rb +119 -0
  32. data/lib/mongo_mapper/plugins/equality.rb +23 -0
  33. data/lib/mongo_mapper/plugins/identity_map.rb +122 -0
  34. data/lib/mongo_mapper/plugins/inspect.rb +14 -0
  35. data/lib/mongo_mapper/plugins/keys.rb +300 -0
  36. data/lib/mongo_mapper/plugins/logger.rb +17 -0
  37. data/lib/mongo_mapper/plugins/pagination.rb +85 -0
  38. data/lib/mongo_mapper/plugins/protected.rb +41 -0
  39. data/lib/mongo_mapper/plugins/rails.rb +45 -0
  40. data/lib/mongo_mapper/plugins/serialization.rb +105 -0
  41. data/lib/mongo_mapper/plugins/validations.rb +62 -0
  42. data/lib/mongo_mapper/support.rb +214 -0
  43. data/mongo_mapper.gemspec +179 -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 +1245 -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 +139 -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 +55 -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 +125 -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 +248 -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,300 @@
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 ||= 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_db=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
+ @new = from_db ? false : true
157
+ self._type = self.class.name if respond_to?(:_type=)
158
+ self.attributes = attrs
159
+ end
160
+
161
+ def new?
162
+ @new
163
+ end
164
+
165
+ def attributes=(attrs)
166
+ return if attrs.blank?
167
+
168
+ attrs.each_pair do |name, value|
169
+ writer_method = "#{name}="
170
+
171
+ if respond_to?(writer_method)
172
+ self.send(writer_method, value)
173
+ else
174
+ self[name.to_s] = value
175
+ end
176
+ end
177
+ end
178
+
179
+ def attributes
180
+ attrs = HashWithIndifferentAccess.new
181
+
182
+ keys.each_pair do |name, key|
183
+ value = key.set(self[key.name])
184
+ attrs[name] = value
185
+ end
186
+
187
+ embedded_associations.each do |association|
188
+ if documents = instance_variable_get(association.ivar)
189
+ attrs[association.name] = documents.map { |document| document.to_mongo }
190
+ end
191
+ end
192
+
193
+ attrs
194
+ end
195
+ alias :to_mongo :attributes
196
+
197
+ def id
198
+ _id
199
+ end
200
+
201
+ def id=(value)
202
+ if self.class.using_object_id?
203
+ value = MongoMapper.normalize_object_id(value)
204
+ end
205
+
206
+ self[:_id] = value
207
+ end
208
+
209
+ def [](name)
210
+ read_key(name)
211
+ end
212
+
213
+ def []=(name, value)
214
+ ensure_key_exists(name)
215
+ write_key(name, value)
216
+ end
217
+
218
+ # @api public
219
+ def keys
220
+ self.class.keys
221
+ end
222
+
223
+ # @api private?
224
+ def key_names
225
+ keys.keys
226
+ end
227
+
228
+ # @api private?
229
+ def non_embedded_keys
230
+ keys.values.select { |key| !key.embeddable? }
231
+ end
232
+
233
+ # @api private?
234
+ def embedded_keys
235
+ keys.values.select { |key| key.embeddable? }
236
+ end
237
+
238
+ private
239
+ def ensure_key_exists(name)
240
+ self.class.key(name) unless respond_to?("#{name}=")
241
+ end
242
+
243
+ def read_key(name)
244
+ if key = keys[name]
245
+ var_name = "@#{name}"
246
+ value = key.get(instance_variable_get(var_name))
247
+ instance_variable_set(var_name, value)
248
+ else
249
+ raise KeyNotFound, "Could not find key: #{name.inspect}"
250
+ end
251
+ end
252
+
253
+ def read_key_before_typecast(name)
254
+ instance_variable_get("@#{name}_before_typecast")
255
+ end
256
+
257
+ def write_key(name, value)
258
+ key = keys[name]
259
+ instance_variable_set "@#{name}_before_typecast", value
260
+ instance_variable_set "@#{name}", key.set(value)
261
+ end
262
+ end
263
+
264
+ class Key
265
+ attr_accessor :name, :type, :options, :default_value
266
+
267
+ def initialize(*args)
268
+ options = args.extract_options!
269
+ @name, @type = args.shift.to_s, args.shift
270
+ self.options = (options || {}).symbolize_keys
271
+ self.default_value = self.options.delete(:default)
272
+ end
273
+
274
+ def ==(other)
275
+ @name == other.name && @type == other.type
276
+ end
277
+
278
+ def embeddable?
279
+ type.respond_to?(:embeddable?) && type.embeddable? ? true : false
280
+ end
281
+
282
+ def number?
283
+ [Integer, Float].include?(type)
284
+ end
285
+
286
+ def get(value)
287
+ if value.nil? && !default_value.nil?
288
+ return default_value
289
+ end
290
+
291
+ type.from_mongo(value)
292
+ end
293
+
294
+ def set(value)
295
+ type.to_mongo(value)
296
+ end
297
+ end
298
+ end
299
+ end
300
+ end