mongo_mapper 0.7.5 → 0.7.6

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 (76) hide show
  1. data/lib/mongo_mapper.rb +3 -5
  2. data/lib/mongo_mapper/document.rb +23 -53
  3. data/lib/mongo_mapper/plugins/associations.rb +1 -1
  4. data/lib/mongo_mapper/plugins/associations/base.rb +4 -4
  5. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +1 -1
  6. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +1 -1
  7. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +1 -1
  8. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +1 -1
  9. data/lib/mongo_mapper/plugins/equality.rb +3 -3
  10. data/lib/mongo_mapper/plugins/identity_map.rb +8 -7
  11. data/lib/mongo_mapper/plugins/keys.rb +49 -73
  12. data/lib/mongo_mapper/plugins/keys/key.rb +44 -0
  13. data/lib/mongo_mapper/plugins/modifiers.rb +9 -5
  14. data/lib/mongo_mapper/plugins/pagination/proxy.rb +3 -3
  15. data/lib/mongo_mapper/plugins/serialization.rb +3 -3
  16. data/lib/mongo_mapper/plugins/timestamps.rb +1 -1
  17. data/lib/mongo_mapper/plugins/validations.rb +2 -2
  18. data/lib/mongo_mapper/query.rb +9 -129
  19. data/lib/mongo_mapper/support.rb +17 -39
  20. data/lib/mongo_mapper/version.rb +1 -1
  21. metadata +54 -140
  22. data/.gitignore +0 -10
  23. data/Rakefile +0 -37
  24. data/mongo_mapper.gemspec +0 -214
  25. data/performance/read_write.rb +0 -52
  26. data/specs.watchr +0 -51
  27. data/test/NOTE_ON_TESTING +0 -1
  28. data/test/active_model_lint_test.rb +0 -13
  29. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +0 -63
  30. data/test/functional/associations/test_belongs_to_proxy.rb +0 -101
  31. data/test/functional/associations/test_in_array_proxy.rb +0 -325
  32. data/test/functional/associations/test_many_documents_as_proxy.rb +0 -229
  33. data/test/functional/associations/test_many_documents_proxy.rb +0 -536
  34. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +0 -176
  35. data/test/functional/associations/test_many_embedded_proxy.rb +0 -256
  36. data/test/functional/associations/test_many_polymorphic_proxy.rb +0 -302
  37. data/test/functional/associations/test_one_embedded_proxy.rb +0 -68
  38. data/test/functional/associations/test_one_proxy.rb +0 -196
  39. data/test/functional/test_associations.rb +0 -44
  40. data/test/functional/test_binary.rb +0 -27
  41. data/test/functional/test_callbacks.rb +0 -151
  42. data/test/functional/test_dirty.rb +0 -163
  43. data/test/functional/test_document.rb +0 -1219
  44. data/test/functional/test_embedded_document.rb +0 -210
  45. data/test/functional/test_identity_map.rb +0 -507
  46. data/test/functional/test_indexing.rb +0 -44
  47. data/test/functional/test_logger.rb +0 -20
  48. data/test/functional/test_modifiers.rb +0 -394
  49. data/test/functional/test_pagination.rb +0 -93
  50. data/test/functional/test_protected.rb +0 -163
  51. data/test/functional/test_string_id_compatibility.rb +0 -67
  52. data/test/functional/test_timestamps.rb +0 -64
  53. data/test/functional/test_userstamps.rb +0 -28
  54. data/test/functional/test_validations.rb +0 -342
  55. data/test/models.rb +0 -227
  56. data/test/support/custom_matchers.rb +0 -37
  57. data/test/support/timing.rb +0 -16
  58. data/test/test_helper.rb +0 -64
  59. data/test/unit/associations/test_base.rb +0 -212
  60. data/test/unit/associations/test_proxy.rb +0 -105
  61. data/test/unit/serializers/test_json_serializer.rb +0 -202
  62. data/test/unit/test_descendant_appends.rb +0 -71
  63. data/test/unit/test_document.rb +0 -225
  64. data/test/unit/test_dynamic_finder.rb +0 -123
  65. data/test/unit/test_embedded_document.rb +0 -657
  66. data/test/unit/test_keys.rb +0 -185
  67. data/test/unit/test_mongo_mapper.rb +0 -118
  68. data/test/unit/test_pagination.rb +0 -160
  69. data/test/unit/test_plugins.rb +0 -50
  70. data/test/unit/test_query.rb +0 -374
  71. data/test/unit/test_rails.rb +0 -181
  72. data/test/unit/test_rails_compatibility.rb +0 -52
  73. data/test/unit/test_serialization.rb +0 -51
  74. data/test/unit/test_support.rb +0 -382
  75. data/test/unit/test_time_zones.rb +0 -39
  76. data/test/unit/test_validations.rb +0 -544
@@ -1,10 +1,8 @@
1
- # Make sure you have the following libs in your load path or you could have issues:
2
- # gem 'activesupport', '>= 2.3.4'
3
- # gem 'mongo', '1.0'
4
- # gem 'jnunemaker-validatable', '1.8.4'
1
+ # Make sure you have the correct versions of the gems (see gemspec) in your load path.
5
2
  require 'set'
6
3
  require 'uri'
7
4
  require 'mongo'
5
+ require 'plucky'
8
6
  require 'validatable'
9
7
  require 'active_support/all'
10
8
 
@@ -71,7 +69,7 @@ module MongoMapper
71
69
  def self.config_for_environment(environment)
72
70
  env = config[environment]
73
71
  return env if env['uri'].blank?
74
-
72
+
75
73
  uri = URI.parse(env['uri'])
76
74
  raise InvalidScheme.new('must be mongodb') unless uri.scheme == 'mongodb'
77
75
  {
@@ -45,34 +45,31 @@ module MongoMapper
45
45
  end
46
46
 
47
47
  def find(*args)
48
- assert_no_first_last_or_all(args)
49
48
  options = args.extract_options!
50
49
  return nil if args.size == 0
51
50
 
52
51
  if args.first.is_a?(Array) || args.size > 1
53
52
  find_some(args, options)
54
53
  else
55
- find_one(options.merge({:_id => args[0]}))
54
+ query = query(options).update(:_id => args[0])
55
+ find_one(query.to_hash)
56
56
  end
57
57
  end
58
58
 
59
59
  def find!(*args)
60
- assert_no_first_last_or_all(args)
61
60
  options = args.extract_options!
62
61
  raise DocumentNotFound, "Couldn't find without an ID" if args.size == 0
63
62
 
64
63
  if args.first.is_a?(Array) || args.size > 1
65
64
  find_some!(args, options)
66
65
  else
67
- find_one(options.merge({:_id => args[0]})) || raise(DocumentNotFound, "Document match #{options.inspect} does not exist in #{collection.name} collection")
66
+ query = query(options).update(:_id => args[0])
67
+ find_one(query.to_hash) || raise(DocumentNotFound, "Document match #{options.inspect} does not exist in #{collection.name} collection")
68
68
  end
69
69
  end
70
70
 
71
71
  def find_each(options={})
72
- criteria, options = to_query(options)
73
- collection.find(criteria, options).each do |doc|
74
- yield load(doc)
75
- end
72
+ query(options).find().each { |doc| yield load(doc) }
76
73
  end
77
74
 
78
75
  def find_by_id(id)
@@ -93,7 +90,7 @@ module MongoMapper
93
90
 
94
91
  def last(options={})
95
92
  raise ':order option must be provided when using last' if options[:order].blank?
96
- find_one(options.merge(:order => invert_order_clause(options[:order])))
93
+ find_one(query(options).reverse.to_hash)
97
94
  end
98
95
 
99
96
  def all(options={})
@@ -101,7 +98,7 @@ module MongoMapper
101
98
  end
102
99
 
103
100
  def count(options={})
104
- collection.find(to_criteria(options)).count
101
+ query(options).count
105
102
  end
106
103
 
107
104
  def exists?(options={})
@@ -126,11 +123,11 @@ module MongoMapper
126
123
  end
127
124
 
128
125
  def delete(*ids)
129
- collection.remove(to_criteria(:_id => ids.flatten))
126
+ query(:_id => ids.flatten).remove
130
127
  end
131
128
 
132
129
  def delete_all(options={})
133
- collection.remove(to_criteria(options))
130
+ query(options).remove
134
131
  end
135
132
 
136
133
  def destroy(*ids)
@@ -153,6 +150,11 @@ module MongoMapper
153
150
  superclass.respond_to?(:keys) && superclass.keys.key?(:_type)
154
151
  end
155
152
 
153
+ # @api private for now
154
+ def query(options={})
155
+ Query.new(self, options)
156
+ end
157
+
156
158
  private
157
159
  def initialize_each(*docs)
158
160
  instances = []
@@ -165,15 +167,9 @@ module MongoMapper
165
167
  instances.size == 1 ? instances[0] : instances
166
168
  end
167
169
 
168
- def assert_no_first_last_or_all(args)
169
- if args[0] == :first || args[0] == :last || args[0] == :all
170
- raise ArgumentError, "#{self}.find(:#{args}) is no longer supported, use #{self}.#{args} instead."
171
- end
172
- end
173
-
174
170
  def find_some(ids, options={})
175
- ids = ids.flatten.compact.uniq
176
- find_many(options.merge(:_id => ids)).compact
171
+ query = query(options).update(:_id => ids.flatten.compact.uniq)
172
+ find_many(query.to_hash).compact
177
173
  end
178
174
 
179
175
  def find_some!(ids, options={})
@@ -189,30 +185,12 @@ module MongoMapper
189
185
 
190
186
  # All query methods that load documents pass through find_one or find_many
191
187
  def find_one(options={})
192
- criteria, options = to_query(options)
193
- if doc = collection.find_one(criteria, options)
194
- load(doc)
195
- end
188
+ load(query(options).first)
196
189
  end
197
190
 
198
191
  # All query methods that load documents pass through find_one or find_many
199
192
  def find_many(options)
200
- criteria, options = to_query(options)
201
- collection.find(criteria, options).to_a.map do |doc|
202
- load(doc)
203
- end
204
- end
205
-
206
- def invert_order_clause(order)
207
- order.split(',').map do |order_segment|
208
- if order_segment =~ /\sasc/i
209
- order_segment.sub /\sasc/i, ' desc'
210
- elsif order_segment =~ /\sdesc/i
211
- order_segment.sub /\sdesc/i, ' asc'
212
- else
213
- "#{order_segment.strip} desc"
214
- end
215
- end.join(',')
193
+ query(options).all().map { |doc| load(doc) }
216
194
  end
217
195
 
218
196
  def update_single(id, attrs)
@@ -220,9 +198,9 @@ module MongoMapper
220
198
  raise ArgumentError, "Updating a single document requires an id and a hash of attributes"
221
199
  end
222
200
 
223
- doc = find(id)
224
- doc.update_attributes(attrs)
225
- doc
201
+ find(id).tap do |doc|
202
+ doc.update_attributes(attrs)
203
+ end
226
204
  end
227
205
 
228
206
  def update_multiple(docs)
@@ -234,14 +212,6 @@ module MongoMapper
234
212
  docs.each_pair { |id, attrs| instances << update(id, attrs) }
235
213
  instances
236
214
  end
237
-
238
- def to_criteria(options={})
239
- Query.new(self, options).criteria
240
- end
241
-
242
- def to_query(options={})
243
- Query.new(self, options).to_a
244
- end
245
215
  end
246
216
 
247
217
  module InstanceMethods
@@ -274,9 +244,9 @@ module MongoMapper
274
244
  end
275
245
 
276
246
  def reload
277
- if attrs = collection.find_one({:_id => _id})
247
+ if doc = self.class.query(:_id => id).first
278
248
  self.class.associations.each { |name, assoc| send(name).reset if respond_to?(name) }
279
- self.attributes = attrs
249
+ self.attributes = doc
280
250
  self
281
251
  else
282
252
  raise DocumentNotFound, "Document match #{_id.inspect} does not exist in #{collection.name} collection"
@@ -84,7 +84,7 @@ module MongoMapper
84
84
 
85
85
  proxy
86
86
  end
87
-
87
+
88
88
  def save_to_collection(options = {})
89
89
  super
90
90
  associations.each do |association_name, association|
@@ -16,8 +16,8 @@ module MongoMapper
16
16
 
17
17
  def class_name
18
18
  return @class_name if defined?(@class_name)
19
-
20
- @class_name =
19
+
20
+ @class_name =
21
21
  if cn = options[:class_name]
22
22
  cn
23
23
  elsif many?
@@ -78,8 +78,8 @@ module MongoMapper
78
78
  # hate this, need to revisit
79
79
  def proxy_class
80
80
  return @proxy_class if defined?(@proxy_class)
81
-
82
- @proxy_class =
81
+
82
+ @proxy_class =
83
83
  if many?
84
84
  if klass.embeddable?
85
85
  polymorphic? ? ManyEmbeddedPolymorphicProxy : ManyEmbeddedProxy
@@ -3,7 +3,7 @@ module MongoMapper
3
3
  module Associations
4
4
  class BelongsToPolymorphicProxy < Proxy
5
5
  undef_method :object_id
6
-
6
+
7
7
  def replace(doc)
8
8
  if doc
9
9
  doc.save if doc.new?
@@ -3,7 +3,7 @@ module MongoMapper
3
3
  module Associations
4
4
  class BelongsToProxy < Proxy
5
5
  undef_method :object_id
6
-
6
+
7
7
  def replace(doc)
8
8
  if doc
9
9
  doc.save if doc.new?
@@ -88,7 +88,7 @@ module MongoMapper
88
88
  end
89
89
  reset
90
90
  end
91
-
91
+
92
92
  def save_to_collection(options = {})
93
93
  @target.each { |doc| doc.save(options) } if @target
94
94
  end
@@ -3,7 +3,7 @@ module MongoMapper
3
3
  module Associations
4
4
  class OneProxy < Proxy
5
5
  undef_method :object_id
6
-
6
+
7
7
  def build(attrs={})
8
8
  instantiate_target(:new, attrs)
9
9
  end
@@ -5,15 +5,15 @@ module MongoMapper
5
5
  def ==(other)
6
6
  other.is_a?(self.class) && _id == other._id
7
7
  end
8
-
8
+
9
9
  def eql?(other)
10
10
  self == other
11
11
  end
12
-
12
+
13
13
  def equal?(other)
14
14
  object_id === other.object_id
15
15
  end
16
-
16
+
17
17
  def hash
18
18
  _id.hash
19
19
  end
@@ -8,7 +8,7 @@ module MongoMapper
8
8
  def self.clear
9
9
  models.each { |m| m.identity_map.clear }
10
10
  end
11
-
11
+
12
12
  def self.configure(model)
13
13
  IdentityMap.models << model
14
14
  end
@@ -28,27 +28,28 @@ module MongoMapper
28
28
  end
29
29
 
30
30
  def find_one(options={})
31
- criteria, query_options = to_query(options)
32
-
31
+ query = query(options)
32
+ criteria = query.criteria.to_hash
33
+
33
34
  if simple_find?(criteria) && identity_map.key?(criteria[:_id])
34
35
  identity_map[criteria[:_id]]
35
36
  else
36
37
  super.tap do |document|
37
- remove_documents_from_map(document) if selecting_fields?(query_options)
38
+ remove_documents_from_map(document) if selecting_fields?(query.options)
38
39
  end
39
40
  end
40
41
  end
41
42
 
42
43
  def find_many(options)
43
- criteria, query_options = to_query(options)
44
44
  super.tap do |documents|
45
- remove_documents_from_map(documents) if selecting_fields?(query_options)
45
+ remove_documents_from_map(documents) if selecting_fields?(query(options).options)
46
46
  end
47
47
  end
48
48
 
49
49
  def load(attrs)
50
+ return nil if attrs.nil?
50
51
  document = identity_map[attrs['_id']]
51
-
52
+
52
53
  if document.nil? || identity_map_off?
53
54
  document = super
54
55
  identity_map[document._id] = document if identity_map_on?
@@ -1,6 +1,8 @@
1
1
  module MongoMapper
2
2
  module Plugins
3
3
  module Keys
4
+ autoload :Key, 'mongo_mapper/plugins/keys/key'
5
+
4
6
  def self.configure(model)
5
7
  model.key :_id, ObjectId
6
8
  end
@@ -17,15 +19,13 @@ module MongoMapper
17
19
  end
18
20
 
19
21
  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
22
+ Key.new(*args).tap do |key|
23
+ keys[key.name] = key
24
+ create_accessors_for(key)
25
+ create_key_in_descendants(*args)
26
+ create_indexes_for(key)
27
+ create_validations_for(key)
28
+ end
29
29
  end
30
30
 
31
31
  def key?(key)
@@ -36,9 +36,12 @@ module MongoMapper
36
36
  object_id_key?(:_id)
37
37
  end
38
38
 
39
+ def object_id_keys
40
+ keys.keys.select { |key| keys[key].type == ObjectId }.map(&:to_sym)
41
+ end
42
+
39
43
  def object_id_key?(name)
40
- key = keys[name.to_s]
41
- key && key.type == ObjectId
44
+ object_id_keys.include?(name.to_sym)
42
45
  end
43
46
 
44
47
  def to_mongo(instance)
@@ -53,6 +56,7 @@ module MongoMapper
53
56
 
54
57
  # load is overridden in identity map to ensure same objects are loaded
55
58
  def load(attrs)
59
+ return nil if attrs.nil?
56
60
  begin
57
61
  klass = attrs['_type'].present? ? attrs['_type'].constantize : self
58
62
  klass.new(attrs, true)
@@ -62,14 +66,16 @@ module MongoMapper
62
66
  end
63
67
 
64
68
  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
69
+ def key_accessors_module_defined?
70
+ if method(:const_defined?).arity == 1 # Ruby 1.9 compat check
71
+ const_defined?('MongoMapperKeys')
72
+ else
73
+ const_defined?('MongoMapperKeys', false)
74
+ end
75
+ end
71
76
 
72
- if module_defined
77
+ def accessors_module
78
+ if key_accessors_module_defined?
73
79
  const_get 'MongoMapperKeys'
74
80
  else
75
81
  const_set 'MongoMapperKeys', Module.new
@@ -154,7 +160,7 @@ module MongoMapper
154
160
 
155
161
  if from_database
156
162
  @new = false
157
- self.attributes = attrs
163
+ load_from_database(attrs)
158
164
  else
159
165
  @new = true
160
166
  assign(attrs)
@@ -170,13 +176,11 @@ module MongoMapper
170
176
  def attributes=(attrs)
171
177
  return if attrs.blank?
172
178
 
173
- attrs.each_pair do |name, value|
174
- writer_method = "#{name}="
175
-
176
- if respond_to?(writer_method)
177
- self.send(writer_method, value)
179
+ attrs.each_pair do |key, value|
180
+ if respond_to?(:"#{key}=")
181
+ self.send(:"#{key}=", value)
178
182
  else
179
- self[name.to_s] = value
183
+ self[key] = value
180
184
  end
181
185
  end
182
186
  end
@@ -255,6 +259,17 @@ module MongoMapper
255
259
  end
256
260
 
257
261
  private
262
+ def load_from_database(attrs)
263
+ return if attrs.blank?
264
+ attrs.each do |key, value|
265
+ if respond_to?(:"#{key}=") && !self.class.key?(key)
266
+ self.send(:"#{key}=", value)
267
+ else
268
+ self[key] = value
269
+ end
270
+ end
271
+ end
272
+
258
273
  def default_id_value(attrs)
259
274
  unless attrs.nil?
260
275
  provided_keys = attrs.keys.map { |k| k.to_s }
@@ -278,68 +293,29 @@ module MongoMapper
278
293
  end
279
294
  end
280
295
 
281
- def read_key(name)
282
- if key = keys[name]
283
- var_name = "@#{name}"
284
- value = key.get(instance_variable_get(var_name))
296
+ def read_key(key_name)
297
+ if key = keys[key_name]
298
+ value = key.get(instance_variable_get(:"@#{key_name}"))
285
299
  set_parent_document(key, value)
286
- instance_variable_set(var_name, value)
300
+ instance_variable_set(:"@#{key_name}", value)
287
301
  else
288
- raise KeyNotFound, "Could not find key: #{name.inspect}"
302
+ raise KeyNotFound, "Could not find key: #{key_name.inspect}"
289
303
  end
290
304
  end
291
305
 
292
306
  def read_key_before_typecast(name)
293
- instance_variable_get("@#{name}_before_typecast")
307
+ instance_variable_get(:"@#{name}_before_typecast")
294
308
  end
295
309
 
296
310
  def write_key(name, value)
297
- key = keys[name]
298
-
311
+ key = keys[name.to_s]
299
312
  set_parent_document(key, value)
300
- instance_variable_set "@#{name}_before_typecast", value
301
- instance_variable_set "@#{name}", key.set(value)
313
+ instance_variable_set :"@#{name}_before_typecast", value
314
+ instance_variable_set :"@#{name}", key.set(value)
302
315
  end
303
316
  end
304
317
 
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
318
 
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
319
  end
344
320
  end
345
321
  end