mongoid 5.0.2 → 5.1.0
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
- 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
|