mongoid 8.1.10 → 8.1.11

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +9 -9
  3. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +1 -1
  4. data/lib/mongoid/contextual/mongo.rb +1 -1
  5. data/lib/mongoid/validatable/associated.rb +1 -1
  6. data/lib/mongoid/validatable/macros.rb +15 -0
  7. data/lib/mongoid/validatable/numericality.rb +19 -0
  8. data/lib/mongoid/validatable.rb +1 -0
  9. data/lib/mongoid/version.rb +5 -1
  10. data/spec/integration/app_spec.rb +6 -0
  11. data/spec/integration/associations/embeds_one_spec.rb +25 -5
  12. data/spec/mongoid/association_spec.rb +0 -60
  13. data/spec/mongoid/contextual/mongo_spec.rb +6 -0
  14. data/spec/mongoid/validatable/numericality_spec.rb +16 -0
  15. data/spec/shared/LICENSE +20 -0
  16. data/spec/shared/bin/get-mongodb-download-url +17 -0
  17. data/spec/shared/bin/s3-copy +45 -0
  18. data/spec/shared/bin/s3-upload +69 -0
  19. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  20. data/spec/shared/lib/mrss/cluster_config.rb +231 -0
  21. data/spec/shared/lib/mrss/constraints.rb +378 -0
  22. data/spec/shared/lib/mrss/docker_runner.rb +298 -0
  23. data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
  24. data/spec/shared/lib/mrss/event_subscriber.rb +210 -0
  25. data/spec/shared/lib/mrss/lite_constraints.rb +238 -0
  26. data/spec/shared/lib/mrss/release/candidate.rb +281 -0
  27. data/spec/shared/lib/mrss/release/product_data.rb +144 -0
  28. data/spec/shared/lib/mrss/server_version_registry.rb +113 -0
  29. data/spec/shared/lib/mrss/session_registry.rb +69 -0
  30. data/spec/shared/lib/mrss/session_registry_legacy.rb +60 -0
  31. data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
  32. data/spec/shared/lib/mrss/utils.rb +37 -0
  33. data/spec/shared/lib/tasks/candidate.rake +64 -0
  34. data/spec/shared/share/Dockerfile.erb +251 -0
  35. data/spec/shared/share/haproxy-1.conf +16 -0
  36. data/spec/shared/share/haproxy-2.conf +17 -0
  37. data/spec/shared/shlib/config.sh +27 -0
  38. data/spec/shared/shlib/distro.sh +84 -0
  39. data/spec/shared/shlib/server.sh +423 -0
  40. data/spec/shared/shlib/set_env.sh +110 -0
  41. data/spec/support/expectations.rb +20 -17
  42. metadata +58 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0937a2d421f6fde901ea6cf6b84f44f3e0881203d907467bb4be6d29af0294be'
4
- data.tar.gz: 603cbd46675272938b68ffa596d5cc49f0c2e60b1fe8d09f79100a67b1d06364
3
+ metadata.gz: 21db50efa946dce1a9e43718d0e15d37c0e4373c9d693123d21cfe13b8afb3bf
4
+ data.tar.gz: 7d01ee5d5a8eb3c81a92c3055e94bf8bb90a8a911258401459b5e447c45a220c
5
5
  SHA512:
6
- metadata.gz: 1a82445002bf56d4751a0b808a8aa5ff196ff92d057d6fa4230db17819b365df7e0c3fe9abd46ec063c7575e761cdef921d3682c3e37e7105b9e5bd04310e8fa
7
- data.tar.gz: 443a3ce79311113369b4adf96302fc457ef6ae69643ab42845b0ee448f61c946820828137b3d6fbfb121386347cfd15ba129c7ded504f43c7ba3a980f7caf3aa
6
+ metadata.gz: f1e10a0cb03863509acb1d150460e4f7c475092b45bb65f7619ecd8f0384d4ed6e92416565a07e6e900af26fbf26b32c4cd2c9418c4dfbe1f858da451a3e9378
7
+ data.tar.gz: e2e75603f8ba8f3debead96161f81350ced4696aac125ba0b17f48a94a42cc48e867651deb1b6a60c3d57f48fa237f26ac61775f3c2be3750f62fc2b66d27661
data/Rakefile CHANGED
@@ -10,16 +10,16 @@ $: << File.join(ROOT, 'spec/shared/lib')
10
10
  require "rake"
11
11
  require "rspec/core/rake_task"
12
12
 
13
- # stands in for the Bundler-provided `build` task, which builds the
14
- # gem for this project. Our release process builds the gems in a
15
- # particular way, in a GitHub action. This task is just to help remind
16
- # developers of that fact.
13
+ if File.exist?('./spec/shared/lib/tasks/candidate.rake')
14
+ load 'spec/shared/lib/tasks/candidate.rake'
15
+ end
16
+
17
+ desc 'Build the gem'
17
18
  task :build do
18
- abort <<~WARNING
19
- `rake build` does nothing in this project. The gem must be built via
20
- the `Mongoid Release` action on GitHub, which is triggered manually when
21
- a new release is ready.
22
- WARNING
19
+ command = %w[ gem build ]
20
+ command << "--output=#{ENV['GEM_FILE_NAME']}" if ENV['GEM_FILE_NAME']
21
+ command << (ENV['GEMSPEC'] || 'mongoid.gemspec')
22
+ system(*command)
23
23
  end
24
24
 
25
25
  # `rake version` is used by the deployment system so get the release version
@@ -32,8 +32,8 @@ module Mongoid
32
32
  bind_one
33
33
  characterize_one(_target)
34
34
  update_attributes_hash(_target)
35
- _base._reset_memoized_descendants!
36
35
  _target.save if persistable?
36
+ _base._reset_memoized_descendants!
37
37
  end
38
38
  end
39
39
 
@@ -1091,7 +1091,7 @@ module Mongoid
1091
1091
  end
1092
1092
 
1093
1093
  def retrieve_nth_to_last_with_limit(n, limit)
1094
- v = view.sort(inverse_sorting).skip(n).limit(limit || 1)
1094
+ v = view.sort(inverse_sorting).limit(limit || 1)
1095
1095
  v = v.skip(n) if n > 0
1096
1096
  raw_docs = v.to_a.reverse
1097
1097
  process_raw_docs(raw_docs, limit)
@@ -73,7 +73,7 @@ module Mongoid
73
73
  # use map.all? instead of just all?, because all? will do short-circuit
74
74
  # evaluation and terminate on the first failed validation.
75
75
  list.map do |value|
76
- if value && !value.flagged_for_destroy?
76
+ if value && !value.flagged_for_destroy? && (!value.persisted? || value.changed?)
77
77
  value.validated? ? true : value.valid?
78
78
  else
79
79
  true
@@ -84,6 +84,21 @@ module Mongoid
84
84
  def validates_presence_of(*args)
85
85
  validates_with(PresenceValidator, _merge_attributes(args))
86
86
  end
87
+
88
+ # Validates whether or not a field contains a numeric value.
89
+ #
90
+ # @example
91
+ # class Person
92
+ # include Mongoid::Document
93
+ # field :cost
94
+ #
95
+ # validates_numericality_of :cost
96
+ # end
97
+ #
98
+ # @param [ Object... ] *args The names of the field(s) to validate.
99
+ def validates_numericality_of(*args)
100
+ validates_with(NumericalityValidator, _merge_attributes(args))
101
+ end
87
102
  end
88
103
  end
89
104
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+ module Validatable
5
+ # A specialization of the ActiveModel numericality validator, which adds
6
+ # logic to recognize and accept BSON::Decimal128 as a number.
7
+ class NumericalityValidator < ActiveModel::Validations::NumericalityValidator
8
+ private
9
+
10
+ # Ensure that BSON::Decimal128 is treated as a BigDecimal during the
11
+ # validation step.
12
+ def prepare_value_for_validation(value, record, attr_name)
13
+ result = super
14
+
15
+ result.is_a?(BSON::Decimal128) ? result.to_big_decimal : result
16
+ end
17
+ end
18
+ end
19
+ end
@@ -5,6 +5,7 @@ require "mongoid/validatable/localizable"
5
5
  require "mongoid/validatable/associated"
6
6
  require "mongoid/validatable/format"
7
7
  require "mongoid/validatable/length"
8
+ require "mongoid/validatable/numericality"
8
9
  require "mongoid/validatable/queryable"
9
10
  require "mongoid/validatable/presence"
10
11
  require "mongoid/validatable/uniqueness"
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mongoid
4
- VERSION = "8.1.10"
4
+ # The current version of Mongoid
5
+ #
6
+ # Note that this file is automatically updated via `rake candidate:create`.
7
+ # Manual changes to this file will be overwritten by that rake task.
8
+ VERSION = '8.1.11'
5
9
  end
@@ -95,6 +95,12 @@ describe 'Mongoid application tests' do
95
95
  end
96
96
 
97
97
  context 'new application - rails' do
98
+ before(:all) do
99
+ if SpecConfig.instance.rails_version < '7.1'
100
+ skip '`rails new` with rails < 7.1 fails because modern concurrent-ruby removed logger dependency'
101
+ end
102
+ end
103
+
98
104
  it 'creates' do
99
105
  install_rails
100
106
 
@@ -3,8 +3,7 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe 'embeds_one associations' do
6
-
7
- context 're-associating the same object' do
6
+ context 'when re-associating the same object' do
8
7
  context 'with dependent: destroy' do
9
8
  let(:canvas) do
10
9
  Canvas.create!(palette: Palette.new)
@@ -16,7 +15,7 @@ describe 'embeds_one associations' do
16
15
  canvas.palette = canvas.palette
17
16
  canvas.save!
18
17
  canvas.reload
19
- canvas.palette.should == palette
18
+ expect(canvas.palette).to eq palette
20
19
  end
21
20
  end
22
21
  end
@@ -30,12 +29,33 @@ describe 'embeds_one associations' do
30
29
  end
31
30
 
32
31
  it 'loads the association correctly' do
33
- expect { klass }.to_not raise_error
34
- expect { klass.new.address }.to_not raise_error
32
+ expect { klass }.not_to raise_error
33
+ expect { klass.new.address }.not_to raise_error
35
34
  instance = klass.new
36
35
  address = Address.new
37
36
  instance.address = address
38
37
  expect(instance.address).to eq address
39
38
  end
40
39
  end
40
+
41
+ context 'when parent is persisted' do
42
+ let!(:person) do
43
+ Person.create!
44
+ end
45
+
46
+ context 'when assigning the new child' do
47
+ context 'when assigning an attribute to the child' do
48
+ before do
49
+ # person.reload
50
+ person.name = Name.new
51
+ person.name.first_name = 'Dmitry'
52
+ person.save!
53
+ end
54
+
55
+ it 'persists the child' do
56
+ expect(person.reload.name.first_name).to eq 'Dmitry'
57
+ end
58
+ end
59
+ end
60
+ end
41
61
  end
@@ -100,66 +100,6 @@ describe Mongoid::Association do
100
100
  expect(name).to_not be_an_embedded_many
101
101
  end
102
102
  end
103
-
104
- context "when validation depends on association" do
105
- before(:all) do
106
- class Author
107
- include Mongoid::Document
108
- embeds_many :books, cascade_callbacks: true
109
- field :condition, type: Boolean
110
- end
111
-
112
- class Book
113
- include Mongoid::Document
114
- embedded_in :author
115
- validate :parent_condition_is_not_true
116
-
117
- def parent_condition_is_not_true
118
- return unless author&.condition
119
- errors.add :base, "Author condition is true."
120
- end
121
- end
122
-
123
- Author.delete_all
124
- Book.delete_all
125
- end
126
-
127
- let(:author) { Author.new }
128
- let(:book) { Book.new }
129
-
130
- context "when author is not persisted" do
131
- it "is valid without books" do
132
- expect(author.valid?).to be true
133
- end
134
-
135
- it "is valid with a book" do
136
- author.books << book
137
- expect(author.valid?).to be true
138
- end
139
-
140
- it "is not valid when condition is true with a book" do
141
- author.condition = true
142
- author.books << book
143
- expect(author.valid?).to be false
144
- end
145
- end
146
-
147
- context "when author is persisted" do
148
- before do
149
- author.books << book
150
- author.save
151
- end
152
-
153
- it "remains valid initially" do
154
- expect(author.valid?).to be true
155
- end
156
-
157
- it "becomes invalid when condition is set to true" do
158
- author.update_attributes(condition: true)
159
- expect(author.valid?).to be false
160
- end
161
- end
162
- end
163
103
  end
164
104
 
165
105
  describe "#embedded_one?" do
@@ -3387,6 +3387,12 @@ describe Mongoid::Contextual::Mongo do
3387
3387
  it "limits the results" do
3388
3388
  expect(context.skip(1).entries).to eq([ new_order ])
3389
3389
  end
3390
+
3391
+ context "with #last" do
3392
+ it "returns the nth from last element" do
3393
+ expect(context.skip(1).last).to eq(depeche_mode)
3394
+ end
3395
+ end
3390
3396
  end
3391
3397
 
3392
3398
  describe "#sort" do
@@ -28,5 +28,21 @@ describe ActiveModel::Validations::NumericalityValidator do
28
28
  expect(model).to_not be_valid
29
29
  end
30
30
  end
31
+
32
+ context 'when the value is numeric' do
33
+ let(:model) { TestModel.new(amount: '15.0') }
34
+
35
+ it 'returns true' do
36
+ expect(model).to be_valid
37
+ end
38
+ end
39
+
40
+ context 'when the value is a BSON::Decimal128' do
41
+ let(:model) { TestModel.new(amount: BSON::Decimal128.new('15.0')) }
42
+
43
+ it 'returns true' do
44
+ expect(model).to be_valid
45
+ end
46
+ end
31
47
  end
32
48
  end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2020 MongoDB, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ desired_version, arch = ARGV
4
+ if arch.nil?
5
+ STDERR.puts "Usage: get-mongodb-download-url desired-version arch"
6
+ exit 1
7
+ end
8
+
9
+ $: << File.join(File.dirname(__FILE__), '../lib')
10
+ require 'mrss/server_version_registry'
11
+
12
+ begin
13
+ puts Mrss::ServerVersionRegistry.new(desired_version, arch).download_url
14
+ rescue Mrss::ServerVersionRegistry::Error => exc
15
+ STDERR.puts "Error: #{exc}"
16
+ exit 2
17
+ end
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'aws-sdk-s3'
5
+
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: s3-copy options"
9
+
10
+ opts.on("-r", "--region=REGION", "AWS region to use (default us-east-1)") do |v|
11
+ options[:region] = v
12
+ end
13
+
14
+ opts.on("-p", "--param=KEY=VALUE", "Specify parameter for new files") do |v|
15
+ options[:params] ||= {}
16
+ k, v = v.split('=', 2)
17
+ options[:params][k.to_sym] = v
18
+ end
19
+
20
+ opts.on("-f", "--from=BUCKET:PATH", "Bucket name and key (or path) to copy from") do |v|
21
+ options[:from] = v
22
+ end
23
+
24
+ opts.on("-t", "--to=BUCKET:PATH", "Bucket name and key (or path) to write to (may be specified more than once)") do |v|
25
+ options[:to] ||= []
26
+ options[:to] << v
27
+ end
28
+ end.parse!
29
+
30
+ ENV['AWS_REGION'] ||= options[:region] || 'us-east-1'
31
+
32
+ bucket, key = options.fetch(:from).split(':', 2)
33
+
34
+ s3 = Aws::S3::Client.new
35
+
36
+ options.fetch(:to).each do |dest|
37
+ STDERR.puts "Copying to #{dest}"
38
+ dbucket, dkey = dest.split(':', 2)
39
+ s3.copy_object(
40
+ bucket: dbucket,
41
+ key: dkey,
42
+ copy_source: "/#{bucket}/#{key}",
43
+ **options[:params] || {},
44
+ )
45
+ end
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'aws-sdk-s3'
5
+
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: s3-upload options"
9
+
10
+ opts.on("-r", "--region=REGION", "AWS region to use (default us-east-1)") do |v|
11
+ options[:region] = v
12
+ end
13
+
14
+ opts.on("-p", "--param=KEY=VALUE", "Specify parameter for S3 upload") do |v|
15
+ options[:params] ||= {}
16
+ k, v = v.split('=', 2)
17
+ options[:params][k.to_sym] = v
18
+ end
19
+
20
+ opts.on("-f", "--file=PATH", "Path to the file to upload, - to upload standard input") do |v|
21
+ options[:file] = v
22
+ end
23
+
24
+ opts.on("-w", "--write=BUCKET:PATH", "Bucket name and key (or path) to upload to") do |v|
25
+ options[:write] = v
26
+ end
27
+
28
+ opts.on("-c", "--copy=BUCKET:PATH", "Bucket name and key (or path) to copy to (may be specified more than once)") do |v|
29
+ options[:copy] ||= []
30
+ options[:copy] << v
31
+ end
32
+ end.parse!
33
+
34
+ ENV['AWS_REGION'] ||= options[:region] || 'us-east-1'
35
+
36
+ def upload(f, options)
37
+ s3 = Aws::S3::Client.new
38
+ write = options.fetch(:write)
39
+ STDERR.puts "Writing #{write}"
40
+ bucket, key = write.split(':', 2)
41
+ s3.put_object(
42
+ body: f.read,
43
+ bucket: bucket,
44
+ key: key,
45
+ **options[:params] || {},
46
+ )
47
+ if copy = options[:copy]
48
+ copy.each do |dest|
49
+ STDERR.puts "Copying to #{dest}"
50
+ dbucket, dkey = dest.split(':', 2)
51
+ s3.copy_object(
52
+ bucket: dbucket,
53
+ key: dkey,
54
+ copy_source: "/#{bucket}/#{key}",
55
+ **options[:params] || {},
56
+ )
57
+ end
58
+ end
59
+ end
60
+
61
+ if options[:file] == '-'
62
+ upload(STDIN, options)
63
+ elsif options[:file]
64
+ File.open(options[:file]) do |f|
65
+ upload(f, options)
66
+ end
67
+ else
68
+ upload(STDIN, options)
69
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ autoload :ChildProcess, 'childprocess'
5
+ autoload :Tempfile, 'tempfile'
6
+
7
+ module Mrss
8
+ module ChildProcessHelper
9
+ class SpawnError < StandardError; end
10
+
11
+ module_function def call(cmd, env: nil, cwd: nil)
12
+ process = ChildProcess.new(*cmd)
13
+ process.io.inherit!
14
+ if cwd
15
+ process.cwd = cwd
16
+ end
17
+ if env
18
+ env.each do |k, v|
19
+ process.environment[k.to_s] = v
20
+ end
21
+ end
22
+ process.start
23
+ process.wait
24
+ process
25
+ end
26
+
27
+ module_function def check_call(cmd, env: nil, cwd: nil)
28
+ process = call(cmd, env: env, cwd: cwd)
29
+ unless process.exit_code == 0
30
+ raise SpawnError, "Failed to execute: #{cmd}"
31
+ end
32
+ end
33
+
34
+ module_function def get_output(cmd, env: nil, cwd: nil)
35
+ process = ChildProcess.new(*cmd)
36
+ process.io.inherit!
37
+ if cwd
38
+ process.cwd = cwd
39
+ end
40
+ if env
41
+ env.each do |k, v|
42
+ process.environment[k.to_s] = v
43
+ end
44
+ end
45
+
46
+ output = ''
47
+ r, w = IO.pipe
48
+
49
+ begin
50
+ process.io.stdout = w
51
+ process.start
52
+ w.close
53
+
54
+ thread = Thread.new do
55
+ begin
56
+ loop do
57
+ output << r.readpartial(16384)
58
+ end
59
+ rescue EOFError
60
+ end
61
+ end
62
+
63
+ process.wait
64
+ thread.join
65
+ ensure
66
+ r.close
67
+ end
68
+
69
+ [process, output]
70
+ end
71
+
72
+ module_function def check_output(*args)
73
+ process, output = get_output(*args)
74
+ unless process.exit_code == 0
75
+ raise SpawnError,"Failed to execute: #{args}"
76
+ end
77
+ output
78
+ end
79
+ end
80
+ end