mongo_mapper 0.15.0 → 0.15.1
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 +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
|