mongo_mapper 0.14.0.rc1 → 0.15.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/LICENSE +1 -1
- data/{README.rdoc → README.md} +26 -21
- data/examples/keys.rb +1 -1
- data/examples/modifiers/set.rb +1 -1
- data/examples/querying.rb +1 -1
- data/examples/safe.rb +2 -2
- data/examples/scopes.rb +1 -1
- data/lib/mongo_mapper.rb +3 -0
- data/lib/mongo_mapper/connection.rb +16 -38
- data/lib/mongo_mapper/extensions/object_id.rb +5 -1
- data/lib/mongo_mapper/plugins/accessible.rb +1 -1
- data/lib/mongo_mapper/plugins/associations/base.rb +10 -2
- data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +1 -1
- data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +6 -0
- data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +36 -6
- data/lib/mongo_mapper/plugins/associations/in_foreign_array_proxy.rb +136 -0
- data/lib/mongo_mapper/plugins/associations/many_association.rb +4 -2
- data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +3 -1
- data/lib/mongo_mapper/plugins/associations/proxy.rb +11 -3
- data/lib/mongo_mapper/plugins/associations/single_association.rb +5 -4
- data/lib/mongo_mapper/plugins/dirty.rb +29 -37
- data/lib/mongo_mapper/plugins/document.rb +1 -1
- data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +1 -1
- data/lib/mongo_mapper/plugins/embedded_callbacks.rb +1 -0
- data/lib/mongo_mapper/plugins/embedded_document.rb +2 -2
- data/lib/mongo_mapper/plugins/identity_map.rb +3 -1
- data/lib/mongo_mapper/plugins/indexes.rb +13 -6
- data/lib/mongo_mapper/plugins/keys.rb +12 -7
- data/lib/mongo_mapper/plugins/keys/key.rb +13 -8
- data/lib/mongo_mapper/plugins/modifiers.rb +39 -14
- data/lib/mongo_mapper/plugins/persistence.rb +6 -2
- data/lib/mongo_mapper/plugins/querying.rb +9 -3
- data/lib/mongo_mapper/plugins/querying/decorated_plucky_query.rb +6 -6
- data/lib/mongo_mapper/plugins/safe.rb +10 -4
- data/lib/mongo_mapper/plugins/scopes.rb +19 -3
- data/lib/mongo_mapper/plugins/stats.rb +1 -3
- data/lib/mongo_mapper/plugins/strong_parameters.rb +26 -0
- data/lib/mongo_mapper/railtie.rb +1 -0
- data/lib/mongo_mapper/utils.rb +2 -2
- data/lib/mongo_mapper/version.rb +1 -1
- data/spec/examples.txt +1729 -0
- data/spec/functional/accessible_spec.rb +7 -1
- data/spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb +2 -2
- data/spec/functional/associations/belongs_to_proxy_spec.rb +55 -5
- data/spec/functional/associations/in_array_proxy_spec.rb +149 -14
- data/spec/functional/associations/in_foreign_array_proxy_spec.rb +321 -0
- data/spec/functional/associations/many_documents_as_proxy_spec.rb +6 -6
- data/spec/functional/associations/many_documents_proxy_spec.rb +22 -22
- data/spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb +2 -2
- data/spec/functional/associations/many_polymorphic_proxy_spec.rb +4 -4
- data/spec/functional/associations/one_as_proxy_spec.rb +8 -8
- data/spec/functional/associations/one_embedded_proxy_spec.rb +28 -0
- data/spec/functional/associations/one_proxy_spec.rb +19 -9
- data/spec/functional/associations_spec.rb +3 -3
- data/spec/functional/binary_spec.rb +2 -2
- data/spec/functional/caching_spec.rb +15 -22
- data/spec/functional/callbacks_spec.rb +2 -2
- data/spec/functional/counter_cache_spec.rb +10 -10
- data/spec/functional/dirty_spec.rb +48 -10
- data/spec/functional/dirty_with_callbacks_spec.rb +59 -0
- data/spec/functional/document_spec.rb +5 -8
- data/spec/functional/dumpable_spec.rb +1 -1
- data/spec/functional/embedded_document_spec.rb +5 -5
- data/spec/functional/identity_map_spec.rb +8 -8
- data/spec/functional/indexes_spec.rb +19 -18
- data/spec/functional/keys_spec.rb +51 -33
- data/spec/functional/logger_spec.rb +2 -2
- data/spec/functional/modifiers_spec.rb +81 -19
- data/spec/functional/partial_updates_spec.rb +8 -8
- data/spec/functional/protected_spec.rb +1 -1
- data/spec/functional/querying_spec.rb +70 -22
- data/spec/functional/safe_spec.rb +23 -27
- data/spec/functional/sci_spec.rb +7 -7
- data/spec/functional/scopes_spec.rb +89 -1
- data/spec/functional/static_keys_spec.rb +2 -2
- data/spec/functional/stats_spec.rb +28 -12
- data/spec/functional/strong_parameters_spec.rb +49 -0
- data/spec/functional/validations_spec.rb +8 -16
- data/spec/quality_spec.rb +1 -1
- data/spec/spec_helper.rb +39 -8
- data/spec/support/matchers.rb +1 -1
- data/spec/unit/associations/proxy_spec.rb +13 -5
- data/spec/unit/clone_spec.rb +1 -1
- data/spec/unit/document_spec.rb +3 -3
- data/spec/unit/embedded_document_spec.rb +4 -5
- data/spec/unit/extensions_spec.rb +2 -2
- data/spec/unit/identity_map_middleware_spec.rb +65 -96
- data/spec/unit/key_spec.rb +16 -17
- data/spec/unit/keys_spec.rb +17 -8
- data/spec/unit/mongo_mapper_spec.rb +41 -88
- data/spec/unit/rails_spec.rb +2 -2
- data/spec/unit/validations_spec.rb +18 -18
- metadata +53 -31
- data/lib/mongo_mapper/extensions/ordered_hash.rb +0 -23
@@ -0,0 +1,136 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module MongoMapper
|
3
|
+
module Plugins
|
4
|
+
module Associations
|
5
|
+
class InForeignArrayProxy < Collection
|
6
|
+
include DynamicQuerying::ClassMethods
|
7
|
+
|
8
|
+
def find(*args)
|
9
|
+
query.find(*scoped_ids(args))
|
10
|
+
end
|
11
|
+
|
12
|
+
def find!(*args)
|
13
|
+
query.find!(*scoped_ids(args))
|
14
|
+
end
|
15
|
+
|
16
|
+
def paginate(options)
|
17
|
+
query.paginate(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def all(options={})
|
21
|
+
query(options).all
|
22
|
+
end
|
23
|
+
|
24
|
+
def first(options={})
|
25
|
+
query(options).first
|
26
|
+
end
|
27
|
+
|
28
|
+
def last(options={})
|
29
|
+
query(options).last
|
30
|
+
end
|
31
|
+
|
32
|
+
def count(options={})
|
33
|
+
query(options).count
|
34
|
+
end
|
35
|
+
|
36
|
+
def destroy_all(options={})
|
37
|
+
all(options).each do |doc|
|
38
|
+
doc.destroy
|
39
|
+
end
|
40
|
+
reset
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete_all(options={})
|
44
|
+
docs = query(options).fields(:_id).all
|
45
|
+
klass.delete(docs.map { |d| d.id })
|
46
|
+
reset
|
47
|
+
end
|
48
|
+
|
49
|
+
def nullify
|
50
|
+
replace([])
|
51
|
+
reset
|
52
|
+
end
|
53
|
+
|
54
|
+
def create(attrs={})
|
55
|
+
doc = klass.create(attrs)
|
56
|
+
if doc.persisted?
|
57
|
+
inverse_association(doc) << proxy_owner
|
58
|
+
doc.save
|
59
|
+
reset
|
60
|
+
end
|
61
|
+
doc
|
62
|
+
end
|
63
|
+
|
64
|
+
def create!(attrs={})
|
65
|
+
doc = klass.create!(attrs)
|
66
|
+
|
67
|
+
if doc.persisted?
|
68
|
+
inverse_association(doc) << proxy_owner
|
69
|
+
doc.save
|
70
|
+
reset
|
71
|
+
end
|
72
|
+
doc
|
73
|
+
end
|
74
|
+
|
75
|
+
def <<(*docs)
|
76
|
+
flatten_deeper(docs).each do |doc|
|
77
|
+
inverse_association(doc) << proxy_owner
|
78
|
+
doc.save
|
79
|
+
end
|
80
|
+
reset
|
81
|
+
end
|
82
|
+
alias_method :push, :<<
|
83
|
+
alias_method :concat, :<<
|
84
|
+
|
85
|
+
def replace(docs)
|
86
|
+
doc_ids = docs.map do |doc|
|
87
|
+
doc.save unless doc.persisted?
|
88
|
+
inverse_association(doc) << proxy_owner
|
89
|
+
doc.save
|
90
|
+
doc.id
|
91
|
+
end
|
92
|
+
|
93
|
+
replace_selector = { options[:from] => proxy_owner.id }
|
94
|
+
unless doc_ids.empty?
|
95
|
+
replace_selector[:_id] = {"$not" => {"$in" => doc_ids}}
|
96
|
+
end
|
97
|
+
|
98
|
+
klass.collection.update_many(replace_selector, {
|
99
|
+
"$pull" => { options[:from] => proxy_owner.id }
|
100
|
+
})
|
101
|
+
|
102
|
+
reset
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def query(options={})
|
108
|
+
klass.query({}.
|
109
|
+
merge(association.query_options).
|
110
|
+
merge(options).
|
111
|
+
merge(criteria))
|
112
|
+
end
|
113
|
+
|
114
|
+
def criteria
|
115
|
+
{options[:from] => proxy_owner.id}
|
116
|
+
end
|
117
|
+
|
118
|
+
def scoped_ids(args)
|
119
|
+
valid = args.flatten.map do |id|
|
120
|
+
id = ObjectId.to_mongo(id) if klass.using_object_id?
|
121
|
+
id
|
122
|
+
end
|
123
|
+
valid.empty? ? nil : valid
|
124
|
+
end
|
125
|
+
|
126
|
+
def find_target
|
127
|
+
all
|
128
|
+
end
|
129
|
+
|
130
|
+
def inverse_association(doc)
|
131
|
+
doc.send(options[:as].to_s.pluralize)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -17,6 +17,8 @@ module MongoMapper
|
|
17
17
|
ManyPolymorphicProxy
|
18
18
|
elsif as?
|
19
19
|
ManyDocumentsAsProxy
|
20
|
+
elsif in_foreign_array?
|
21
|
+
InForeignArrayProxy
|
20
22
|
elsif in_array?
|
21
23
|
InArrayProxy
|
22
24
|
else
|
@@ -26,7 +28,7 @@ module MongoMapper
|
|
26
28
|
end
|
27
29
|
|
28
30
|
def setup(model)
|
29
|
-
model.associations_module.module_eval
|
31
|
+
model.associations_module.module_eval(<<-end_eval, __FILE__, __LINE__ + 1)
|
30
32
|
def #{name}
|
31
33
|
get_proxy(associations[#{name.inspect}])
|
32
34
|
end
|
@@ -60,4 +62,4 @@ module MongoMapper
|
|
60
62
|
end
|
61
63
|
end
|
62
64
|
end
|
63
|
-
end
|
65
|
+
end
|
@@ -11,7 +11,9 @@ module MongoMapper
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def replace(doc)
|
14
|
-
if doc.
|
14
|
+
if doc.instance_of?(klass)
|
15
|
+
@target = doc
|
16
|
+
elsif doc.respond_to?(:attributes)
|
15
17
|
@target = klass.load(doc.attributes, true)
|
16
18
|
else
|
17
19
|
@target = klass.load(doc, true)
|
@@ -13,7 +13,6 @@ module MongoMapper
|
|
13
13
|
|
14
14
|
attr_reader :proxy_owner, :association, :target
|
15
15
|
|
16
|
-
alias :proxy_target :target
|
17
16
|
alias :proxy_association :association
|
18
17
|
|
19
18
|
def_delegators :proxy_association, :klass, :options
|
@@ -100,10 +99,15 @@ module MongoMapper
|
|
100
99
|
end
|
101
100
|
end
|
102
101
|
|
102
|
+
def read
|
103
|
+
load_target
|
104
|
+
@target
|
105
|
+
end
|
106
|
+
|
103
107
|
protected
|
104
108
|
|
105
109
|
def load_target
|
106
|
-
|
110
|
+
if !loaded? || stale_target?
|
107
111
|
if @target.is_a?(Array) && @target.any?
|
108
112
|
@target = find_target + @target.find_all { |record| !record.persisted? }
|
109
113
|
else
|
@@ -130,9 +134,13 @@ module MongoMapper
|
|
130
134
|
|
131
135
|
private
|
132
136
|
|
137
|
+
def stale_target?
|
138
|
+
false
|
139
|
+
end
|
140
|
+
|
133
141
|
def method_missing(method, *args, &block)
|
134
142
|
if load_target
|
135
|
-
target.
|
143
|
+
target.public_send(method, *args, &block)
|
136
144
|
end
|
137
145
|
end
|
138
146
|
end
|
@@ -5,10 +5,11 @@ module MongoMapper
|
|
5
5
|
class SingleAssociation < Base
|
6
6
|
def setup(model)
|
7
7
|
@model = model
|
8
|
-
|
8
|
+
|
9
|
+
model.associations_module.module_eval(<<-end_eval, __FILE__, __LINE__ + 1)
|
9
10
|
def #{name}
|
10
11
|
proxy = get_proxy(associations[#{name.inspect}])
|
11
|
-
proxy.nil? ? nil : proxy
|
12
|
+
proxy.nil? ? nil : proxy.read
|
12
13
|
end
|
13
14
|
|
14
15
|
def #{name}=(value)
|
@@ -20,7 +21,7 @@ module MongoMapper
|
|
20
21
|
end
|
21
22
|
|
22
23
|
proxy.replace(value)
|
23
|
-
|
24
|
+
proxy.read
|
24
25
|
end
|
25
26
|
|
26
27
|
def #{name}?
|
@@ -43,4 +44,4 @@ module MongoMapper
|
|
43
44
|
end
|
44
45
|
end
|
45
46
|
end
|
46
|
-
end
|
47
|
+
end
|
@@ -3,58 +3,50 @@ module MongoMapper
|
|
3
3
|
module Plugins
|
4
4
|
module Dirty
|
5
5
|
extend ActiveSupport::Concern
|
6
|
-
|
7
6
|
include ::ActiveModel::Dirty
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
doc.tap { changed_attributes.delete('_id') }
|
14
|
-
end
|
15
|
-
|
16
|
-
def save(*)
|
17
|
-
clear_changes { super }
|
18
|
-
end
|
19
|
-
|
20
|
-
def reload(*)
|
21
|
-
doc = super
|
22
|
-
doc.tap { clear_changes }
|
23
|
-
end
|
24
|
-
|
25
|
-
def clear_changes
|
26
|
-
previous = changes
|
27
|
-
(block_given? ? yield : true).tap do |result|
|
28
|
-
unless result == false #failed validation; nil is OK.
|
29
|
-
@previously_changed = previous
|
30
|
-
changed_attributes.clear
|
8
|
+
module ClassMethods
|
9
|
+
def create_accessors_for(key)
|
10
|
+
super.tap do
|
11
|
+
define_attribute_methods([key.name])
|
31
12
|
end
|
32
13
|
end
|
33
14
|
end
|
34
15
|
|
35
|
-
|
16
|
+
def save_to_collection(*)
|
17
|
+
super.tap do
|
18
|
+
changes_applied
|
19
|
+
end
|
20
|
+
end
|
36
21
|
|
37
|
-
|
38
|
-
|
39
|
-
|
22
|
+
def reload!
|
23
|
+
super.tap do
|
24
|
+
clear_changes_information
|
25
|
+
end
|
40
26
|
end
|
41
27
|
|
42
28
|
private
|
43
29
|
|
44
|
-
def write_key(
|
45
|
-
|
46
|
-
|
30
|
+
def write_key(key_name, value)
|
31
|
+
key_name = unalias_key(key_name)
|
32
|
+
|
33
|
+
if !keys.key?(key_name)
|
47
34
|
super
|
48
35
|
else
|
49
|
-
|
50
|
-
|
51
|
-
|
36
|
+
# find the MongoMapper::Plugins::Keys::Key
|
37
|
+
_, key = keys.detect { |n, v| n == key_name }
|
38
|
+
|
39
|
+
# typecast to the new value
|
40
|
+
old_value = read_key(key_name)
|
41
|
+
new_value = key.get(key.set(value))
|
42
|
+
|
43
|
+
# only mark changed if really changed value (after typecasting)
|
44
|
+
unless old_value == new_value
|
45
|
+
attribute_will_change!(key_name)
|
52
46
|
end
|
53
|
-
end
|
54
|
-
end
|
55
47
|
|
56
|
-
|
57
|
-
|
48
|
+
super
|
49
|
+
end
|
58
50
|
end
|
59
51
|
end
|
60
52
|
end
|
@@ -41,12 +41,12 @@ module MongoMapper
|
|
41
41
|
|
42
42
|
def persist(options={})
|
43
43
|
@_new = false
|
44
|
-
|
44
|
+
changes_applied if respond_to?(:changes_applied)
|
45
45
|
save_to_collection(options)
|
46
46
|
end
|
47
47
|
|
48
48
|
def _root_document
|
49
|
-
|
49
|
+
_parent_document.try(:_root_document)
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
@@ -133,10 +133,12 @@ module PluckyMethods
|
|
133
133
|
end
|
134
134
|
|
135
135
|
def find_each(opts={})
|
136
|
+
return super if !block_given?
|
137
|
+
|
136
138
|
query = clone.amend(opts)
|
137
139
|
super(opts) do |doc|
|
138
140
|
doc.remove_from_identity_map if doc && query.fields?
|
139
|
-
yield doc
|
141
|
+
yield doc
|
140
142
|
end
|
141
143
|
end
|
142
144
|
end
|
@@ -6,19 +6,20 @@ module MongoMapper
|
|
6
6
|
|
7
7
|
module ClassMethods
|
8
8
|
def ensure_index(spec, options = {})
|
9
|
-
|
9
|
+
#TODO: should we emulate the mongo 1.x behaviour of caching attempts to create indexes?
|
10
|
+
collection.indexes.create_one dealias_options(spec), options
|
10
11
|
end
|
11
12
|
|
12
13
|
def create_index(spec, options = {})
|
13
|
-
collection.
|
14
|
+
collection.indexes.create_one dealias_options(spec), options
|
14
15
|
end
|
15
16
|
|
16
17
|
def drop_index(name)
|
17
|
-
collection.
|
18
|
+
collection.indexes.drop_one name
|
18
19
|
end
|
19
20
|
|
20
21
|
def drop_indexes
|
21
|
-
collection.
|
22
|
+
collection.indexes.drop_all
|
22
23
|
end
|
23
24
|
|
24
25
|
private
|
@@ -26,11 +27,17 @@ module MongoMapper
|
|
26
27
|
def dealias_options(options)
|
27
28
|
case options
|
28
29
|
when Symbol, String
|
29
|
-
|
30
|
+
{abbr(options) => 1}
|
30
31
|
when Hash
|
31
32
|
dealias_keys(options)
|
32
33
|
when Array
|
33
|
-
options.
|
34
|
+
if options.first.is_a?(Hash)
|
35
|
+
options.map {|o| dealias_options(o) }
|
36
|
+
elsif options.first.is_a?(Array) # [[:foo, 1], [:bar, 1]]
|
37
|
+
options.inject({}) {|acc, tuple| acc.merge(dealias_options(tuple))}
|
38
|
+
else
|
39
|
+
dealias_keys(Hash[*options])
|
40
|
+
end
|
34
41
|
else
|
35
42
|
options
|
36
43
|
end
|
@@ -144,9 +144,8 @@ module MongoMapper
|
|
144
144
|
end
|
145
145
|
|
146
146
|
def create_accessors_for(key)
|
147
|
-
accessors = ""
|
148
147
|
if key.read_accessor?
|
149
|
-
|
148
|
+
accessors_module.module_eval(<<-end_eval, __FILE__, __LINE__+1)
|
150
149
|
def #{key.name}
|
151
150
|
read_key(:#{key.name})
|
152
151
|
end
|
@@ -158,7 +157,7 @@ module MongoMapper
|
|
158
157
|
end
|
159
158
|
|
160
159
|
if key.write_accessor?
|
161
|
-
|
160
|
+
accessors_module.module_eval(<<-end_eval, __FILE__, __LINE__+1)
|
162
161
|
def #{key.name}=(value)
|
163
162
|
write_key(:#{key.name}, value)
|
164
163
|
end
|
@@ -166,7 +165,7 @@ module MongoMapper
|
|
166
165
|
end
|
167
166
|
|
168
167
|
if key.predicate_accessor?
|
169
|
-
|
168
|
+
accessors_module.module_eval(<<-end_eval, __FILE__, __LINE__+1)
|
170
169
|
def #{key.name}?
|
171
170
|
read_key(:#{key.name}).present?
|
172
171
|
end
|
@@ -179,7 +178,6 @@ module MongoMapper
|
|
179
178
|
end
|
180
179
|
end
|
181
180
|
|
182
|
-
accessors_module.module_eval accessors
|
183
181
|
include accessors_module
|
184
182
|
end
|
185
183
|
|
@@ -295,8 +293,16 @@ module MongoMapper
|
|
295
293
|
end
|
296
294
|
end
|
297
295
|
|
296
|
+
# NOTE: We can't use alias_method here as we need the #attributes=
|
297
|
+
# superclass method to get called (for example:
|
298
|
+
# MongoMapper::Plugins::Accessible filters non-permitted parameters
|
299
|
+
# through `attributes=`
|
300
|
+
def assign_attributes(new_attributes)
|
301
|
+
self.attributes = new_attributes
|
302
|
+
end
|
303
|
+
|
298
304
|
def to_mongo(include_abbreviatons = true)
|
299
|
-
|
305
|
+
Hash.new.tap do |attrs|
|
300
306
|
self.class.unaliased_keys.each do |name, key|
|
301
307
|
value = self.read_key(key.name)
|
302
308
|
if key.type == ObjectId || !value.nil?
|
@@ -449,7 +455,6 @@ module MongoMapper
|
|
449
455
|
else
|
450
456
|
@_dynamic_attributes[key.name.to_sym] = as_typecast
|
451
457
|
end
|
452
|
-
@attributes = nil
|
453
458
|
value
|
454
459
|
end
|
455
460
|
|