mongo_mapper 0.14.0.rc1 → 0.15.3
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.
- 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
|
|