mongo_mapper 0.15.0 → 0.15.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -8
- data/lib/mongo_mapper.rb +2 -0
- 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/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/proxy.rb +5 -1
- data/lib/mongo_mapper/plugins/associations/single_association.rb +5 -4
- data/lib/mongo_mapper/plugins/identity_map.rb +3 -1
- data/lib/mongo_mapper/plugins/keys.rb +8 -0
- data/lib/mongo_mapper/plugins/keys/key.rb +13 -8
- data/lib/mongo_mapper/plugins/modifiers.rb +12 -4
- data/lib/mongo_mapper/plugins/querying/decorated_plucky_query.rb +4 -3
- data/lib/mongo_mapper/plugins/strong_parameters.rb +26 -0
- data/lib/mongo_mapper/railtie.rb +1 -0
- data/lib/mongo_mapper/version.rb +1 -1
- data/spec/examples.txt +1655 -1581
- data/spec/functional/accessible_spec.rb +6 -0
- data/spec/functional/associations/belongs_to_proxy_spec.rb +18 -1
- data/spec/functional/associations/in_array_proxy_spec.rb +135 -0
- data/spec/functional/associations/in_foreign_array_proxy_spec.rb +321 -0
- data/spec/functional/document_spec.rb +0 -3
- data/spec/functional/identity_map_spec.rb +2 -2
- data/spec/functional/keys_spec.rb +29 -11
- data/spec/functional/modifiers_spec.rb +14 -0
- data/spec/functional/querying_spec.rb +22 -0
- data/spec/functional/strong_parameters_spec.rb +49 -0
- data/spec/unit/associations/proxy_spec.rb +0 -4
- data/spec/unit/keys_spec.rb +10 -1
- data/spec/unit/validations_spec.rb +18 -18
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77d669d6c4286995fb2afbed7cf23475c6dc496cb3faf97a57f3b8f115e263b3
|
4
|
+
data.tar.gz: d11040bb05eeb6cb57465c4585c0667b16d750b16d7fd5fedca5e03734b769ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56a665e63c65da52238cc18348333a2a9c6b6d1c5acf564308f60f8fcec8a1be759c5b7546a0259092a7f6acc9170659d55c462ac59fffe6520f3339a650df15
|
7
|
+
data.tar.gz: 8dceac60d1ef1159585453450d2960e4d9d50e472ec7728f3b703da8e40e80af625b01bb6c4022327e16631960faa698da93c7d37c563cd1c839eb3ebfc28ddb
|
data/README.md
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
A Ruby Object Mapper for Mongo.
|
4
4
|
|
5
|
-
[<img src="https://badge.fury.io/rb/mongo_mapper.
|
5
|
+
[<img src="https://badge.fury.io/rb/mongo_mapper.svg" alt="RubyGems">](https://rubygems.org/gems/mongo_mapper)
|
6
6
|
|
7
|
-
[<img src="https://travis-ci.org/mongomapper/mongomapper.
|
7
|
+
[<img src="https://travis-ci.org/mongomapper/mongomapper.svg?branch=master" alt="Build Status" />](https://travis-ci.org/mongomapper/mongomapper)
|
8
8
|
|
9
|
-
[<img src="https://coveralls.io/repos/mongomapper/mongomapper/badge.
|
9
|
+
[<img src="https://coveralls.io/repos/mongomapper/mongomapper/badge.svg" alt="Coverage Status" />](https://coveralls.io/r/mongomapper/mongomapper)
|
10
10
|
|
11
11
|
## Install
|
12
12
|
|
@@ -45,8 +45,6 @@ Additionally, MongoMapper is tested against:
|
|
45
45
|
|
46
46
|
Hit up the Google group: http://groups.google.com/group/mongomapper
|
47
47
|
|
48
|
-
Hop on IRC: irc://chat.freenode.net/#mongomapper
|
49
|
-
|
50
48
|
## Copyright
|
51
49
|
|
52
50
|
Copyright (c) 2009-2020 MongoMapper. See LICENSE for details.
|
@@ -59,6 +57,5 @@ MongoMapper/Plucky is:
|
|
59
57
|
* Chris Heald
|
60
58
|
* Scott Taylor
|
61
59
|
|
62
|
-
|
63
|
-
|
64
|
-
* Frederick Cheung
|
60
|
+
But all open source projects are a team effort and could not happen without
|
61
|
+
everyone who has contributed. See `CONTRIBUTORS` for the full list. Thank you!
|
data/lib/mongo_mapper.rb
CHANGED
@@ -62,6 +62,7 @@ module MongoMapper
|
|
62
62
|
autoload :Scopes, 'mongo_mapper/plugins/scopes'
|
63
63
|
autoload :Serialization, 'mongo_mapper/plugins/serialization'
|
64
64
|
autoload :Stats, 'mongo_mapper/plugins/stats'
|
65
|
+
autoload :StrongParameters, 'mongo_mapper/plugins/strong_parameters'
|
65
66
|
autoload :Timestamps, 'mongo_mapper/plugins/timestamps'
|
66
67
|
autoload :Userstamps, 'mongo_mapper/plugins/userstamps'
|
67
68
|
autoload :Validations, 'mongo_mapper/plugins/validations'
|
@@ -87,6 +88,7 @@ module MongoMapper
|
|
87
88
|
autoload :OneEmbeddedProxy, 'mongo_mapper/plugins/associations/one_embedded_proxy'
|
88
89
|
autoload :OneEmbeddedPolymorphicProxy, 'mongo_mapper/plugins/associations/one_embedded_polymorphic_proxy'
|
89
90
|
autoload :InArrayProxy, 'mongo_mapper/plugins/associations/in_array_proxy'
|
91
|
+
autoload :InForeignArrayProxy, 'mongo_mapper/plugins/associations/in_foreign_array_proxy'
|
90
92
|
end
|
91
93
|
end
|
92
94
|
|
@@ -6,7 +6,7 @@ module MongoMapper
|
|
6
6
|
attr_reader :name, :options, :query_options
|
7
7
|
|
8
8
|
# Options that should not be considered MongoDB query options/criteria
|
9
|
-
AssociationOptions = [:as, :class, :class_name, :dependent, :extend, :foreign_key, :in, :polymorphic, :autosave, :touch, :counter_cache]
|
9
|
+
AssociationOptions = [:as, :class, :class_name, :dependent, :extend, :foreign_key, :in, :from, :polymorphic, :autosave, :touch, :counter_cache, :ordered]
|
10
10
|
|
11
11
|
def initialize(name, options={}, &extension)
|
12
12
|
@name, @options, @query_options, @original_options = name.to_sym, {}, {}, options
|
@@ -28,13 +28,17 @@ module MongoMapper
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def as?
|
31
|
-
!!@options[:as]
|
31
|
+
!in_foreign_array? && !!@options[:as]
|
32
32
|
end
|
33
33
|
|
34
34
|
def in_array?
|
35
35
|
!!@options[:in]
|
36
36
|
end
|
37
37
|
|
38
|
+
def in_foreign_array?
|
39
|
+
!!@options[:from]
|
40
|
+
end
|
41
|
+
|
38
42
|
def embeddable?
|
39
43
|
klass.embeddable?
|
40
44
|
end
|
@@ -47,6 +51,10 @@ module MongoMapper
|
|
47
51
|
!!@options[:counter_cache]
|
48
52
|
end
|
49
53
|
|
54
|
+
def ordered?
|
55
|
+
!!@options[:ordered]
|
56
|
+
end
|
57
|
+
|
50
58
|
def type_key_name
|
51
59
|
"_type"
|
52
60
|
end
|
@@ -6,11 +6,11 @@ module MongoMapper
|
|
6
6
|
include DynamicQuerying::ClassMethods
|
7
7
|
|
8
8
|
def find(*args)
|
9
|
-
query.find(*scoped_ids(args))
|
9
|
+
order_results(query.find(*scoped_ids(args)))
|
10
10
|
end
|
11
11
|
|
12
12
|
def find!(*args)
|
13
|
-
query.find!(*scoped_ids(args))
|
13
|
+
order_results(query.find!(*scoped_ids(args)))
|
14
14
|
end
|
15
15
|
|
16
16
|
def paginate(options)
|
@@ -20,17 +20,29 @@ module MongoMapper
|
|
20
20
|
|
21
21
|
def all(options={})
|
22
22
|
return [] if ids.blank?
|
23
|
-
query(options).all
|
23
|
+
order_results(query(options).all)
|
24
24
|
end
|
25
25
|
|
26
26
|
def first(options={})
|
27
27
|
return nil if ids.blank?
|
28
|
-
|
28
|
+
|
29
|
+
if ordered?
|
30
|
+
ids = find_ordered_ids(options)
|
31
|
+
find!(ids.first) if ids.any?
|
32
|
+
else
|
33
|
+
query(options).first
|
34
|
+
end
|
29
35
|
end
|
30
36
|
|
31
37
|
def last(options={})
|
32
38
|
return nil if ids.blank?
|
33
|
-
|
39
|
+
|
40
|
+
if ordered?
|
41
|
+
ids = find_ordered_ids(options)
|
42
|
+
find!(ids.last) if ids.any?
|
43
|
+
else
|
44
|
+
query(options).last
|
45
|
+
end
|
34
46
|
end
|
35
47
|
|
36
48
|
def count(options={})
|
@@ -120,6 +132,13 @@ module MongoMapper
|
|
120
132
|
valid.empty? ? nil : valid
|
121
133
|
end
|
122
134
|
|
135
|
+
def find_ordered_ids(options={})
|
136
|
+
return ids if options.empty?
|
137
|
+
|
138
|
+
matched_ids = klass.collection.distinct(:_id, query(options).criteria.to_hash)
|
139
|
+
matched_ids.sort_by! { |matched_id| ids.index(matched_id) }
|
140
|
+
end
|
141
|
+
|
123
142
|
def find_target
|
124
143
|
return [] if ids.blank?
|
125
144
|
all
|
@@ -128,7 +147,18 @@ module MongoMapper
|
|
128
147
|
def ids
|
129
148
|
proxy_owner[options[:in]]
|
130
149
|
end
|
150
|
+
|
151
|
+
def order_results(objects)
|
152
|
+
return objects if !ordered?
|
153
|
+
return objects unless objects.respond_to?(:to_a) && objects.respond_to?(:sort_by)
|
154
|
+
objects.sort_by { |obj| ids.index(obj.id) }
|
155
|
+
end
|
156
|
+
|
157
|
+
def ordered?
|
158
|
+
association.ordered?
|
159
|
+
end
|
160
|
+
|
131
161
|
end
|
132
162
|
end
|
133
163
|
end
|
134
|
-
end
|
164
|
+
end
|
@@ -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
|
@@ -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,6 +99,11 @@ 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
|
@@ -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
|
@@ -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
|
@@ -295,6 +295,14 @@ module MongoMapper
|
|
295
295
|
end
|
296
296
|
end
|
297
297
|
|
298
|
+
# NOTE: We can't use alias_method here as we need the #attributes=
|
299
|
+
# superclass method to get called (for example:
|
300
|
+
# MongoMapper::Plugins::Accessible filters non-permitted parameters
|
301
|
+
# through `attributes=`
|
302
|
+
def assign_attributes(new_attributes)
|
303
|
+
self.attributes = new_attributes
|
304
|
+
end
|
305
|
+
|
298
306
|
def to_mongo(include_abbreviatons = true)
|
299
307
|
Hash.new.tap do |attrs|
|
300
308
|
self.class.unaliased_keys.each do |name, key|
|
@@ -3,10 +3,11 @@ module MongoMapper
|
|
3
3
|
module Plugins
|
4
4
|
module Keys
|
5
5
|
class Key
|
6
|
-
|
7
|
-
|
6
|
+
RESERVED_KEYS = %w( id class object_id attributes )
|
8
7
|
ID_STR = '_id'
|
9
8
|
|
9
|
+
attr_accessor :name, :type, :options, :default, :ivar, :abbr, :accessors
|
10
|
+
|
10
11
|
def initialize(*args)
|
11
12
|
options_from_args = args.extract_options!
|
12
13
|
@name, @type = args.shift.to_s, args.shift
|
@@ -60,12 +61,17 @@ module MongoMapper
|
|
60
61
|
# Special Case: Generate default _id on access
|
61
62
|
value = default_value if @is_id and !value
|
62
63
|
|
64
|
+
value = type.from_mongo(value)
|
65
|
+
|
63
66
|
if @typecast
|
64
|
-
klass = typecast_class
|
65
|
-
|
66
|
-
|
67
|
-
type
|
67
|
+
klass = typecast_class # Don't make this lookup on every call
|
68
|
+
# typecast assumes array-ish object.
|
69
|
+
value = value.map { |v| klass.from_mongo(v) }
|
70
|
+
# recast it in the original type
|
71
|
+
value = type.from_mongo(value)
|
68
72
|
end
|
73
|
+
|
74
|
+
value
|
69
75
|
end
|
70
76
|
|
71
77
|
def set(value)
|
@@ -93,7 +99,6 @@ module MongoMapper
|
|
93
99
|
!!@name.match(/\A[a-z_][a-z0-9_]*\z/i)
|
94
100
|
end
|
95
101
|
|
96
|
-
RESERVED_KEYS = %w( id class object_id )
|
97
102
|
def reserved_name?
|
98
103
|
RESERVED_KEYS.include?(@name)
|
99
104
|
end
|
@@ -124,7 +129,7 @@ module MongoMapper
|
|
124
129
|
|
125
130
|
def validate_key_name!
|
126
131
|
if reserved_name?
|
127
|
-
raise MongoMapper::InvalidKey.new("`#{@name}` is a reserved key name
|
132
|
+
raise MongoMapper::InvalidKey.new("`#{@name}` is a reserved key name")
|
128
133
|
elsif !valid_ruby_name?
|
129
134
|
raise MongoMapper::InvalidKey.new("`#{@name}` is not a valid key name. Keys must match [a-z][a-z0-9_]*")
|
130
135
|
end
|