mongoid 9.0.6 → 9.0.7

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 -2
  10. data/spec/integration/app_spec.rb +6 -0
  11. data/spec/integration/associations/embeds_one_spec.rb +25 -6
  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 -18
  42. metadata +59 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: de63ddee828740cd1ba5832d931cd2c9731f19c1435076527533fb7856b48f8c
4
- data.tar.gz: 4cfa7d1e9f00ec0fe57c7709c7147f9b214375de3e402d5a468e03cf6e61af40
3
+ metadata.gz: 72ad16d0232a1f1c1144801896ec7322b6c452100e02e9bc5c1ba6d21e015e62
4
+ data.tar.gz: 5ca24809ea6498805807e80e78d347a8f2d517862d2ace0c39a73fb81918e178
5
5
  SHA512:
6
- metadata.gz: 4542042a79b3a3162acd38d28898cd0755128b0a209d33e3ecf18b98d12e7ab1d220045156164ec235672cff337fc2d2a53a5779b37b601e9d903481e2793236
7
- data.tar.gz: 5bd06c74d154e09f12120d81813e6e2a1380d141cd3613350355e30e0ded7fc9774dd7dfb1ca335e00a7ad0c512e4a019e0df51bec7cb06e334cead745dd098c
6
+ metadata.gz: 82a45afa6174672d301145d1910eb6ef4d86433f078fd6514c22d644c101637404f5c2562e497dd3c9576935ce0f267fb3993d3493c520645a85e42799d0601f
7
+ data.tar.gz: d01a7d34d5b01aa5b7629b4dad7735f6c86756e86e4cbe6695301f1fe1864043041ccf4beb785bb28e48b2886d85a2c7dc4d54dddbf644a24f07b9852e15ba17
data/Rakefile CHANGED
@@ -11,16 +11,16 @@ $: << File.join(ROOT, 'spec/shared/lib')
11
11
  require "rake"
12
12
  require "rspec/core/rake_task"
13
13
 
14
- # stands in for the Bundler-provided `build` task, which builds the
15
- # gem for this project. Our release process builds the gems in a
16
- # particular way, in a GitHub action. This task is just to help remind
17
- # developers of that fact.
14
+ if File.exist?('./spec/shared/lib/tasks/candidate.rake')
15
+ load 'spec/shared/lib/tasks/candidate.rake'
16
+ end
17
+
18
+ desc 'Build the gem'
18
19
  task :build do
19
- abort <<~WARNING
20
- `rake build` does nothing in this project. The gem must be built via
21
- the `Mongoid Release` action on GitHub, which is triggered manually when
22
- a new release is ready.
23
- WARNING
20
+ command = %w[ gem build ]
21
+ command << "--output=#{ENV['GEM_FILE_NAME']}" if ENV['GEM_FILE_NAME']
22
+ command << (ENV['GEMSPEC'] || 'mongoid.gemspec')
23
+ system(*command)
24
24
  end
25
25
 
26
26
  # `rake version` is used by the deployment system so get the release version
@@ -36,8 +36,8 @@ module Mongoid
36
36
  bind_one
37
37
  characterize_one(_target)
38
38
  update_attributes_hash(_target)
39
- _base._reset_memoized_descendants!
40
39
  _target.save if persistable?
40
+ _base._reset_memoized_descendants!
41
41
  end
42
42
  end
43
43
 
@@ -1063,7 +1063,7 @@ module Mongoid
1063
1063
  end
1064
1064
 
1065
1065
  def retrieve_nth_to_last_with_limit(n, limit)
1066
- v = view.sort(inverse_sorting).skip(n).limit(limit || 1)
1066
+ v = view.sort(inverse_sorting).limit(limit || 1)
1067
1067
  v = v.skip(n) if n > 0
1068
1068
  raw_docs = v.to_a.reverse
1069
1069
  process_raw_docs(raw_docs, limit)
@@ -74,7 +74,7 @@ module Mongoid
74
74
  # use map.all? instead of just all?, because all? will do short-circuit
75
75
  # evaluation and terminate on the first failed validation.
76
76
  list.map do |value|
77
- if value && !value.flagged_for_destroy?
77
+ if value && !value.flagged_for_destroy? && (!value.persisted? || value.changed?)
78
78
  value.validated? ? true : value.valid?
79
79
  else
80
80
  true
@@ -89,6 +89,21 @@ module Mongoid
89
89
  def validates_presence_of(*args)
90
90
  validates_with(PresenceValidator, _merge_attributes(args))
91
91
  end
92
+
93
+ # Validates whether or not a field contains a numeric value.
94
+ #
95
+ # @example
96
+ # class Person
97
+ # include Mongoid::Document
98
+ # field :cost
99
+ #
100
+ # validates_numericality_of :cost
101
+ # end
102
+ #
103
+ # @param [ Object... ] *args The names of the field(s) to validate.
104
+ def validates_numericality_of(*args)
105
+ validates_with(NumericalityValidator, _merge_attributes(args))
106
+ end
92
107
  end
93
108
  end
94
109
  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
@@ -6,6 +6,7 @@ require "mongoid/validatable/localizable"
6
6
  require "mongoid/validatable/associated"
7
7
  require "mongoid/validatable/format"
8
8
  require "mongoid/validatable/length"
9
+ require "mongoid/validatable/numericality"
9
10
  require "mongoid/validatable/queryable"
10
11
  require "mongoid/validatable/presence"
11
12
  require "mongoid/validatable/uniqueness"
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
- # rubocop:todo all
3
2
 
4
3
  module Mongoid
5
- VERSION = "9.0.6"
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 = '9.0.7'
6
9
  end
@@ -127,6 +127,12 @@ describe 'Mongoid application tests' do
127
127
  end
128
128
 
129
129
  context 'new application - rails' do
130
+ before(:all) do
131
+ if SpecConfig.instance.rails_version < '7.1'
132
+ skip '`rails new` with rails < 7.1 fails because modern concurrent-ruby removed logger dependency'
133
+ end
134
+ end
135
+
130
136
  it 'creates' do
131
137
  prepare_new_rails_app 'mongoid-test' do
132
138
  check_call(%w(rails g model post), env: clean_env)
@@ -1,11 +1,9 @@
1
1
  # frozen_string_literal: true
2
- # rubocop:todo all
3
2
 
4
3
  require 'spec_helper'
5
4
 
6
5
  describe 'embeds_one associations' do
7
-
8
- context 're-associating the same object' do
6
+ context 'when re-associating the same object' do
9
7
  context 'with dependent: destroy' do
10
8
  let(:canvas) do
11
9
  Canvas.create!(palette: Palette.new)
@@ -17,7 +15,7 @@ describe 'embeds_one associations' do
17
15
  canvas.palette = canvas.palette
18
16
  canvas.save!
19
17
  canvas.reload
20
- canvas.palette.should == palette
18
+ expect(canvas.palette).to eq palette
21
19
  end
22
20
  end
23
21
  end
@@ -31,12 +29,33 @@ describe 'embeds_one associations' do
31
29
  end
32
30
 
33
31
  it 'loads the association correctly' do
34
- expect { klass }.to_not raise_error
35
- expect { klass.new.address }.to_not raise_error
32
+ expect { klass }.not_to raise_error
33
+ expect { klass.new.address }.not_to raise_error
36
34
  instance = klass.new
37
35
  address = Address.new
38
36
  instance.address = address
39
37
  expect(instance.address).to eq address
40
38
  end
41
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
42
61
  end
@@ -115,66 +115,6 @@ describe Mongoid::Association do
115
115
  expect(name).to_not be_an_embedded_many
116
116
  end
117
117
  end
118
-
119
- context "when validation depends on association" do
120
- before(:all) do
121
- class Author
122
- include Mongoid::Document
123
- embeds_many :books, cascade_callbacks: true
124
- field :condition, type: Boolean
125
- end
126
-
127
- class Book
128
- include Mongoid::Document
129
- embedded_in :author
130
- validate :parent_condition_is_not_true
131
-
132
- def parent_condition_is_not_true
133
- return unless author&.condition
134
- errors.add :base, "Author condition is true."
135
- end
136
- end
137
-
138
- Author.delete_all
139
- Book.delete_all
140
- end
141
-
142
- let(:author) { Author.new }
143
- let(:book) { Book.new }
144
-
145
- context "when author is not persisted" do
146
- it "is valid without books" do
147
- expect(author.valid?).to be true
148
- end
149
-
150
- it "is valid with a book" do
151
- author.books << book
152
- expect(author.valid?).to be true
153
- end
154
-
155
- it "is not valid when condition is true with a book" do
156
- author.condition = true
157
- author.books << book
158
- expect(author.valid?).to be false
159
- end
160
- end
161
-
162
- context "when author is persisted" do
163
- before do
164
- author.books << book
165
- author.save
166
- end
167
-
168
- it "remains valid initially" do
169
- expect(author.valid?).to be true
170
- end
171
-
172
- it "becomes invalid when condition is set to true" do
173
- author.update_attributes(condition: true)
174
- expect(author.valid?).to be false
175
- end
176
- end
177
- end
178
118
  end
179
119
 
180
120
  describe "#embedded_one?" do
@@ -3282,6 +3282,12 @@ describe Mongoid::Contextual::Mongo do
3282
3282
  it "limits the results" do
3283
3283
  expect(context.skip(1).entries).to eq([ new_order ])
3284
3284
  end
3285
+
3286
+ context "with #last" do
3287
+ it "returns the nth from last element" do
3288
+ expect(context.skip(1).last).to eq(depeche_mode)
3289
+ end
3290
+ end
3285
3291
  end
3286
3292
 
3287
3293
  describe "#sort" do
@@ -29,5 +29,21 @@ describe ActiveModel::Validations::NumericalityValidator do
29
29
  expect(model).to_not be_valid
30
30
  end
31
31
  end
32
+
33
+ context 'when the value is numeric' do
34
+ let(:model) { TestModel.new(amount: '15.0') }
35
+
36
+ it 'returns true' do
37
+ expect(model).to be_valid
38
+ end
39
+ end
40
+
41
+ context 'when the value is a BSON::Decimal128' do
42
+ let(:model) { TestModel.new(amount: BSON::Decimal128.new('15.0')) }
43
+
44
+ it 'returns true' do
45
+ expect(model).to be_valid
46
+ end
47
+ end
32
48
  end
33
49
  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