novelys_mongo_mapper 0.6.10

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