mongo_mapper 0.7.3 → 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 (38) hide show
  1. data/Rakefile +3 -2
  2. data/lib/mongo_mapper.rb +2 -3
  3. data/lib/mongo_mapper/plugins/associations.rb +10 -1
  4. data/lib/mongo_mapper/plugins/associations/base.rb +2 -2
  5. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +12 -3
  6. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +41 -0
  7. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +1 -0
  8. data/lib/mongo_mapper/plugins/associations/proxy.rb +8 -2
  9. data/lib/mongo_mapper/plugins/callbacks.rb +7 -3
  10. data/lib/mongo_mapper/plugins/descendants.rb +2 -2
  11. data/lib/mongo_mapper/plugins/keys.rb +14 -7
  12. data/lib/mongo_mapper/plugins/modifiers.rb +30 -14
  13. data/lib/mongo_mapper/plugins/protected.rb +1 -1
  14. data/lib/mongo_mapper/plugins/serialization.rb +1 -1
  15. data/lib/mongo_mapper/query.rb +27 -19
  16. data/lib/mongo_mapper/support.rb +10 -6
  17. data/lib/mongo_mapper/version.rb +1 -1
  18. data/mongo_mapper.gemspec +14 -8
  19. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +1 -1
  20. data/test/functional/associations/test_belongs_to_proxy.rb +1 -1
  21. data/test/functional/associations/test_many_documents_proxy.rb +100 -17
  22. data/test/functional/associations/test_one_embedded_proxy.rb +68 -0
  23. data/test/functional/associations/test_one_proxy.rb +48 -13
  24. data/test/functional/test_binary.rb +1 -1
  25. data/test/functional/test_document.rb +7 -7
  26. data/test/functional/test_embedded_document.rb +8 -0
  27. data/test/functional/test_identity_map.rb +2 -2
  28. data/test/functional/test_modifiers.rb +249 -185
  29. data/test/functional/test_protected.rb +4 -0
  30. data/test/functional/test_string_id_compatibility.rb +1 -1
  31. data/test/support/custom_matchers.rb +0 -18
  32. data/test/test_helper.rb +6 -4
  33. data/test/unit/associations/test_base.rb +7 -2
  34. data/test/unit/test_document.rb +5 -5
  35. data/test/unit/test_embedded_document.rb +7 -7
  36. data/test/unit/test_query.rb +17 -7
  37. data/test/unit/test_support.rb +26 -14
  38. metadata +33 -16
data/Rakefile CHANGED
@@ -13,9 +13,10 @@ Jeweler::Tasks.new do |gem|
13
13
  gem.version = MongoMapper::Version
14
14
 
15
15
  gem.add_dependency('activesupport', '>= 2.3.4')
16
- gem.add_dependency('mongo', '0.19.3')
17
- gem.add_dependency('jnunemaker-validatable', '1.8.3')
16
+ gem.add_dependency('mongo', '0.20.1')
17
+ gem.add_dependency('jnunemaker-validatable', '1.8.4')
18
18
 
19
+ gem.add_development_dependency('json', '>= 1.2.3')
19
20
  gem.add_development_dependency('jnunemaker-matchy', '0.4.0')
20
21
  gem.add_development_dependency('shoulda', '2.10.2')
21
22
  gem.add_development_dependency('timecop', '0.3.1')
@@ -1,8 +1,7 @@
1
1
  # Make sure you have the following libs in your load path or you could have issues:
2
2
  # gem 'activesupport', '>= 2.3.4'
3
- # gem 'mongo', '0.19.3'
4
- # gem 'jnunemaker-validatable', '1.8.3'
5
- # gem 'activesupport', '= 2.3.4'
3
+ # gem 'mongo', '0.20.1'
4
+ # gem 'jnunemaker-validatable', '1.8.4'
6
5
  require 'set'
7
6
  require 'uri'
8
7
  require 'mongo'
@@ -84,6 +84,14 @@ module MongoMapper
84
84
 
85
85
  proxy
86
86
  end
87
+
88
+ def save_to_collection(options = {})
89
+ super
90
+ associations.each do |association_name, association|
91
+ proxy = get_proxy(association)
92
+ proxy.save_to_collection(options) if proxy.proxy_respond_to?(:save_to_collection)
93
+ end
94
+ end
87
95
  end
88
96
 
89
97
  autoload :Base, 'mongo_mapper/plugins/associations/base'
@@ -97,9 +105,10 @@ module MongoMapper
97
105
  autoload :ManyEmbeddedPolymorphicProxy, 'mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy'
98
106
  autoload :ManyDocumentsAsProxy, 'mongo_mapper/plugins/associations/many_documents_as_proxy'
99
107
  autoload :OneProxy, 'mongo_mapper/plugins/associations/one_proxy'
108
+ autoload :OneEmbeddedProxy, 'mongo_mapper/plugins/associations/one_embedded_proxy'
100
109
  autoload :InArrayProxy, 'mongo_mapper/plugins/associations/in_array_proxy'
101
110
  end
102
111
  end
103
112
  end
104
113
 
105
- require 'mongo_mapper/plugins/associations/proxy'
114
+ require 'mongo_mapper/plugins/associations/proxy'
@@ -56,7 +56,7 @@ module MongoMapper
56
56
  end
57
57
 
58
58
  def embeddable?
59
- many? && klass.embeddable?
59
+ (one? || many?) && klass.embeddable?
60
60
  end
61
61
 
62
62
  def type_key_name
@@ -95,7 +95,7 @@ module MongoMapper
95
95
  end
96
96
  end
97
97
  elsif one?
98
- OneProxy
98
+ klass.embeddable? ? OneEmbeddedProxy : OneProxy
99
99
  else
100
100
  polymorphic? ? BelongsToPolymorphicProxy : BelongsToProxy
101
101
  end
@@ -37,13 +37,13 @@ module MongoMapper
37
37
  def replace(docs)
38
38
  load_target
39
39
  target.map(&:destroy)
40
- docs.each { |doc| apply_scope(doc).save }
40
+ docs.each { |doc| prepare(doc).save }
41
41
  reset
42
42
  end
43
43
 
44
44
  def <<(*docs)
45
45
  ensure_owner_saved
46
- flatten_deeper(docs).each { |doc| apply_scope(doc).save }
46
+ flatten_deeper(docs).each { |doc| prepare(doc).save }
47
47
  reset
48
48
  end
49
49
  alias_method :push, :<<
@@ -52,7 +52,8 @@ module MongoMapper
52
52
  def build(attrs={})
53
53
  doc = klass.new(attrs)
54
54
  apply_scope(doc)
55
- reset
55
+ @target ||= [] unless loaded?
56
+ @target << doc
56
57
  doc
57
58
  end
58
59
 
@@ -87,6 +88,10 @@ module MongoMapper
87
88
  end
88
89
  reset
89
90
  end
91
+
92
+ def save_to_collection(options = {})
93
+ @target.each { |doc| doc.save(options) } if @target
94
+ end
90
95
 
91
96
  protected
92
97
  def scoped_conditions
@@ -105,6 +110,10 @@ module MongoMapper
105
110
  owner.save if owner.new?
106
111
  end
107
112
 
113
+ def prepare(doc)
114
+ klass === doc ? apply_scope(doc) : build(doc)
115
+ end
116
+
108
117
  def apply_scope(doc)
109
118
  ensure_owner_saved
110
119
  doc[foreign_key] = owner.id
@@ -0,0 +1,41 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Associations
4
+ class OneEmbeddedProxy < Proxy
5
+ undef_method :object_id
6
+
7
+ def build(attributes={})
8
+ @target = klass.new(attributes)
9
+ assign_references(@target)
10
+ loaded
11
+ @target
12
+ end
13
+
14
+ def replace(doc)
15
+ if doc.respond_to?(:attributes)
16
+ @target = klass.load(doc.attributes)
17
+ else
18
+ @target = klass.load(doc)
19
+ end
20
+ assign_references(@target)
21
+ loaded
22
+ @target
23
+ end
24
+
25
+ protected
26
+
27
+ def find_target
28
+ if @value
29
+ klass.load(@value).tap do |child|
30
+ assign_references(child)
31
+ end
32
+ end
33
+ end
34
+
35
+ def assign_references(doc)
36
+ doc._parent_document = owner
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -37,6 +37,7 @@ module MongoMapper
37
37
 
38
38
  unless doc.nil?
39
39
  owner.save if owner.new?
40
+ doc = klass.new(doc) unless klass === doc
40
41
  doc[foreign_key] = owner.id
41
42
  doc.save if doc.new?
42
43
  loaded
@@ -96,8 +96,14 @@ module MongoMapper
96
96
  end
97
97
 
98
98
  def load_target
99
- @target = find_target unless loaded?
100
- loaded
99
+ unless loaded?
100
+ if @target.is_a?(Array) && @target.any?
101
+ @target = find_target + @target.find_all { |record| record.new? }
102
+ else
103
+ @target = find_target
104
+ end
105
+ loaded
106
+ end
101
107
  @target
102
108
  rescue MongoMapper::DocumentNotFound
103
109
  reset
@@ -81,8 +81,12 @@ module MongoMapper
81
81
  return unless self.class.respond_to?(callback_chain_method)
82
82
  self.class.send(callback_chain_method).run(self, options, &block)
83
83
  self.embedded_associations.each do |association|
84
- self.send(association.name).each do |document|
85
- document.run_callbacks(kind, options, &block)
84
+ if association.one?
85
+ self.send(association.name).run_callbacks(kind, options, &block)
86
+ else
87
+ self.send(association.name).each do |document|
88
+ document.run_callbacks(kind, options, &block)
89
+ end
86
90
  end
87
91
  end
88
92
  end
@@ -233,4 +237,4 @@ module MongoMapper
233
237
  end
234
238
  end
235
239
  end
236
- end
240
+ end
@@ -3,12 +3,12 @@ module MongoMapper
3
3
  module Descendants
4
4
  module ClassMethods
5
5
  def inherited(descendant)
6
- (@descendants ||= []) << descendant
6
+ descendants << descendant
7
7
  super
8
8
  end
9
9
 
10
10
  def descendants
11
- @descendants
11
+ @descendants ||= []
12
12
  end
13
13
  end
14
14
  end
@@ -95,7 +95,6 @@ module MongoMapper
95
95
  end
96
96
 
97
97
  def create_key_in_descendants(*args)
98
- return if descendants.blank?
99
98
  descendants.each { |descendant| descendant.key(*args) }
100
99
  end
101
100
 
@@ -188,7 +187,11 @@ module MongoMapper
188
187
 
189
188
  embedded_associations.each do |association|
190
189
  if documents = instance_variable_get(association.ivar)
191
- attrs[association.name] = documents.map { |document| document.to_mongo }
190
+ if association.one?
191
+ attrs[association.name] = documents.to_mongo
192
+ else
193
+ attrs[association.name] = documents.map { |document| document.to_mongo }
194
+ end
192
195
  end
193
196
  end
194
197
 
@@ -252,7 +255,7 @@ module MongoMapper
252
255
  unless attrs.nil?
253
256
  provided_keys = attrs.keys.map { |k| k.to_s }
254
257
  unless provided_keys.include?('_id') || provided_keys.include?('id')
255
- write_key :_id, Mongo::ObjectID.new
258
+ write_key :_id, BSON::ObjectID.new
256
259
  end
257
260
  end
258
261
  end
@@ -265,10 +268,17 @@ module MongoMapper
265
268
  self.class.key(name) unless respond_to?("#{name}=")
266
269
  end
267
270
 
271
+ def set_parent_document(key, value)
272
+ if key.embeddable? && value.is_a?(key.type)
273
+ value._parent_document = self
274
+ end
275
+ end
276
+
268
277
  def read_key(name)
269
278
  if key = keys[name]
270
279
  var_name = "@#{name}"
271
280
  value = key.get(instance_variable_get(var_name))
281
+ set_parent_document(key, value)
272
282
  instance_variable_set(var_name, value)
273
283
  else
274
284
  raise KeyNotFound, "Could not find key: #{name.inspect}"
@@ -282,10 +292,7 @@ module MongoMapper
282
292
  def write_key(name, value)
283
293
  key = keys[name]
284
294
 
285
- if key.embeddable? && value.is_a?(key.type)
286
- value._parent_document = self
287
- end
288
-
295
+ set_parent_document(key, value)
289
296
  instance_variable_set "@#{name}_before_typecast", value
290
297
  instance_variable_set "@#{name}", key.set(value)
291
298
  end
@@ -17,6 +17,19 @@ module MongoMapper
17
17
  modifier_update('$set', args)
18
18
  end
19
19
 
20
+ def unset(*args)
21
+ if args[0].is_a?(Hash)
22
+ criteria, keys = args.shift, args
23
+ else
24
+ keys, ids = args.partition { |arg| arg.is_a?(Symbol) }
25
+ criteria = {:id => ids}
26
+ end
27
+
28
+ criteria = to_criteria(criteria)
29
+ modifiers = keys.inject({}) { |hash, key| hash[key] = 1; hash }
30
+ collection.update(criteria, {'$unset' => modifiers}, :multi => true)
31
+ end
32
+
20
33
  def push(*args)
21
34
  modifier_update('$push', args)
22
35
  end
@@ -25,11 +38,10 @@ module MongoMapper
25
38
  modifier_update('$pushAll', args)
26
39
  end
27
40
 
28
- def push_uniq(*args)
29
- criteria, keys = criteria_and_keys_from_args(args)
30
- keys.each { |key, value | criteria[key] = {'$ne' => value} }
31
- collection.update(criteria, {'$push' => keys}, :multi => true)
41
+ def add_to_set(*args)
42
+ modifier_update('$addToSet', args)
32
43
  end
44
+ alias push_uniq add_to_set
33
45
 
34
46
  def pull(*args)
35
47
  modifier_update('$pull', args)
@@ -46,8 +58,7 @@ module MongoMapper
46
58
  private
47
59
  def modifier_update(modifier, args)
48
60
  criteria, keys = criteria_and_keys_from_args(args)
49
- modifiers = {modifier => keys}
50
- collection.update(criteria, modifiers, :multi => true)
61
+ collection.update(criteria, {modifier => keys}, :multi => true)
51
62
  end
52
63
 
53
64
  def criteria_and_keys_from_args(args)
@@ -58,32 +69,37 @@ module MongoMapper
58
69
  end
59
70
 
60
71
  module InstanceMethods
72
+ def unset(*keys)
73
+ self.class.unset(id, *keys)
74
+ end
75
+
61
76
  def increment(hash)
62
- self.class.increment({:_id => id}, hash)
77
+ self.class.increment(id, hash)
63
78
  end
64
79
 
65
80
  def decrement(hash)
66
- self.class.decrement({:_id => id}, hash)
81
+ self.class.decrement(id, hash)
67
82
  end
68
83
 
69
84
  def set(hash)
70
- self.class.set({:_id => id}, hash)
85
+ self.class.set(id, hash)
71
86
  end
72
87
 
73
88
  def push(hash)
74
- self.class.push({:_id => id}, hash)
89
+ self.class.push(id, hash)
75
90
  end
76
91
 
77
92
  def pull(hash)
78
- self.class.pull({:_id => id}, hash)
93
+ self.class.pull(id, hash)
79
94
  end
80
95
 
81
- def push_uniq(hash)
82
- self.class.push_uniq({:_id => id}, hash)
96
+ def add_to_set(hash)
97
+ self.class.push_uniq(id, hash)
83
98
  end
99
+ alias push_uniq add_to_set
84
100
 
85
101
  def pop(hash)
86
- self.class.pop({:_id => id}, hash)
102
+ self.class.pop(id, hash)
87
103
  end
88
104
  end
89
105
  end
@@ -36,7 +36,7 @@ module MongoMapper
36
36
 
37
37
  protected
38
38
  def filter_protected_attrs(attrs)
39
- return attrs if protected_attributes.blank?
39
+ return attrs if protected_attributes.blank? || attrs.blank?
40
40
  attrs.dup.delete_if { |key, val| protected_attributes.include?(key.to_sym) }
41
41
  end
42
42
  end
@@ -51,7 +51,7 @@ module MongoMapper
51
51
  hash[key] = value.map do |item|
52
52
  item.respond_to?(:as_json) ? item.as_json(options) : item
53
53
  end
54
- elsif value.is_a? Mongo::ObjectID
54
+ elsif value.is_a? BSON::ObjectID
55
55
  hash[key] = value.to_s
56
56
  elsif value.respond_to?(:as_json)
57
57
  hash[key] = value.as_json(options)
@@ -68,49 +68,52 @@ module MongoMapper
68
68
  if model.object_id_key?(key)
69
69
  case value
70
70
  when String
71
- value = Mongo::ObjectID.from_string(value)
71
+ value = ObjectId.to_mongo(value)
72
72
  when Array
73
73
  value.map! { |id| ObjectId.to_mongo(id) }
74
74
  end
75
75
  end
76
76
 
77
77
  if symbol_operator?(key)
78
- value = {"$#{key.operator}" => value}
79
- key = normalized_key(key.field)
78
+ key, value = normalized_key(key.field), {"$#{key.operator}" => value}
80
79
  end
81
80
 
82
- criteria[key] = normalized_value(key, value)
81
+ criteria[key] = normalized_value(criteria, key, value)
83
82
  end
84
83
 
85
84
  criteria
86
85
  end
87
86
 
88
- def to_fields(fields)
89
- return if fields.blank?
87
+ def to_fields(keys)
88
+ return keys if keys.is_a?(Hash)
89
+ return nil if keys.blank?
90
90
 
91
- if fields.respond_to?(:flatten, :compact)
92
- fields.flatten.compact
91
+ if keys.respond_to?(:flatten, :compact)
92
+ keys.flatten.compact
93
93
  else
94
- fields.split(',').map { |field| field.strip }
94
+ keys.split(',').map { |key| key.strip }
95
95
  end
96
96
  end
97
97
 
98
- def to_order(field, direction=nil)
99
- direction ||= 'ASC'
100
- direction = direction.upcase == 'ASC' ? 1 : -1
101
- [normalized_key(field).to_s, direction]
98
+ def to_order(key, direction=nil)
99
+ [normalized_key(key).to_s, normalized_direction(direction)]
102
100
  end
103
101
 
104
- def normalized_key(field)
105
- field.to_s == 'id' ? :_id : field
102
+ def normalized_key(key)
103
+ key.to_s == 'id' ? :_id : key
106
104
  end
107
105
 
108
- def normalized_value(field, value)
106
+ # TODO: this is getting heavy enough to move to a class
107
+ def normalized_value(criteria, key, value)
109
108
  case value
110
109
  when Array, Set
111
- modifier?(field) ? value.to_a : {'$in' => value.to_a}
110
+ modifier?(key) ? value.to_a : {'$in' => value.to_a}
112
111
  when Hash
113
- to_criteria(value, field)
112
+ if criteria[key].kind_of?(Hash)
113
+ criteria[key].dup.merge(to_criteria(value, key))
114
+ else
115
+ to_criteria(value, key)
116
+ end
114
117
  when Time
115
118
  value.utc
116
119
  else
@@ -118,6 +121,11 @@ module MongoMapper
118
121
  end
119
122
  end
120
123
 
124
+ def normalized_direction(direction)
125
+ direction ||= 'asc'
126
+ direction.downcase == 'asc' ? Mongo::ASCENDING : Mongo::DESCENDING
127
+ end
128
+
121
129
  def normalized_sort(sort)
122
130
  return if sort.blank?
123
131
 
@@ -132,4 +140,4 @@ module MongoMapper
132
140
  end
133
141
  end
134
142
  end
135
- end
143
+ end