mongoid 5.0.2 → 5.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/lib/mongoid/changeable.rb +1 -1
- data/lib/mongoid/clients.rb +1 -0
- data/lib/mongoid/clients/options.rb +119 -7
- data/lib/mongoid/config.rb +7 -0
- data/lib/mongoid/config/options.rb +15 -0
- data/lib/mongoid/contextual/geo_near.rb +12 -0
- data/lib/mongoid/contextual/mongo.rb +6 -0
- data/lib/mongoid/criteria.rb +2 -56
- data/lib/mongoid/criteria/findable.rb +4 -1
- data/lib/mongoid/criteria/includable.rb +142 -0
- data/lib/mongoid/criteria/modifiable.rb +13 -1
- data/lib/mongoid/document.rb +21 -0
- data/lib/mongoid/fields/foreign_key.rb +5 -1
- data/lib/mongoid/fields/localized.rb +16 -1
- data/lib/mongoid/fields/validators/macro.rb +1 -0
- data/lib/mongoid/findable.rb +1 -0
- data/lib/mongoid/loggable.rb +1 -1
- data/lib/mongoid/matchable.rb +6 -1
- data/lib/mongoid/persistable.rb +2 -2
- data/lib/mongoid/relations/eager.rb +12 -5
- data/lib/mongoid/relations/eager/base.rb +3 -1
- data/lib/mongoid/relations/referenced/many.rb +39 -6
- data/lib/mongoid/relations/targets/enumerable.rb +3 -3
- data/lib/mongoid/scopable.rb +5 -2
- data/lib/mongoid/version.rb +1 -1
- data/spec/app/models/address.rb +2 -0
- data/spec/app/models/agent.rb +2 -0
- data/spec/app/models/alert.rb +2 -0
- data/spec/app/models/post.rb +1 -0
- data/spec/config/mongoid.yml +1 -0
- data/spec/mongoid/changeable_spec.rb +1 -1
- data/spec/mongoid/clients/options_spec.rb +57 -0
- data/spec/mongoid/config_spec.rb +38 -0
- data/spec/mongoid/contextual/geo_near_spec.rb +19 -0
- data/spec/mongoid/criteria/findable_spec.rb +11 -0
- data/spec/mongoid/criteria/modifiable_spec.rb +126 -0
- data/spec/mongoid/criteria_spec.rb +81 -5
- data/spec/mongoid/document_spec.rb +56 -0
- data/spec/mongoid/fields/foreign_key_spec.rb +23 -0
- data/spec/mongoid/fields/localized_spec.rb +32 -14
- data/spec/mongoid/matchable_spec.rb +127 -1
- data/spec/mongoid/persistable_spec.rb +24 -0
- data/spec/mongoid/relations/embedded/many_spec.rb +16 -0
- data/spec/mongoid/relations/referenced/many_spec.rb +60 -0
- data/spec/mongoid/relations/referenced/many_to_many_spec.rb +40 -0
- data/spec/mongoid/scopable_spec.rb +67 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/authorization.rb +2 -1
- metadata +6 -5
- metadata.gz.sig +0 -0
@@ -55,6 +55,18 @@ module Mongoid
|
|
55
55
|
create_document(:create!, attrs, &block)
|
56
56
|
end
|
57
57
|
|
58
|
+
# Define attributes with which new documents will be created.
|
59
|
+
#
|
60
|
+
# @example Define attributes to be used when a new document is created.
|
61
|
+
# Person.create_with(job: 'Engineer').find_or_create_by(employer: 'MongoDB')
|
62
|
+
#
|
63
|
+
# @return [ Mongoid::Criteria ] A criteria.
|
64
|
+
#
|
65
|
+
# @since 5.1.0
|
66
|
+
def create_with(attrs = {})
|
67
|
+
where(selector.merge(attrs))
|
68
|
+
end
|
69
|
+
|
58
70
|
# Find the first +Document+ given the conditions, or creates a new document
|
59
71
|
# with the conditions that were supplied.
|
60
72
|
#
|
@@ -160,7 +172,7 @@ module Mongoid
|
|
160
172
|
#
|
161
173
|
# @since 3.0.0
|
162
174
|
def create_document(method, attrs = nil, &block)
|
163
|
-
attributes = selector.reduce(attrs
|
175
|
+
attributes = selector.reduce(attrs ? attrs.dup : {}) do |hash, (key, value)|
|
164
176
|
unless key.to_s =~ /\$/ || value.is_a?(Hash)
|
165
177
|
hash[key] = value
|
166
178
|
end
|
data/lib/mongoid/document.rb
CHANGED
@@ -180,6 +180,27 @@ module Mongoid
|
|
180
180
|
attributes
|
181
181
|
end
|
182
182
|
|
183
|
+
# Calls #as_json on the document with additional, Mongoid-specific options.
|
184
|
+
#
|
185
|
+
# @example Get the document as json.
|
186
|
+
# document.as_json(compact: true)
|
187
|
+
#
|
188
|
+
# @param [ Hash ] options The options.
|
189
|
+
#
|
190
|
+
# @option options [ true, false ] :compact Whether to include fields with
|
191
|
+
# nil values in the json document.
|
192
|
+
#
|
193
|
+
# @return [ Hash ] The document as json.
|
194
|
+
#
|
195
|
+
# @since 5.1.0
|
196
|
+
def as_json(options = nil)
|
197
|
+
if options && (options[:compact] == true)
|
198
|
+
super(options).reject! { |k,v| v.nil? }
|
199
|
+
else
|
200
|
+
super(options)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
183
204
|
# Returns an instance of the specified class with the attributes,
|
184
205
|
# errors, and embedded documents of the current document.
|
185
206
|
#
|
@@ -63,7 +63,11 @@ module Mongoid
|
|
63
63
|
# @since 3.0.0
|
64
64
|
def evolve(object)
|
65
65
|
if object_id_field? || object.is_a?(Document)
|
66
|
-
|
66
|
+
if metadata.polymorphic? && constraint
|
67
|
+
constraint.convert(object)
|
68
|
+
else
|
69
|
+
object.__evolve_object_id__
|
70
|
+
end
|
67
71
|
else
|
68
72
|
related_id_field.evolve(object)
|
69
73
|
end
|
@@ -48,6 +48,21 @@ module Mongoid
|
|
48
48
|
|
49
49
|
private
|
50
50
|
|
51
|
+
# Are fallbacks being used for this localized field.
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
#
|
55
|
+
# @example Should fallbacks be used.
|
56
|
+
# field.fallbacks?
|
57
|
+
#
|
58
|
+
# @return [ true, false ] If fallbacks should be used.
|
59
|
+
#
|
60
|
+
# @since 5.1.0
|
61
|
+
def fallbacks?
|
62
|
+
return true if options[:fallbacks].nil?
|
63
|
+
!!options[:fallbacks]
|
64
|
+
end
|
65
|
+
|
51
66
|
# Lookup the value from the provided object.
|
52
67
|
#
|
53
68
|
# @api private
|
@@ -62,7 +77,7 @@ module Mongoid
|
|
62
77
|
# @since 3.0.0
|
63
78
|
def lookup(object)
|
64
79
|
locale = ::I18n.locale
|
65
|
-
if ::I18n.respond_to?(:fallbacks)
|
80
|
+
if fallbacks? && ::I18n.respond_to?(:fallbacks)
|
66
81
|
object[::I18n.fallbacks[locale].map(&:to_s).find{ |loc| object.has_key?(loc) }]
|
67
82
|
else
|
68
83
|
object[locale.to_s]
|
data/lib/mongoid/findable.rb
CHANGED
data/lib/mongoid/loggable.rb
CHANGED
data/lib/mongoid/matchable.rb
CHANGED
@@ -55,7 +55,12 @@ module Mongoid
|
|
55
55
|
selector.each_pair do |key, value|
|
56
56
|
if value.is_a?(Hash)
|
57
57
|
value.each do |item|
|
58
|
-
|
58
|
+
if item[0].to_s == "$not".freeze
|
59
|
+
item = item[1]
|
60
|
+
return false if matcher(self, key, item).matches?(item)
|
61
|
+
else
|
62
|
+
return false unless matcher(self, key, Hash[*item]).matches?(Hash[*item])
|
63
|
+
end
|
59
64
|
end
|
60
65
|
else
|
61
66
|
return false unless matcher(self, key, value).matches?(value)
|
data/lib/mongoid/persistable.rb
CHANGED
@@ -55,7 +55,7 @@ module Mongoid
|
|
55
55
|
# @since 4.0.0
|
56
56
|
def atomically
|
57
57
|
begin
|
58
|
-
@atomic_updates_to_execute = {}
|
58
|
+
@atomic_updates_to_execute = @atomic_updates_to_execute || {}
|
59
59
|
yield(self) if block_given?
|
60
60
|
persist_atomic_operations(@atomic_updates_to_execute)
|
61
61
|
true
|
@@ -207,7 +207,7 @@ module Mongoid
|
|
207
207
|
#
|
208
208
|
# @since 4.0.0
|
209
209
|
def persist_atomic_operations(operations)
|
210
|
-
if persisted?
|
210
|
+
if persisted? && operations
|
211
211
|
selector = atomic_selector
|
212
212
|
_root.collection.find(selector).update_one(positionally(selector, operations))
|
213
213
|
end
|
@@ -34,11 +34,18 @@ module Mongoid
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def preload(relations, docs)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
grouped_relations = relations.group_by do |metadata|
|
38
|
+
metadata.inverse_class_name
|
39
|
+
end
|
40
|
+
grouped_relations.keys.each do |_klass|
|
41
|
+
grouped_relations[_klass] = grouped_relations[_klass].group_by do |metadata|
|
42
|
+
metadata.relation
|
43
|
+
end
|
44
|
+
end
|
45
|
+
grouped_relations.each do |_klass, associations|
|
46
|
+
docs = associations.collect do |_relation, association|
|
47
|
+
_relation.eager_load_klass.new(association, docs).run
|
48
|
+
end.flatten
|
42
49
|
end
|
43
50
|
end
|
44
51
|
end
|
@@ -320,14 +320,47 @@ module Mongoid
|
|
320
320
|
#
|
321
321
|
# @since 2.0.0.rc.1
|
322
322
|
def append(document)
|
323
|
-
# @todo: remove?
|
324
323
|
document.with(@persistence_options) if @persistence_options
|
324
|
+
with_add_callbacks(document, already_related?(document)) do
|
325
|
+
target.push(document)
|
326
|
+
characterize_one(document)
|
327
|
+
bind_one(document)
|
328
|
+
end
|
329
|
+
end
|
325
330
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
+
# Execute before/after add callbacks around the block unless the objects
|
332
|
+
# already have a persisted relation.
|
333
|
+
#
|
334
|
+
# @example Execute before/after add callbacks around the block.
|
335
|
+
# relation.with_add_callbacks(document, false)
|
336
|
+
#
|
337
|
+
# @param [ Document ] document The document to append to the target.
|
338
|
+
# @param [ true, false ] already_related Whether the document is already related
|
339
|
+
# to the target.
|
340
|
+
#
|
341
|
+
# @since 5.1.0
|
342
|
+
def with_add_callbacks(document, already_related)
|
343
|
+
execute_callback :before_add, document unless already_related
|
344
|
+
yield
|
345
|
+
execute_callback :after_add, document unless already_related
|
346
|
+
end
|
347
|
+
|
348
|
+
# Whether the document and the base already have a persisted relation.
|
349
|
+
#
|
350
|
+
# @example Is the document already related to the base.
|
351
|
+
# relation.already_related?(document)
|
352
|
+
#
|
353
|
+
# @param [ Document ] document The document to possibly append to the target.
|
354
|
+
#
|
355
|
+
# @return [ true, false ] Whether the document is already related to the base and the
|
356
|
+
# relation is persisted.
|
357
|
+
#
|
358
|
+
# @since 5.1.0
|
359
|
+
def already_related?(document)
|
360
|
+
document.persisted? &&
|
361
|
+
document.__metadata &&
|
362
|
+
document.respond_to?(document.__metadata.foreign_key) &&
|
363
|
+
document.__send__(document.__metadata.foreign_key) == base.id
|
331
364
|
end
|
332
365
|
|
333
366
|
# Instantiate the binding associated with this relation.
|
@@ -241,7 +241,7 @@ module Mongoid
|
|
241
241
|
else
|
242
242
|
@_added, @executed = {}, true
|
243
243
|
@_loaded = target.inject({}) do |_target, doc|
|
244
|
-
_target[doc._id] = doc
|
244
|
+
_target[doc._id] = doc if doc
|
245
245
|
_target
|
246
246
|
end
|
247
247
|
end
|
@@ -340,7 +340,7 @@ module Mongoid
|
|
340
340
|
#
|
341
341
|
# @since 3.0.15
|
342
342
|
def marshal_dump
|
343
|
-
[ _added, _loaded, _unloaded ]
|
343
|
+
[ _added, _loaded, _unloaded, @executed]
|
344
344
|
end
|
345
345
|
|
346
346
|
# Loads the data needed to Marshal.load an enumerable proxy.
|
@@ -352,7 +352,7 @@ module Mongoid
|
|
352
352
|
#
|
353
353
|
# @since 3.0.15
|
354
354
|
def marshal_load(data)
|
355
|
-
@_added, @_loaded, @_unloaded = data
|
355
|
+
@_added, @_loaded, @_unloaded, @executed = data
|
356
356
|
end
|
357
357
|
|
358
358
|
# Reset the enumerable back to its persisted state.
|
data/lib/mongoid/scopable.rb
CHANGED
@@ -87,7 +87,8 @@ module Mongoid
|
|
87
87
|
# @return [ Proc ] The default scope.
|
88
88
|
#
|
89
89
|
# @since 1.0.0
|
90
|
-
def default_scope(value)
|
90
|
+
def default_scope(value = nil)
|
91
|
+
value = Proc.new { yield } if block_given?
|
91
92
|
check_scope_validity(value)
|
92
93
|
self.default_scoping = process_default_scope(value)
|
93
94
|
end
|
@@ -115,7 +116,9 @@ module Mongoid
|
|
115
116
|
#
|
116
117
|
# @since 3.0.0
|
117
118
|
def queryable
|
118
|
-
Threaded.current_scope(self) || Criteria.new(self)
|
119
|
+
crit = Threaded.current_scope(self) || Criteria.new(self)
|
120
|
+
crit.embedded = true if crit.klass.embedded?
|
121
|
+
crit
|
119
122
|
end
|
120
123
|
|
121
124
|
# Create a scope that can be accessed from the class level or chained to
|
data/lib/mongoid/version.rb
CHANGED
data/spec/app/models/address.rb
CHANGED
@@ -44,6 +44,8 @@ class Address
|
|
44
44
|
belongs_to :band
|
45
45
|
|
46
46
|
scope :without_postcode, ->{ where(postcode: nil) }
|
47
|
+
scope :ordered, ->{ order_by(state: 1) }
|
48
|
+
scope :without_postcode_ordered, ->{ without_postcode.ordered }
|
47
49
|
scope :rodeo, ->{ where(street: "Rodeo Dr") } do
|
48
50
|
def mansion?
|
49
51
|
all? { |address| address.street == "Rodeo Dr" }
|
data/spec/app/models/agent.rb
CHANGED
@@ -5,8 +5,10 @@ class Agent
|
|
5
5
|
field :number, type: String
|
6
6
|
field :dob, type: Time
|
7
7
|
embeds_many :names, as: :namable
|
8
|
+
embeds_one :address
|
8
9
|
belongs_to :game
|
9
10
|
belongs_to :agency, touch: true, autobuild: true
|
10
11
|
|
11
12
|
has_and_belongs_to_many :accounts
|
13
|
+
has_and_belongs_to_many :basics
|
12
14
|
end
|
data/spec/app/models/alert.rb
CHANGED
data/spec/app/models/post.rb
CHANGED
@@ -14,6 +14,7 @@ class Post
|
|
14
14
|
has_and_belongs_to_many :tags, before_add: :before_add_tag, after_add: :after_add_tag, before_remove: :before_remove_tag, after_remove: :after_remove_tag
|
15
15
|
has_many :videos, validate: false
|
16
16
|
has_many :roles, validate: false
|
17
|
+
has_many :alerts
|
17
18
|
|
18
19
|
belongs_to :posteable, polymorphic: true
|
19
20
|
accepts_nested_attributes_for :posteable, autosave: true
|
data/spec/config/mongoid.yml
CHANGED
@@ -28,6 +28,41 @@ describe Mongoid::Clients::Options do
|
|
28
28
|
expect(Band.new.persistence_options).to be_nil
|
29
29
|
end
|
30
30
|
|
31
|
+
context 'when passed a block', if: testing_locally? do
|
32
|
+
|
33
|
+
let!(:connections_before) do
|
34
|
+
Band.mongo_client.database.command(serverStatus: 1).first['connections']['current']
|
35
|
+
end
|
36
|
+
|
37
|
+
before do
|
38
|
+
Band.with(options) do |klass|
|
39
|
+
klass.where(name: 'emily').to_a
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
let(:connections_after) do
|
44
|
+
Band.mongo_client.database.command(serverStatus: 1).first['connections']['current']
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when a new cluster is created by the driver' do
|
48
|
+
|
49
|
+
let(:options) { { connect_timeout: 2 } }
|
50
|
+
|
51
|
+
it 'disconnects the new cluster' do
|
52
|
+
expect(connections_after).to eq(connections_before)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when the same cluster is used by the new client' do
|
57
|
+
|
58
|
+
let(:options) { { database: 'same-cluster' } }
|
59
|
+
|
60
|
+
it 'does not disconnect the original cluster' do
|
61
|
+
expect(connections_after).to eq(connections_before)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
31
66
|
context "when calling .collection method" do
|
32
67
|
|
33
68
|
before do
|
@@ -82,6 +117,28 @@ describe Mongoid::Clients::Options do
|
|
82
117
|
it "passes down the options to collection" do
|
83
118
|
expect(instance.collection.database.name).to eq('test')
|
84
119
|
end
|
120
|
+
|
121
|
+
context "when the object is shared between threads" do
|
122
|
+
|
123
|
+
before do
|
124
|
+
threads = []
|
125
|
+
doc = Band.create(name: "Beatles")
|
126
|
+
100.times do |i|
|
127
|
+
threads << Thread.new do
|
128
|
+
if i % 2 == 0
|
129
|
+
doc.with(nil).set(name: "Rolling Stones")
|
130
|
+
else
|
131
|
+
doc.with(options).set(name: "Beatles")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
threads.join
|
136
|
+
end
|
137
|
+
|
138
|
+
it "does not share the persistence options" do
|
139
|
+
expect(Band.persistence_options).to eq(nil)
|
140
|
+
end
|
141
|
+
end
|
85
142
|
end
|
86
143
|
|
87
144
|
describe "#persistence_options" do
|
data/spec/mongoid/config_spec.rb
CHANGED
@@ -60,6 +60,23 @@ describe Mongoid::Config do
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
+
context "when the log level is not set in the configuration" do
|
64
|
+
|
65
|
+
before do
|
66
|
+
Mongoid.configure do |config|
|
67
|
+
config.load_configuration(CONFIG)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it "sets the Mongoid logger level to the default" do
|
72
|
+
expect(Mongoid.logger.level).to eq(Logger::INFO)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "sets the Mongo driver logger level to the default" do
|
76
|
+
expect(Mongo::Logger.logger.level).to eq(Logger::INFO)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
63
80
|
describe "#load!" do
|
64
81
|
|
65
82
|
before(:all) do
|
@@ -89,6 +106,27 @@ describe Mongoid::Config do
|
|
89
106
|
end
|
90
107
|
end
|
91
108
|
|
109
|
+
context "when the log level is set in the configuration" do
|
110
|
+
|
111
|
+
before do
|
112
|
+
described_class.load!(file, :test)
|
113
|
+
end
|
114
|
+
|
115
|
+
after do
|
116
|
+
Mongoid.configure do |config|
|
117
|
+
config.load_configuration(CONFIG)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
it "sets the Mongoid logger level" do
|
122
|
+
expect(Mongoid.logger.level).to eq(Logger::WARN)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "sets the Mongo driver logger level" do
|
126
|
+
expect(Mongo::Logger.logger.level).to eq(Logger::WARN)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
92
130
|
context "when provided an environment" do
|
93
131
|
|
94
132
|
before do
|