mongoid 9.0.8 → 9.0.9
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/lib/mongoid/association/embedded/embeds_many/proxy.rb +3 -29
- data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +0 -6
- data/lib/mongoid/association/referenced/has_many/enumerable.rb +40 -0
- data/lib/mongoid/association/referenced/has_many/proxy.rb +17 -1
- data/lib/mongoid/attributes.rb +19 -1
- data/lib/mongoid/contextual/mongo.rb +8 -89
- data/lib/mongoid/pluckable.rb +132 -0
- data/lib/mongoid/traversable.rb +0 -2
- data/lib/mongoid/version.rb +1 -1
- data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +394 -0
- data/spec/mongoid/attributes_spec.rb +13 -0
- data/spec/shared/CANDIDATE.md +28 -0
- data/spec/shared/lib/mrss/spec_organizer.rb +32 -3
- data/spec/shared/shlib/server.sh +1 -1
- data/spec/support/models/company.rb +2 -0
- data/spec/support/models/passport.rb +1 -0
- data/spec/support/models/product.rb +2 -0
- data/spec/support/models/seo.rb +2 -0
- metadata +7 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c5dca0df68ed7086b6c641b1021e556812e23872d5bd52a9777093e2d8cf1e64
|
|
4
|
+
data.tar.gz: 24630d19104369a922b3a5a152012ae8eba3719576ef3c9e5f97304b6896a1c4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 03013a0c28cda90cfc3b99c26d22e7672e8ce1c6449e599d7ae39a42d19ba742f37dea7510411ca004c7d5d5f25af80e9230bac63fef8ce43515711d7f669343
|
|
7
|
+
data.tar.gz: 8bd5b0c4faa4f5f16fcc56ff612bae5a14fd40fa01513fda545469d225c6cd4909022c066719950147dc116dc1a7bdb79b69d48ad40454aabdb45481994dd69b
|
|
@@ -14,6 +14,7 @@ module Mongoid
|
|
|
14
14
|
# the array of child documents.
|
|
15
15
|
class Proxy < Association::Many
|
|
16
16
|
include Batchable
|
|
17
|
+
extend Forwardable
|
|
17
18
|
|
|
18
19
|
# Class-level methods for the Proxy class.
|
|
19
20
|
module ClassMethods
|
|
@@ -54,6 +55,8 @@ module Mongoid
|
|
|
54
55
|
|
|
55
56
|
extend ClassMethods
|
|
56
57
|
|
|
58
|
+
def_delegators :criteria, :find, :pluck
|
|
59
|
+
|
|
57
60
|
# Instantiate a new embeds_many association.
|
|
58
61
|
#
|
|
59
62
|
# @example Create the new association.
|
|
@@ -312,35 +315,6 @@ module Mongoid
|
|
|
312
315
|
end
|
|
313
316
|
end
|
|
314
317
|
|
|
315
|
-
# Finds a document in this association through several different
|
|
316
|
-
# methods.
|
|
317
|
-
#
|
|
318
|
-
# This method delegates to +Mongoid::Criteria#find+. If this method is
|
|
319
|
-
# not given a block, it returns one or many documents for the provided
|
|
320
|
-
# _id values.
|
|
321
|
-
#
|
|
322
|
-
# If this method is given a block, it returns the first document
|
|
323
|
-
# of those found by the current Criteria object for which the block
|
|
324
|
-
# returns a truthy value.
|
|
325
|
-
#
|
|
326
|
-
# @example Find a document by its id.
|
|
327
|
-
# person.addresses.find(BSON::ObjectId.new)
|
|
328
|
-
#
|
|
329
|
-
# @example Find documents for multiple ids.
|
|
330
|
-
# person.addresses.find([ BSON::ObjectId.new, BSON::ObjectId.new ])
|
|
331
|
-
#
|
|
332
|
-
# @example Finds the first matching document using a block.
|
|
333
|
-
# person.addresses.find { |addr| addr.state == 'CA' }
|
|
334
|
-
#
|
|
335
|
-
# @param [ Object... ] *args Various arguments.
|
|
336
|
-
# @param &block Optional block to pass.
|
|
337
|
-
# @yield [ Object ] Yields each enumerable element to the block.
|
|
338
|
-
#
|
|
339
|
-
# @return [ Document | Array<Document> | nil ] A document or matching documents.
|
|
340
|
-
def find(...)
|
|
341
|
-
criteria.find(...)
|
|
342
|
-
end
|
|
343
|
-
|
|
344
318
|
# Get all the documents in the association that are loaded into memory.
|
|
345
319
|
#
|
|
346
320
|
# @example Get the in memory documents.
|
|
@@ -53,8 +53,6 @@ module Mongoid
|
|
|
53
53
|
# @param [ Document... ] *args Any number of documents.
|
|
54
54
|
#
|
|
55
55
|
# @return [ Array<Document> ] The loaded docs.
|
|
56
|
-
#
|
|
57
|
-
# rubocop:disable Metrics/AbcSize
|
|
58
56
|
def <<(*args)
|
|
59
57
|
docs = args.flatten
|
|
60
58
|
return concat(docs) if docs.size > 1
|
|
@@ -89,7 +87,6 @@ module Mongoid
|
|
|
89
87
|
end
|
|
90
88
|
unsynced(_base, foreign_key) and self
|
|
91
89
|
end
|
|
92
|
-
# rubocop:enable Metrics/AbcSize
|
|
93
90
|
|
|
94
91
|
alias push <<
|
|
95
92
|
|
|
@@ -360,8 +357,6 @@ module Mongoid
|
|
|
360
357
|
# in bulk
|
|
361
358
|
# @param [ Array ] inserts the list of Hashes of attributes that will
|
|
362
359
|
# be inserted (corresponding to the ``docs`` list)
|
|
363
|
-
#
|
|
364
|
-
# rubocop:disable Metrics/AbcSize
|
|
365
360
|
def append_document(doc, ids, docs, inserts)
|
|
366
361
|
return unless doc
|
|
367
362
|
|
|
@@ -379,7 +374,6 @@ module Mongoid
|
|
|
379
374
|
unsynced(_base, foreign_key)
|
|
380
375
|
end
|
|
381
376
|
end
|
|
382
|
-
# rubocop:enable Metrics/AbcSize
|
|
383
377
|
end
|
|
384
378
|
end
|
|
385
379
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rubocop:todo all
|
|
3
3
|
|
|
4
|
+
require 'mongoid/pluckable'
|
|
5
|
+
|
|
4
6
|
module Mongoid
|
|
5
7
|
module Association
|
|
6
8
|
module Referenced
|
|
@@ -12,6 +14,7 @@ module Mongoid
|
|
|
12
14
|
class Enumerable
|
|
13
15
|
extend Forwardable
|
|
14
16
|
include ::Enumerable
|
|
17
|
+
include Pluckable
|
|
15
18
|
|
|
16
19
|
# The three main instance variables are collections of documents.
|
|
17
20
|
#
|
|
@@ -374,6 +377,43 @@ module Mongoid
|
|
|
374
377
|
@_added, @_loaded, @_unloaded, @executed = data
|
|
375
378
|
end
|
|
376
379
|
|
|
380
|
+
# Plucks the given field names from the documents in the target.
|
|
381
|
+
# If the collection has been loaded, it plucks from the loaded
|
|
382
|
+
# documents; otherwise, it plucks from the unloaded criteria.
|
|
383
|
+
# Regardless, it also plucks from any added documents.
|
|
384
|
+
#
|
|
385
|
+
# @param [ Symbol... ] *fields The field names to pluck.
|
|
386
|
+
#
|
|
387
|
+
# @return [ Array | Array<Array> ] The array of field values. If
|
|
388
|
+
# multiple fields are given, an array of arrays is returned.
|
|
389
|
+
def pluck(*keys)
|
|
390
|
+
[].tap do |results|
|
|
391
|
+
if _loaded? || _added.any?
|
|
392
|
+
klass = @_association.klass
|
|
393
|
+
prepared = prepare_pluck(keys, document_class: klass)
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
if _loaded?
|
|
397
|
+
docs = _loaded.values.map { |v| BSON::Document.new(v.attributes) }
|
|
398
|
+
results.concat pluck_from_documents(docs, prepared[:field_names], document_class: klass)
|
|
399
|
+
elsif _unloaded
|
|
400
|
+
criteria = if _added.any?
|
|
401
|
+
ids_to_exclude = _added.keys
|
|
402
|
+
_unloaded.not(:_id.in => ids_to_exclude)
|
|
403
|
+
else
|
|
404
|
+
_unloaded
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
results.concat criteria.pluck(*keys)
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
if _added.any?
|
|
411
|
+
docs = _added.values.map { |v| BSON::Document.new(v.attributes) }
|
|
412
|
+
results.concat pluck_from_documents(docs, prepared[:field_names], document_class: klass)
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
|
|
377
417
|
# Reset the enumerable back to its persisted state.
|
|
378
418
|
#
|
|
379
419
|
# @example Reset the enumerable.
|
|
@@ -33,7 +33,7 @@ module Mongoid
|
|
|
33
33
|
|
|
34
34
|
extend ClassMethods
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
def_delegators :criteria, :count
|
|
37
37
|
def_delegators :_target, :first, :in_memory, :last, :reset, :uniq
|
|
38
38
|
|
|
39
39
|
# Instantiate a new references_many association. Will set the foreign key
|
|
@@ -281,6 +281,22 @@ module Mongoid
|
|
|
281
281
|
|
|
282
282
|
alias nullify_all nullify
|
|
283
283
|
|
|
284
|
+
# Plucks the given field names from the documents in the
|
|
285
|
+
# association. It is safe to use whether the association is
|
|
286
|
+
# loaded or not, and whether there are unsaved documents in the
|
|
287
|
+
# association or not.
|
|
288
|
+
#
|
|
289
|
+
# @example Pluck the titles of all posts.
|
|
290
|
+
# person.posts.pluck(:title)
|
|
291
|
+
#
|
|
292
|
+
# @param [ Symbol... ] *fields The field names to pluck.
|
|
293
|
+
#
|
|
294
|
+
# @return [ Array | Array<Array> ] The array of field values. If
|
|
295
|
+
# multiple fields are given, an array of arrays is returned.
|
|
296
|
+
def pluck(*fields)
|
|
297
|
+
_target.pluck(*fields)
|
|
298
|
+
end
|
|
299
|
+
|
|
284
300
|
# Clear the association. Will delete the documents from the db if they are
|
|
285
301
|
# already persisted.
|
|
286
302
|
#
|
data/lib/mongoid/attributes.rb
CHANGED
|
@@ -175,7 +175,7 @@ module Mongoid
|
|
|
175
175
|
localized = fields[field_name].try(:localized?)
|
|
176
176
|
attributes_before_type_cast[name.to_s] = value
|
|
177
177
|
typed_value = typed_value_for(field_name, value)
|
|
178
|
-
unless
|
|
178
|
+
unless attribute_will_not_change?(field_name, typed_value) || attribute_changed?(field_name)
|
|
179
179
|
attribute_will_change!(field_name)
|
|
180
180
|
end
|
|
181
181
|
if localized
|
|
@@ -366,5 +366,23 @@ module Mongoid
|
|
|
366
366
|
end
|
|
367
367
|
value.present?
|
|
368
368
|
end
|
|
369
|
+
|
|
370
|
+
# If `value` is a `BSON::Decimal128`, convert it to a `BigDecimal` for
|
|
371
|
+
# comparison purposes. This is necessary because `BSON::Decimal128` does
|
|
372
|
+
# not implement `#==` in a way that is compatible with `BigDecimal`.
|
|
373
|
+
def normalize_value(value)
|
|
374
|
+
value.is_a?(BSON::Decimal128) ? BigDecimal(value.to_s) : value
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# Determine if the attribute will not change, by comparing the current
|
|
378
|
+
# value with the new value. The values are normalized to account for
|
|
379
|
+
# types that do not implement `#==` in a way that is compatible with
|
|
380
|
+
# each other, such as `BSON::Decimal128` and `BigDecimal`.
|
|
381
|
+
def attribute_will_not_change?(field_name, typed_value)
|
|
382
|
+
normalized_attribute = normalize_value(attributes[field_name])
|
|
383
|
+
normalized_typed_value = normalize_value(typed_value)
|
|
384
|
+
|
|
385
|
+
normalized_attribute == normalized_typed_value
|
|
386
|
+
end
|
|
369
387
|
end
|
|
370
388
|
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# rubocop:todo all
|
|
3
3
|
|
|
4
4
|
require 'mongoid/atomic_update_preparer'
|
|
5
|
+
require 'mongoid/pluckable'
|
|
5
6
|
require "mongoid/contextual/mongo/documents_loader"
|
|
6
7
|
require "mongoid/contextual/atomic"
|
|
7
8
|
require "mongoid/contextual/aggregable/mongo"
|
|
@@ -22,6 +23,7 @@ module Mongoid
|
|
|
22
23
|
include Atomic
|
|
23
24
|
include Association::EagerLoadable
|
|
24
25
|
include Queryable
|
|
26
|
+
include Pluckable
|
|
25
27
|
|
|
26
28
|
# Options constant.
|
|
27
29
|
OPTIONS = [ :hint,
|
|
@@ -331,23 +333,12 @@ module Mongoid
|
|
|
331
333
|
# in the array will be a single value. Otherwise, each
|
|
332
334
|
# result in the array will be an array of values.
|
|
333
335
|
def pluck(*fields)
|
|
334
|
-
# Multiple fields can map to the same field name. For example,
|
|
335
|
-
# a field and its _translations field map to the same
|
|
336
|
-
# because of this, we need to
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
normalized_field_names.push(db_fn)
|
|
341
|
-
hash[klass.cleanse_localized_field_names(f)] = true
|
|
342
|
-
hash
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
view.projection(normalized_select).reduce([]) do |plucked, doc|
|
|
346
|
-
values = normalized_field_names.map do |n|
|
|
347
|
-
extract_value(doc, n)
|
|
348
|
-
end
|
|
349
|
-
plucked << (values.size == 1 ? values.first : values)
|
|
350
|
-
end
|
|
336
|
+
# Multiple fields can map to the same field name. For example,
|
|
337
|
+
# plucking a field and its _translations field map to the same
|
|
338
|
+
# field in the database. because of this, we need to prepare the
|
|
339
|
+
# projection specifically.
|
|
340
|
+
prep = prepare_pluck(fields, prepare_projection: true)
|
|
341
|
+
pluck_from_documents(view.projection(prep[:projection]), prep[:field_names])
|
|
351
342
|
end
|
|
352
343
|
|
|
353
344
|
# Pick the single field values from the database.
|
|
@@ -893,78 +884,6 @@ module Mongoid
|
|
|
893
884
|
collection.write_concern.nil? || collection.write_concern.acknowledged?
|
|
894
885
|
end
|
|
895
886
|
|
|
896
|
-
# Fetch the element from the given hash and demongoize it using the
|
|
897
|
-
# given field. If the obj is an array, map over it and call this method
|
|
898
|
-
# on all of its elements.
|
|
899
|
-
#
|
|
900
|
-
# @param [ Hash | Array<Hash> ] obj The hash or array of hashes to fetch from.
|
|
901
|
-
# @param [ String ] meth The key to fetch from the hash.
|
|
902
|
-
# @param [ Field ] field The field to use for demongoization.
|
|
903
|
-
#
|
|
904
|
-
# @return [ Object ] The demongoized value.
|
|
905
|
-
#
|
|
906
|
-
# @api private
|
|
907
|
-
def fetch_and_demongoize(obj, meth, field)
|
|
908
|
-
if obj.is_a?(Array)
|
|
909
|
-
obj.map { |doc| fetch_and_demongoize(doc, meth, field) }
|
|
910
|
-
else
|
|
911
|
-
res = obj.try(:fetch, meth, nil)
|
|
912
|
-
field ? field.demongoize(res) : res.class.demongoize(res)
|
|
913
|
-
end
|
|
914
|
-
end
|
|
915
|
-
|
|
916
|
-
# Extracts the value for the given field name from the given attribute
|
|
917
|
-
# hash.
|
|
918
|
-
#
|
|
919
|
-
# @param [ Hash ] attrs The attributes hash.
|
|
920
|
-
# @param [ String ] field_name The name of the field to extract.
|
|
921
|
-
#
|
|
922
|
-
# @param [ Object ] The value for the given field name
|
|
923
|
-
def extract_value(attrs, field_name)
|
|
924
|
-
i = 1
|
|
925
|
-
num_meths = field_name.count('.') + 1
|
|
926
|
-
curr = attrs.dup
|
|
927
|
-
|
|
928
|
-
klass.traverse_association_tree(field_name) do |meth, obj, is_field|
|
|
929
|
-
field = obj if is_field
|
|
930
|
-
is_translation = false
|
|
931
|
-
# If no association or field was found, check if the meth is an
|
|
932
|
-
# _translations field.
|
|
933
|
-
if obj.nil? & tr = meth.match(/(.*)_translations\z/)&.captures&.first
|
|
934
|
-
is_translation = true
|
|
935
|
-
meth = tr
|
|
936
|
-
end
|
|
937
|
-
|
|
938
|
-
# 1. If curr is an array fetch from all elements in the array.
|
|
939
|
-
# 2. If the field is localized, and is not an _translations field
|
|
940
|
-
# (_translations fields don't show up in the fields hash).
|
|
941
|
-
# - If this is the end of the methods, return the translation for
|
|
942
|
-
# the current locale.
|
|
943
|
-
# - Otherwise, return the whole translations hash so the next method
|
|
944
|
-
# can select the language it wants.
|
|
945
|
-
# 3. If the meth is an _translations field, do not demongoize the
|
|
946
|
-
# value so the full hash is returned.
|
|
947
|
-
# 4. Otherwise, fetch and demongoize the value for the key meth.
|
|
948
|
-
curr = if curr.is_a? Array
|
|
949
|
-
res = fetch_and_demongoize(curr, meth, field)
|
|
950
|
-
res.empty? ? nil : res
|
|
951
|
-
elsif !is_translation && field&.localized?
|
|
952
|
-
if i < num_meths
|
|
953
|
-
curr.try(:fetch, meth, nil)
|
|
954
|
-
else
|
|
955
|
-
fetch_and_demongoize(curr, meth, field)
|
|
956
|
-
end
|
|
957
|
-
elsif is_translation
|
|
958
|
-
curr.try(:fetch, meth, nil)
|
|
959
|
-
else
|
|
960
|
-
fetch_and_demongoize(curr, meth, field)
|
|
961
|
-
end
|
|
962
|
-
|
|
963
|
-
i += 1
|
|
964
|
-
end
|
|
965
|
-
curr
|
|
966
|
-
end
|
|
967
|
-
|
|
968
887
|
# Recursively demongoize the given value. This method recursively traverses
|
|
969
888
|
# the class tree to find the correct field to use to demongoize the value.
|
|
970
889
|
#
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mongoid
|
|
4
|
+
# Provides shared behavior for any document with "pluck" functionality.
|
|
5
|
+
#
|
|
6
|
+
# @api private
|
|
7
|
+
module Pluckable
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
# Prepares the field names for plucking by normalizing them to their
|
|
13
|
+
# database field names. Also prepares a projection hash if requested.
|
|
14
|
+
def prepare_pluck(field_names, document_class: klass, prepare_projection: false)
|
|
15
|
+
normalized_field_names = []
|
|
16
|
+
projection = {}
|
|
17
|
+
|
|
18
|
+
field_names.each do |f|
|
|
19
|
+
db_fn = document_class.database_field_name(f)
|
|
20
|
+
normalized_field_names.push(db_fn)
|
|
21
|
+
|
|
22
|
+
next unless prepare_projection
|
|
23
|
+
|
|
24
|
+
cleaned_name = document_class.cleanse_localized_field_names(f)
|
|
25
|
+
canonical_name = document_class.database_field_name(cleaned_name)
|
|
26
|
+
projection[canonical_name] = true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
{ field_names: normalized_field_names, projection: projection }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Plucks the given field names from the given documents.
|
|
33
|
+
def pluck_from_documents(documents, field_names, document_class: klass)
|
|
34
|
+
documents.reduce([]) do |plucked, doc|
|
|
35
|
+
values = field_names.map { |name| extract_value(doc, name.to_s, document_class) }
|
|
36
|
+
plucked << ((values.size == 1) ? values.first : values)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Fetch the element from the given hash and demongoize it using the
|
|
41
|
+
# given field. If the obj is an array, map over it and call this method
|
|
42
|
+
# on all of its elements.
|
|
43
|
+
#
|
|
44
|
+
# @param [ Hash | Array<Hash> ] obj The hash or array of hashes to fetch from.
|
|
45
|
+
# @param [ String ] key The key to fetch from the hash.
|
|
46
|
+
# @param [ Field ] field The field to use for demongoization.
|
|
47
|
+
#
|
|
48
|
+
# @return [ Object ] The demongoized value.
|
|
49
|
+
def fetch_and_demongoize(obj, key, field)
|
|
50
|
+
if obj.is_a?(Array)
|
|
51
|
+
obj.map { |doc| fetch_and_demongoize(doc, key, field) }
|
|
52
|
+
else
|
|
53
|
+
value = obj.try(:fetch, key, nil)
|
|
54
|
+
field ? field.demongoize(value) : value.class.demongoize(value)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Extracts the value for the given field name from the given attribute
|
|
59
|
+
# hash.
|
|
60
|
+
#
|
|
61
|
+
# @param [ Hash ] attrs The attributes hash.
|
|
62
|
+
# @param [ String ] field_name The name of the field to extract.
|
|
63
|
+
#
|
|
64
|
+
# @return [ Object ] The value for the given field name
|
|
65
|
+
def extract_value(attrs, field_name, document_class)
|
|
66
|
+
i = 1
|
|
67
|
+
num_meths = field_name.count('.') + 1
|
|
68
|
+
curr = attrs.dup
|
|
69
|
+
|
|
70
|
+
document_class.traverse_association_tree(field_name) do |meth, obj, is_field|
|
|
71
|
+
field = obj if is_field
|
|
72
|
+
|
|
73
|
+
# use the correct document class to check for localized fields on
|
|
74
|
+
# embedded documents.
|
|
75
|
+
document_class = obj.klass if obj.respond_to?(:klass)
|
|
76
|
+
|
|
77
|
+
is_translation = false
|
|
78
|
+
# If no association or field was found, check if the meth is an
|
|
79
|
+
# _translations field.
|
|
80
|
+
if obj.nil? && (tr = meth.match(/(.*)_translations\z/)&.captures&.first)
|
|
81
|
+
is_translation = true
|
|
82
|
+
meth = document_class.database_field_name(tr)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
curr = descend(i, curr, meth, field, num_meths, is_translation)
|
|
86
|
+
|
|
87
|
+
i += 1
|
|
88
|
+
end
|
|
89
|
+
curr
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Descend one level in the attribute hash.
|
|
93
|
+
#
|
|
94
|
+
# @param [ Integer ] part The current part index.
|
|
95
|
+
# @param [ Hash | Array<Hash> ] current The current level in the attribute hash.
|
|
96
|
+
# @param [ String ] method_name The method name to descend to.
|
|
97
|
+
# @param [ Field|nil ] field The field to use for demongoization.
|
|
98
|
+
# @param [ Boolean ] is_translation Whether the method is an _translations field.
|
|
99
|
+
# @param [ Integer ] part_count The total number of parts in the field name.
|
|
100
|
+
#
|
|
101
|
+
# @return [ Object ] The value at the next level.
|
|
102
|
+
#
|
|
103
|
+
# rubocop:disable Metrics/ParameterLists
|
|
104
|
+
def descend(part, current, method_name, field, part_count, is_translation)
|
|
105
|
+
# 1. If curr is an array fetch from all elements in the array.
|
|
106
|
+
# 2. If the field is localized, and is not an _translations field
|
|
107
|
+
# (_translations fields don't show up in the fields hash).
|
|
108
|
+
# - If this is the end of the methods, return the translation for
|
|
109
|
+
# the current locale.
|
|
110
|
+
# - Otherwise, return the whole translations hash so the next method
|
|
111
|
+
# can select the language it wants.
|
|
112
|
+
# 3. If the meth is an _translations field, do not demongoize the
|
|
113
|
+
# value so the full hash is returned.
|
|
114
|
+
# 4. Otherwise, fetch and demongoize the value for the key meth.
|
|
115
|
+
if current.is_a? Array
|
|
116
|
+
res = fetch_and_demongoize(current, method_name, field)
|
|
117
|
+
res.empty? ? nil : res
|
|
118
|
+
elsif !is_translation && field&.localized?
|
|
119
|
+
if part < part_count
|
|
120
|
+
current.try(:fetch, method_name, nil)
|
|
121
|
+
else
|
|
122
|
+
fetch_and_demongoize(current, method_name, field)
|
|
123
|
+
end
|
|
124
|
+
elsif is_translation
|
|
125
|
+
current.try(:fetch, method_name, nil)
|
|
126
|
+
else
|
|
127
|
+
fetch_and_demongoize(current, method_name, field)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
# rubocop:enable Metrics/ParameterLists
|
|
131
|
+
end
|
|
132
|
+
end
|
data/lib/mongoid/traversable.rb
CHANGED
|
@@ -131,7 +131,6 @@ module Mongoid
|
|
|
131
131
|
# @param [ String ] value The discriminator key to set.
|
|
132
132
|
#
|
|
133
133
|
# @api private
|
|
134
|
-
# rubocop:disable Metrics/AbcSize
|
|
135
134
|
def discriminator_key=(value)
|
|
136
135
|
raise Errors::InvalidDiscriminatorKeyTarget.new(self, superclass) if hereditary?
|
|
137
136
|
|
|
@@ -159,7 +158,6 @@ module Mongoid
|
|
|
159
158
|
default_proc = -> { self.class.discriminator_value }
|
|
160
159
|
field(discriminator_key, default: default_proc, type: String)
|
|
161
160
|
end
|
|
162
|
-
# rubocop:enable Metrics/AbcSize
|
|
163
161
|
|
|
164
162
|
# Returns the discriminator key.
|
|
165
163
|
#
|
data/lib/mongoid/version.rb
CHANGED
|
@@ -1819,6 +1819,400 @@ describe Mongoid::Association::Referenced::HasMany::Enumerable do
|
|
|
1819
1819
|
end
|
|
1820
1820
|
end
|
|
1821
1821
|
|
|
1822
|
+
describe '#pluck' do
|
|
1823
|
+
let(:person) do
|
|
1824
|
+
Person.create!
|
|
1825
|
+
end
|
|
1826
|
+
|
|
1827
|
+
let!(:post) do
|
|
1828
|
+
Post.create!(person_id: person.id, title: 'Test Title')
|
|
1829
|
+
end
|
|
1830
|
+
|
|
1831
|
+
let(:base) { Person }
|
|
1832
|
+
let(:association) { Person.relations[:posts] }
|
|
1833
|
+
|
|
1834
|
+
let(:criteria) do
|
|
1835
|
+
Post.where(person_id: person.id)
|
|
1836
|
+
end
|
|
1837
|
+
|
|
1838
|
+
context 'when the enumerable is not loaded' do
|
|
1839
|
+
let!(:enumerable) do
|
|
1840
|
+
described_class.new(criteria, base, association)
|
|
1841
|
+
end
|
|
1842
|
+
|
|
1843
|
+
context 'when the criteria is present' do
|
|
1844
|
+
it 'delegates to the criteria pluck method' do
|
|
1845
|
+
result = enumerable.pluck(:title)
|
|
1846
|
+
expect(result).to eq(['Test Title'])
|
|
1847
|
+
end
|
|
1848
|
+
|
|
1849
|
+
context 'when added docs are present' do
|
|
1850
|
+
it 'combines the results from the criteria and the added docs' do
|
|
1851
|
+
added_post = Post.new(title: 'Added Title', person_id: person.id)
|
|
1852
|
+
enumerable << added_post
|
|
1853
|
+
|
|
1854
|
+
expect(criteria).to receive(:pluck).with(:title).and_return(['Test Title'])
|
|
1855
|
+
result = enumerable.pluck(:title)
|
|
1856
|
+
expect(result).to eq(['Test Title', 'Added Title'])
|
|
1857
|
+
end
|
|
1858
|
+
end
|
|
1859
|
+
end
|
|
1860
|
+
|
|
1861
|
+
context 'when the criteria is not present' do
|
|
1862
|
+
let(:enumerable) { described_class.new([], base, association) }
|
|
1863
|
+
|
|
1864
|
+
it 'returns nothing' do
|
|
1865
|
+
result = enumerable.pluck(:title)
|
|
1866
|
+
expect(result).to eq([])
|
|
1867
|
+
end
|
|
1868
|
+
|
|
1869
|
+
context 'when added docs are present' do
|
|
1870
|
+
it 'returns the values from the added docs' do
|
|
1871
|
+
added_post = Post.new(title: 'Added Title', person_id: person.id)
|
|
1872
|
+
enumerable << added_post
|
|
1873
|
+
|
|
1874
|
+
result = enumerable.pluck(:title)
|
|
1875
|
+
expect(result).to eq(['Added Title'])
|
|
1876
|
+
end
|
|
1877
|
+
end
|
|
1878
|
+
end
|
|
1879
|
+
end
|
|
1880
|
+
|
|
1881
|
+
context 'when the enumerable is loaded' do
|
|
1882
|
+
let(:enumerable) { described_class.new([post], base, association) }
|
|
1883
|
+
|
|
1884
|
+
it 'returns the values from the loaded documents' do
|
|
1885
|
+
result = enumerable.pluck(:title)
|
|
1886
|
+
expect(result).to eq(['Test Title'])
|
|
1887
|
+
end
|
|
1888
|
+
|
|
1889
|
+
context 'when added docs are present' do
|
|
1890
|
+
it 'returns the values from both loaded and added docs' do
|
|
1891
|
+
added_post = Post.new(title: 'Added Title', person_id: person.id)
|
|
1892
|
+
enumerable << added_post
|
|
1893
|
+
|
|
1894
|
+
result = enumerable.pluck(:title)
|
|
1895
|
+
expect(result).to eq(['Test Title', 'Added Title'])
|
|
1896
|
+
end
|
|
1897
|
+
end
|
|
1898
|
+
end
|
|
1899
|
+
end
|
|
1900
|
+
|
|
1901
|
+
describe '#pluck with aliases' do
|
|
1902
|
+
let!(:parent) do
|
|
1903
|
+
Company.create!
|
|
1904
|
+
end
|
|
1905
|
+
|
|
1906
|
+
context 'when the field is aliased' do
|
|
1907
|
+
let!(:expensive) do
|
|
1908
|
+
parent.products.create!(price: 100000)
|
|
1909
|
+
end
|
|
1910
|
+
|
|
1911
|
+
let!(:cheap) do
|
|
1912
|
+
parent.products.create!(price: 1)
|
|
1913
|
+
end
|
|
1914
|
+
|
|
1915
|
+
context 'when using alias_attribute' do
|
|
1916
|
+
|
|
1917
|
+
let(:plucked) do
|
|
1918
|
+
parent.products.pluck(:price)
|
|
1919
|
+
end
|
|
1920
|
+
|
|
1921
|
+
it 'uses the aliases' do
|
|
1922
|
+
expect(plucked).to eq([ 100000, 1 ])
|
|
1923
|
+
end
|
|
1924
|
+
end
|
|
1925
|
+
end
|
|
1926
|
+
|
|
1927
|
+
context 'when plucking a localized field' do
|
|
1928
|
+
with_default_i18n_configs
|
|
1929
|
+
|
|
1930
|
+
before do
|
|
1931
|
+
I18n.locale = :en
|
|
1932
|
+
p = parent.products.create!(name: 'english-text')
|
|
1933
|
+
I18n.locale = :de
|
|
1934
|
+
p.name = 'deutsch-text'
|
|
1935
|
+
p.save!
|
|
1936
|
+
end
|
|
1937
|
+
|
|
1938
|
+
context 'when plucking the entire field' do
|
|
1939
|
+
let(:plucked) do
|
|
1940
|
+
parent.products.all.pluck(:name)
|
|
1941
|
+
end
|
|
1942
|
+
|
|
1943
|
+
let(:plucked_translations) do
|
|
1944
|
+
parent.products.all.pluck(:name_translations)
|
|
1945
|
+
end
|
|
1946
|
+
|
|
1947
|
+
let(:plucked_translations_both) do
|
|
1948
|
+
parent.products.all.pluck(:name_translations, :name)
|
|
1949
|
+
end
|
|
1950
|
+
|
|
1951
|
+
it 'returns the demongoized translations' do
|
|
1952
|
+
expect(plucked.first).to eq('deutsch-text')
|
|
1953
|
+
end
|
|
1954
|
+
|
|
1955
|
+
it 'returns the full translations hash to _translations' do
|
|
1956
|
+
expect(plucked_translations.first).to eq({'de'=>'deutsch-text', 'en'=>'english-text'})
|
|
1957
|
+
end
|
|
1958
|
+
|
|
1959
|
+
it 'returns both' do
|
|
1960
|
+
expect(plucked_translations_both.first).to eq([{'de'=>'deutsch-text', 'en'=>'english-text'}, 'deutsch-text'])
|
|
1961
|
+
end
|
|
1962
|
+
end
|
|
1963
|
+
|
|
1964
|
+
context 'when plucking a specific locale' do
|
|
1965
|
+
|
|
1966
|
+
let(:plucked) do
|
|
1967
|
+
parent.products.all.pluck(:'name.de')
|
|
1968
|
+
end
|
|
1969
|
+
|
|
1970
|
+
it 'returns the specific translations' do
|
|
1971
|
+
expect(plucked.first).to eq('deutsch-text')
|
|
1972
|
+
end
|
|
1973
|
+
end
|
|
1974
|
+
|
|
1975
|
+
context 'when plucking a specific locale from _translations field' do
|
|
1976
|
+
|
|
1977
|
+
let(:plucked) do
|
|
1978
|
+
parent.products.all.pluck(:'name_translations.de')
|
|
1979
|
+
end
|
|
1980
|
+
|
|
1981
|
+
it 'returns the specific translations' do
|
|
1982
|
+
expect(plucked.first).to eq('deutsch-text')
|
|
1983
|
+
end
|
|
1984
|
+
end
|
|
1985
|
+
|
|
1986
|
+
context 'when fallbacks are enabled with a locale list' do
|
|
1987
|
+
require_fallbacks
|
|
1988
|
+
|
|
1989
|
+
before do
|
|
1990
|
+
I18n.fallbacks[:he] = [ :en ]
|
|
1991
|
+
end
|
|
1992
|
+
|
|
1993
|
+
let(:plucked) do
|
|
1994
|
+
parent.products.all.pluck(:name).first
|
|
1995
|
+
end
|
|
1996
|
+
|
|
1997
|
+
it 'correctly uses the fallback' do
|
|
1998
|
+
I18n.locale = :en
|
|
1999
|
+
parent.products.create!(name: 'english-text')
|
|
2000
|
+
I18n.locale = :he
|
|
2001
|
+
expect(plucked).to eq 'english-text'
|
|
2002
|
+
end
|
|
2003
|
+
end
|
|
2004
|
+
|
|
2005
|
+
context 'when the localized field is aliased' do
|
|
2006
|
+
before do
|
|
2007
|
+
I18n.locale = :en
|
|
2008
|
+
parent.products.delete_all
|
|
2009
|
+
p = parent.products.create!(name: 'ACME Rocket Skates', tagline: 'english-text')
|
|
2010
|
+
I18n.locale = :de
|
|
2011
|
+
p.tagline = 'deutsch-text'
|
|
2012
|
+
p.save!
|
|
2013
|
+
end
|
|
2014
|
+
|
|
2015
|
+
context 'when plucking the entire field' do
|
|
2016
|
+
let(:plucked) do
|
|
2017
|
+
parent.products.all.pluck(:tagline)
|
|
2018
|
+
end
|
|
2019
|
+
|
|
2020
|
+
let(:plucked_unaliased) do
|
|
2021
|
+
parent.products.all.pluck(:tl)
|
|
2022
|
+
end
|
|
2023
|
+
|
|
2024
|
+
let(:plucked_translations) do
|
|
2025
|
+
parent.products.all.pluck(:tagline_translations)
|
|
2026
|
+
end
|
|
2027
|
+
|
|
2028
|
+
let(:plucked_translations_both) do
|
|
2029
|
+
parent.products.all.pluck(:tagline_translations, :tagline)
|
|
2030
|
+
end
|
|
2031
|
+
|
|
2032
|
+
it 'returns the demongoized translations' do
|
|
2033
|
+
expect(plucked.first).to eq('deutsch-text')
|
|
2034
|
+
end
|
|
2035
|
+
|
|
2036
|
+
it 'returns the demongoized translations when unaliased' do
|
|
2037
|
+
expect(plucked_unaliased.first).to eq('deutsch-text')
|
|
2038
|
+
end
|
|
2039
|
+
|
|
2040
|
+
it 'returns the full translations hash to _translations' do
|
|
2041
|
+
expect(plucked_translations.first).to eq({ 'de' => 'deutsch-text', 'en' => 'english-text' })
|
|
2042
|
+
end
|
|
2043
|
+
|
|
2044
|
+
it 'returns both' do
|
|
2045
|
+
expect(plucked_translations_both.first).to eq([{ 'de' => 'deutsch-text', 'en' => 'english-text' }, 'deutsch-text'])
|
|
2046
|
+
end
|
|
2047
|
+
end
|
|
2048
|
+
|
|
2049
|
+
context 'when plucking a specific locale' do
|
|
2050
|
+
|
|
2051
|
+
let(:plucked) do
|
|
2052
|
+
parent.products.all.pluck(:'tagline.de')
|
|
2053
|
+
end
|
|
2054
|
+
|
|
2055
|
+
it 'returns the specific translations' do
|
|
2056
|
+
expect(plucked.first).to eq('deutsch-text')
|
|
2057
|
+
end
|
|
2058
|
+
end
|
|
2059
|
+
|
|
2060
|
+
context 'when plucking a specific locale from _translations field' do
|
|
2061
|
+
|
|
2062
|
+
let(:plucked) do
|
|
2063
|
+
parent.products.all.pluck(:'tagline_translations.de')
|
|
2064
|
+
end
|
|
2065
|
+
|
|
2066
|
+
it 'returns the specific translations' do
|
|
2067
|
+
expect(plucked.first).to eq('deutsch-text')
|
|
2068
|
+
end
|
|
2069
|
+
end
|
|
2070
|
+
|
|
2071
|
+
context 'when fallbacks are enabled with a locale list' do
|
|
2072
|
+
require_fallbacks
|
|
2073
|
+
|
|
2074
|
+
before do
|
|
2075
|
+
I18n.fallbacks[:he] = [:en]
|
|
2076
|
+
end
|
|
2077
|
+
|
|
2078
|
+
let(:plucked) do
|
|
2079
|
+
parent.products.all.pluck(:tagline).first
|
|
2080
|
+
end
|
|
2081
|
+
|
|
2082
|
+
it 'correctly uses the fallback' do
|
|
2083
|
+
I18n.locale = :en
|
|
2084
|
+
parent.products.create!(tagline: 'english-text')
|
|
2085
|
+
I18n.locale = :he
|
|
2086
|
+
expect(plucked).to eq 'english-text'
|
|
2087
|
+
end
|
|
2088
|
+
end
|
|
2089
|
+
end
|
|
2090
|
+
|
|
2091
|
+
context 'when the localized field is embedded' do
|
|
2092
|
+
with_default_i18n_configs
|
|
2093
|
+
|
|
2094
|
+
before do
|
|
2095
|
+
s = Seo.new
|
|
2096
|
+
I18n.locale = :en
|
|
2097
|
+
s.name = 'english-text'
|
|
2098
|
+
I18n.locale = :de
|
|
2099
|
+
s.name = 'deutsch-text'
|
|
2100
|
+
|
|
2101
|
+
parent.products.delete_all
|
|
2102
|
+
parent.products.create!(name: 'ACME Tunnel Paint', seo: s)
|
|
2103
|
+
end
|
|
2104
|
+
|
|
2105
|
+
let(:plucked) do
|
|
2106
|
+
parent.products.pluck('seo.name').first
|
|
2107
|
+
end
|
|
2108
|
+
|
|
2109
|
+
let(:plucked_translations) do
|
|
2110
|
+
parent.products.pluck('seo.name_translations').first
|
|
2111
|
+
end
|
|
2112
|
+
|
|
2113
|
+
let(:plucked_translations_field) do
|
|
2114
|
+
parent.products.pluck('seo.name_translations.en').first
|
|
2115
|
+
end
|
|
2116
|
+
|
|
2117
|
+
it 'returns the translation for the current locale' do
|
|
2118
|
+
expect(plucked).to eq('deutsch-text')
|
|
2119
|
+
end
|
|
2120
|
+
|
|
2121
|
+
it 'returns the full _translation hash' do
|
|
2122
|
+
expect(plucked_translations).to eq({ 'en' => 'english-text', 'de' => 'deutsch-text' })
|
|
2123
|
+
end
|
|
2124
|
+
|
|
2125
|
+
it 'returns the translation for the requested locale' do
|
|
2126
|
+
expect(plucked_translations_field).to eq('english-text')
|
|
2127
|
+
end
|
|
2128
|
+
end
|
|
2129
|
+
end
|
|
2130
|
+
|
|
2131
|
+
context 'when the localized field is embedded and aliased' do
|
|
2132
|
+
with_default_i18n_configs
|
|
2133
|
+
|
|
2134
|
+
before do
|
|
2135
|
+
s = Seo.new
|
|
2136
|
+
I18n.locale = :en
|
|
2137
|
+
s.description = 'english-text'
|
|
2138
|
+
I18n.locale = :de
|
|
2139
|
+
s.description = 'deutsch-text'
|
|
2140
|
+
|
|
2141
|
+
parent.products.delete_all
|
|
2142
|
+
parent.products.create!(name: 'ACME Tunnel Paint', seo: s)
|
|
2143
|
+
end
|
|
2144
|
+
|
|
2145
|
+
let(:plucked) do
|
|
2146
|
+
parent.products.pluck('seo.description').first
|
|
2147
|
+
end
|
|
2148
|
+
|
|
2149
|
+
let(:plucked_unaliased) do
|
|
2150
|
+
parent.products.pluck('seo.desc').first
|
|
2151
|
+
end
|
|
2152
|
+
|
|
2153
|
+
let(:plucked_translations) do
|
|
2154
|
+
parent.products.pluck('seo.description_translations').first
|
|
2155
|
+
end
|
|
2156
|
+
|
|
2157
|
+
let(:plucked_translations_field) do
|
|
2158
|
+
parent.products.pluck('seo.description_translations.en').first
|
|
2159
|
+
end
|
|
2160
|
+
|
|
2161
|
+
it 'returns the translation for the current locale' do
|
|
2162
|
+
I18n.with_locale(:en) do
|
|
2163
|
+
expect(plucked).to eq('english-text')
|
|
2164
|
+
end
|
|
2165
|
+
end
|
|
2166
|
+
|
|
2167
|
+
it 'returns the translation for the current locale when unaliased' do
|
|
2168
|
+
I18n.with_locale(:en) do
|
|
2169
|
+
expect(plucked_unaliased).to eq('english-text')
|
|
2170
|
+
end
|
|
2171
|
+
end
|
|
2172
|
+
|
|
2173
|
+
it 'returns the full _translation hash' do
|
|
2174
|
+
expect(plucked_translations).to eq({ 'en' => 'english-text', 'de' => 'deutsch-text' })
|
|
2175
|
+
end
|
|
2176
|
+
|
|
2177
|
+
it 'returns the translation for the requested locale' do
|
|
2178
|
+
expect(plucked_translations_field).to eq('english-text')
|
|
2179
|
+
end
|
|
2180
|
+
end
|
|
2181
|
+
|
|
2182
|
+
context 'when plucking an embedded field' do
|
|
2183
|
+
let(:label) { Label.new(sales: '1E2') }
|
|
2184
|
+
let!(:band) { Band.create!(label: label) }
|
|
2185
|
+
|
|
2186
|
+
let(:plucked) { Band.where(_id: band.id).pluck('label.sales') }
|
|
2187
|
+
|
|
2188
|
+
it 'demongoizes the field' do
|
|
2189
|
+
expect(plucked).to eq([ BigDecimal('1E2') ])
|
|
2190
|
+
end
|
|
2191
|
+
end
|
|
2192
|
+
|
|
2193
|
+
context 'when plucking an embeds_many field' do
|
|
2194
|
+
let(:label) { Label.new(sales: '1E2') }
|
|
2195
|
+
let!(:band) { Band.create!(labels: [label]) }
|
|
2196
|
+
|
|
2197
|
+
let(:plucked) { Band.where(_id: band.id).pluck('labels.sales') }
|
|
2198
|
+
|
|
2199
|
+
it 'demongoizes the field' do
|
|
2200
|
+
expect(plucked.first).to eq([ BigDecimal('1E2') ])
|
|
2201
|
+
end
|
|
2202
|
+
end
|
|
2203
|
+
|
|
2204
|
+
context 'when plucking a nonexistent embedded field' do
|
|
2205
|
+
let(:label) { Label.new(sales: '1E2') }
|
|
2206
|
+
let!(:band) { Band.create!(label: label) }
|
|
2207
|
+
|
|
2208
|
+
let(:plucked) { Band.where(_id: band.id).pluck('label.qwerty') }
|
|
2209
|
+
|
|
2210
|
+
it 'returns nil' do
|
|
2211
|
+
expect(plucked.first).to eq(nil)
|
|
2212
|
+
end
|
|
2213
|
+
end
|
|
2214
|
+
end
|
|
2215
|
+
|
|
1822
2216
|
describe "#reset" do
|
|
1823
2217
|
|
|
1824
2218
|
let(:person) do
|
|
@@ -1729,6 +1729,19 @@ describe Mongoid::Attributes do
|
|
|
1729
1729
|
end
|
|
1730
1730
|
end
|
|
1731
1731
|
end
|
|
1732
|
+
|
|
1733
|
+
context 'when map_big_decimal_to_decimal128 is enabled' do
|
|
1734
|
+
config_override :map_big_decimal_to_decimal128, true
|
|
1735
|
+
|
|
1736
|
+
context 'when writing an identical number' do
|
|
1737
|
+
let(:band) { Band.create!(name: 'Nirvana', sales: 123456.78).reload }
|
|
1738
|
+
|
|
1739
|
+
it 'does not mark the document as changed' do
|
|
1740
|
+
band.sales = 123456.78
|
|
1741
|
+
expect(band.changed?).to be false
|
|
1742
|
+
end
|
|
1743
|
+
end
|
|
1744
|
+
end
|
|
1732
1745
|
end
|
|
1733
1746
|
|
|
1734
1747
|
describe "#typed_value_for" do
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Candidate Tasks
|
|
2
|
+
|
|
3
|
+
When using the `candidate` rake tasks, you must make sure:
|
|
4
|
+
|
|
5
|
+
1. You are using at least `git` version 2.49.0.
|
|
6
|
+
2. You have the `gh` CLI tool installed.
|
|
7
|
+
3. You are logged into `gh` with an account that has collaborator access to the repository.
|
|
8
|
+
4. You have run `gh repo set-default` from the root of your local checkout to set the default repository to the canonical MongoDB repo.
|
|
9
|
+
5. The `origin` remote for your local checkout is set to your own fork.
|
|
10
|
+
6. The `upstream` remote for your local checkout is set to the canonical
|
|
11
|
+
MongoDB repo.
|
|
12
|
+
|
|
13
|
+
Once configured, you can use the following commands:
|
|
14
|
+
|
|
15
|
+
1. `rake candidate:prs` - This will list all pull requests that will be included in the next release. Any with `[?]` are unlabelled (or are not labelled with a recognized label). Otherwise, `[b]` means `bug`, `[f]` means `feature`, and `[x]` means `bcbreak`.
|
|
16
|
+
2. `rake candidate:preview` - This will generate and display the release notes for the next release, based on the associated pull requests.
|
|
17
|
+
3. `rake candidate:create` - This will create a new PR against the default repository, using the generated release notes as the description. The new PR will be given the `release-candidate` label.
|
|
18
|
+
|
|
19
|
+
Then, after the release candidate PR is approved and merged, the release process will automatically bundle, sign, and release the new version.
|
|
20
|
+
|
|
21
|
+
Once you've merged the PR, you can switch to the "Actions" tab for the repository on GitHub and look for the "Release" workflow (might be named differently), which should have triggered automatically. You can monitor the progress of the release there. If there are any problems, the workflow is generally safe to re-run after you've addressed them.
|
|
22
|
+
|
|
23
|
+
Things to do after the release succeeds:
|
|
24
|
+
|
|
25
|
+
1. Copy the release notes from the PR and create a new release announcement on the forums (https://www.mongodb.com/community/forums/c/announcements/driver-releases/110).
|
|
26
|
+
2. If the release was not automatically announced in #ruby, copy a link to the GitHub release or MongoDB forum post there.
|
|
27
|
+
3. Close the release in Jira.
|
|
28
|
+
|
|
@@ -25,19 +25,19 @@ module Mrss
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def initialize(root: nil, classifiers:, priority_order:,
|
|
28
|
-
spec_root: nil, rspec_json_path: nil, rspec_all_json_path: nil,
|
|
29
|
-
randomize: false
|
|
28
|
+
spec_root: nil, rspec_json_path: nil, rspec_all_json_path: nil, rspec_xml_path: nil, randomize: false
|
|
30
29
|
)
|
|
31
30
|
@spec_root = spec_root || File.join(root, 'spec')
|
|
32
31
|
@classifiers = classifiers
|
|
33
32
|
@priority_order = priority_order
|
|
34
33
|
@rspec_json_path = rspec_json_path || File.join(root, 'tmp/rspec.json')
|
|
35
34
|
@rspec_all_json_path = rspec_all_json_path || File.join(root, 'tmp/rspec-all.json')
|
|
35
|
+
@rspec_xml_path = rspec_xml_path || File.join(root, 'tmp/rspec.xml')
|
|
36
36
|
@randomize = !!randomize
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
attr_reader :spec_root, :classifiers, :priority_order
|
|
40
|
-
attr_reader :rspec_json_path, :rspec_all_json_path
|
|
40
|
+
attr_reader :rspec_json_path, :rspec_all_json_path, :rspec_xml_path
|
|
41
41
|
|
|
42
42
|
def randomize?
|
|
43
43
|
@randomize
|
|
@@ -47,6 +47,25 @@ module Mrss
|
|
|
47
47
|
@seed ||= (rand * 100_000).to_i
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
# Remove all XML files from tmp directory before running tests
|
|
51
|
+
def cleanup_xml_files
|
|
52
|
+
xml_pattern = File.join(File.dirname(rspec_xml_path), '*.xml')
|
|
53
|
+
Dir.glob(xml_pattern).each do |xml_file|
|
|
54
|
+
FileUtils.rm_f(xml_file)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Move the XML file to a timestamped version for evergreen upload
|
|
59
|
+
def archive_xml_file(category)
|
|
60
|
+
return unless File.exist?(rspec_xml_path)
|
|
61
|
+
|
|
62
|
+
timestamp = Time.now.strftime('%Y%m%d_%H%M%S_%3N')
|
|
63
|
+
archived_path = rspec_xml_path.sub(/\.xml$/, "-#{category}-#{timestamp}.xml")
|
|
64
|
+
|
|
65
|
+
FileUtils.mv(rspec_xml_path, archived_path)
|
|
66
|
+
puts "Archived XML results to #{archived_path}"
|
|
67
|
+
end
|
|
68
|
+
|
|
50
69
|
def buckets
|
|
51
70
|
@buckets ||= {}.tap do |buckets|
|
|
52
71
|
Find.find(spec_root) do |path|
|
|
@@ -96,6 +115,8 @@ module Mrss
|
|
|
96
115
|
|
|
97
116
|
def run_buckets(*buckets)
|
|
98
117
|
FileUtils.rm_f(rspec_all_json_path)
|
|
118
|
+
# Clean up all XML files before starting test runs
|
|
119
|
+
cleanup_xml_files
|
|
99
120
|
|
|
100
121
|
buckets.each do |bucket|
|
|
101
122
|
if bucket && !self.buckets[bucket]
|
|
@@ -131,7 +152,12 @@ module Mrss
|
|
|
131
152
|
def run_files(category, paths)
|
|
132
153
|
puts "Running #{category.to_s.gsub('_', ' ')} tests"
|
|
133
154
|
FileUtils.rm_f(rspec_json_path)
|
|
155
|
+
FileUtils.rm_f(rspec_xml_path) # Clean up XML file before running this bucket
|
|
156
|
+
|
|
134
157
|
cmd = %w(rspec) + paths
|
|
158
|
+
# Add junit formatter for XML output
|
|
159
|
+
cmd += ['--format', 'Rfc::Riff', '--format', 'RspecJunitFormatter', '--out', rspec_xml_path]
|
|
160
|
+
|
|
135
161
|
if randomize?
|
|
136
162
|
cmd += %W(--order rand:#{seed})
|
|
137
163
|
end
|
|
@@ -147,6 +173,9 @@ module Mrss
|
|
|
147
173
|
FileUtils.cp(rspec_json_path, rspec_all_json_path)
|
|
148
174
|
end
|
|
149
175
|
end
|
|
176
|
+
|
|
177
|
+
# Archive XML file after running this bucket
|
|
178
|
+
archive_xml_file(category)
|
|
150
179
|
end
|
|
151
180
|
|
|
152
181
|
true
|
data/spec/shared/shlib/server.sh
CHANGED
|
@@ -78,7 +78,7 @@ install_mlaunch_venv() {
|
|
|
78
78
|
# Debian11/Ubuntu2204 have venv installed, but it is nonfunctional unless
|
|
79
79
|
# the python3-venv package is also installed (it lacks the ensurepip
|
|
80
80
|
# module).
|
|
81
|
-
sudo apt-get install --yes python3-venv
|
|
81
|
+
sudo apt-get update && sudo apt-get install --yes python3-venv
|
|
82
82
|
fi
|
|
83
83
|
if test "$USE_SYSTEM_PYTHON_PACKAGES" = 1 &&
|
|
84
84
|
python3 -m pip list |grep mtools
|
data/spec/support/models/seo.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mongoid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 9.0.
|
|
4
|
+
version: 9.0.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- The MongoDB Ruby Team
|
|
@@ -18,7 +18,7 @@ dependencies:
|
|
|
18
18
|
version: '5.1'
|
|
19
19
|
- - "<"
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version: '8.
|
|
21
|
+
version: '8.2'
|
|
22
22
|
- - "!="
|
|
23
23
|
- !ruby/object:Gem::Version
|
|
24
24
|
version: 7.0.0
|
|
@@ -31,7 +31,7 @@ dependencies:
|
|
|
31
31
|
version: '5.1'
|
|
32
32
|
- - "<"
|
|
33
33
|
- !ruby/object:Gem::Version
|
|
34
|
-
version: '8.
|
|
34
|
+
version: '8.2'
|
|
35
35
|
- - "!="
|
|
36
36
|
- !ruby/object:Gem::Version
|
|
37
37
|
version: 7.0.0
|
|
@@ -437,6 +437,7 @@ files:
|
|
|
437
437
|
- lib/mongoid/persistable/updatable.rb
|
|
438
438
|
- lib/mongoid/persistable/upsertable.rb
|
|
439
439
|
- lib/mongoid/persistence_context.rb
|
|
440
|
+
- lib/mongoid/pluckable.rb
|
|
440
441
|
- lib/mongoid/positional.rb
|
|
441
442
|
- lib/mongoid/railtie.rb
|
|
442
443
|
- lib/mongoid/railties/bson_object_id_serializer.rb
|
|
@@ -895,6 +896,7 @@ files:
|
|
|
895
896
|
- spec/mongoid_spec.rb
|
|
896
897
|
- spec/rails/controller_extension/controller_runtime_spec.rb
|
|
897
898
|
- spec/rails/mongoid_spec.rb
|
|
899
|
+
- spec/shared/CANDIDATE.md
|
|
898
900
|
- spec/shared/LICENSE
|
|
899
901
|
- spec/shared/bin/get-mongodb-download-url
|
|
900
902
|
- spec/shared/bin/s3-copy
|
|
@@ -1229,7 +1231,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
1229
1231
|
- !ruby/object:Gem::Version
|
|
1230
1232
|
version: 1.3.6
|
|
1231
1233
|
requirements: []
|
|
1232
|
-
rubygems_version:
|
|
1234
|
+
rubygems_version: 4.0.2
|
|
1233
1235
|
specification_version: 4
|
|
1234
1236
|
summary: Elegant Persistence in Ruby for MongoDB.
|
|
1235
1237
|
test_files:
|
|
@@ -1642,6 +1644,7 @@ test_files:
|
|
|
1642
1644
|
- spec/mongoid_spec.rb
|
|
1643
1645
|
- spec/rails/controller_extension/controller_runtime_spec.rb
|
|
1644
1646
|
- spec/rails/mongoid_spec.rb
|
|
1647
|
+
- spec/shared/CANDIDATE.md
|
|
1645
1648
|
- spec/shared/LICENSE
|
|
1646
1649
|
- spec/shared/bin/get-mongodb-download-url
|
|
1647
1650
|
- spec/shared/bin/s3-copy
|