mongo_mapper 0.7.3 → 0.7.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +3 -2
- data/lib/mongo_mapper.rb +2 -3
- data/lib/mongo_mapper/plugins/associations.rb +10 -1
- data/lib/mongo_mapper/plugins/associations/base.rb +2 -2
- data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +12 -3
- data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +41 -0
- data/lib/mongo_mapper/plugins/associations/one_proxy.rb +1 -0
- data/lib/mongo_mapper/plugins/associations/proxy.rb +8 -2
- data/lib/mongo_mapper/plugins/callbacks.rb +7 -3
- data/lib/mongo_mapper/plugins/descendants.rb +2 -2
- data/lib/mongo_mapper/plugins/keys.rb +14 -7
- data/lib/mongo_mapper/plugins/modifiers.rb +30 -14
- data/lib/mongo_mapper/plugins/protected.rb +1 -1
- data/lib/mongo_mapper/plugins/serialization.rb +1 -1
- data/lib/mongo_mapper/query.rb +27 -19
- data/lib/mongo_mapper/support.rb +10 -6
- data/lib/mongo_mapper/version.rb +1 -1
- data/mongo_mapper.gemspec +14 -8
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +1 -1
- data/test/functional/associations/test_belongs_to_proxy.rb +1 -1
- data/test/functional/associations/test_many_documents_proxy.rb +100 -17
- data/test/functional/associations/test_one_embedded_proxy.rb +68 -0
- data/test/functional/associations/test_one_proxy.rb +48 -13
- data/test/functional/test_binary.rb +1 -1
- data/test/functional/test_document.rb +7 -7
- data/test/functional/test_embedded_document.rb +8 -0
- data/test/functional/test_identity_map.rb +2 -2
- data/test/functional/test_modifiers.rb +249 -185
- data/test/functional/test_protected.rb +4 -0
- data/test/functional/test_string_id_compatibility.rb +1 -1
- data/test/support/custom_matchers.rb +0 -18
- data/test/test_helper.rb +6 -4
- data/test/unit/associations/test_base.rb +7 -2
- data/test/unit/test_document.rb +5 -5
- data/test/unit/test_embedded_document.rb +7 -7
- data/test/unit/test_query.rb +17 -7
- data/test/unit/test_support.rb +26 -14
- 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.
|
17
|
-
gem.add_dependency('jnunemaker-validatable', '1.8.
|
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')
|
data/lib/mongo_mapper.rb
CHANGED
@@ -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.
|
4
|
-
# gem 'jnunemaker-validatable', '1.8.
|
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|
|
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|
|
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
|
-
|
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
|
@@ -96,8 +96,14 @@ module MongoMapper
|
|
96
96
|
end
|
97
97
|
|
98
98
|
def load_target
|
99
|
-
|
100
|
-
|
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
|
-
|
85
|
-
|
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
|
-
|
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
|
-
|
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,
|
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
|
-
|
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
|
29
|
-
|
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
|
-
|
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(
|
77
|
+
self.class.increment(id, hash)
|
63
78
|
end
|
64
79
|
|
65
80
|
def decrement(hash)
|
66
|
-
self.class.decrement(
|
81
|
+
self.class.decrement(id, hash)
|
67
82
|
end
|
68
83
|
|
69
84
|
def set(hash)
|
70
|
-
self.class.set(
|
85
|
+
self.class.set(id, hash)
|
71
86
|
end
|
72
87
|
|
73
88
|
def push(hash)
|
74
|
-
self.class.push(
|
89
|
+
self.class.push(id, hash)
|
75
90
|
end
|
76
91
|
|
77
92
|
def pull(hash)
|
78
|
-
self.class.pull(
|
93
|
+
self.class.pull(id, hash)
|
79
94
|
end
|
80
95
|
|
81
|
-
def
|
82
|
-
self.class.push_uniq(
|
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(
|
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?
|
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)
|
data/lib/mongo_mapper/query.rb
CHANGED
@@ -68,49 +68,52 @@ module MongoMapper
|
|
68
68
|
if model.object_id_key?(key)
|
69
69
|
case value
|
70
70
|
when String
|
71
|
-
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(
|
89
|
-
return if
|
87
|
+
def to_fields(keys)
|
88
|
+
return keys if keys.is_a?(Hash)
|
89
|
+
return nil if keys.blank?
|
90
90
|
|
91
|
-
if
|
92
|
-
|
91
|
+
if keys.respond_to?(:flatten, :compact)
|
92
|
+
keys.flatten.compact
|
93
93
|
else
|
94
|
-
|
94
|
+
keys.split(',').map { |key| key.strip }
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
-
def to_order(
|
99
|
-
direction
|
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(
|
105
|
-
|
102
|
+
def normalized_key(key)
|
103
|
+
key.to_s == 'id' ? :_id : key
|
106
104
|
end
|
107
105
|
|
108
|
-
|
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?(
|
110
|
+
modifier?(key) ? value.to_a : {'$in' => value.to_a}
|
112
111
|
when Hash
|
113
|
-
|
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
|