mongoid 7.0.12 → 7.0.13

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '08107532375c756a9a063a167169b570fa9d5f195e753a4f19a70ee7778eff1c'
4
- data.tar.gz: addbf1ebd6dc0edc11a2604b4e3c1bf7297d67ad12739dcd44e981d2a8949cac
3
+ metadata.gz: c687add0cd3bcd3001781fcb3011966fb5617848c1b1096c9ecf08a6a8d3e123
4
+ data.tar.gz: 242b9126fc96c4719aa23e96a25f9ff590c063020b8d8806315cbd887316fd24
5
5
  SHA512:
6
- metadata.gz: 77dbc0a24838853d496fd5957770ef250c952cf57a126b36845540e6421888aa3abc9465ecb64d0a55940324c02a7e22a37935729deec400b6d2f0510138930a
7
- data.tar.gz: 0b78a7bbe3049aff86657a02e88f61ef4b8281f5cd198567bbe153d44f7f56d74eca7b0119a40d9b8188ffb0b78f73847abbba82b39b601220f177ef7ac80a61
6
+ metadata.gz: 13f8de150ffbc721206b56d8b532525bd66d380454d3ae53e202159da26d39db48a0c70f15ae0f10278adaa97ec3380c6565c2c97993f7953920b1ab89ea02a4
7
+ data.tar.gz: 2e5a35a8935add506b7a514ca392ce1f9f9e3d6440b0152a154d56bce2b9f5e12dabf7ce1ca9c99f6f424d7efcdeb1ddd3e05dad17849b91b7b76fd3b5cb543d
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/Rakefile CHANGED
@@ -2,8 +2,13 @@ require "bundler"
2
2
  require "bundler/gem_tasks"
3
3
  Bundler.setup
4
4
 
5
+ ROOT = File.expand_path(File.join(File.dirname(__FILE__)))
6
+
7
+ $: << File.join(ROOT, 'spec/shared/lib')
8
+
5
9
  require "rake"
6
10
  require "rspec/core/rake_task"
11
+ require 'mrss/spec_organizer'
7
12
 
8
13
  $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
9
14
  require "mongoid/version"
@@ -33,6 +38,32 @@ RSpec::Core::RakeTask.new('spec:progress') do |spec|
33
38
  spec.pattern = "spec/**/*_spec.rb"
34
39
  end
35
40
 
41
+ CLASSIFIERS = [
42
+ [%r,^mongoid/attribute,, :attributes],
43
+ [%r,^mongoid/association/[or],, :associations_referenced],
44
+ [%r,^mongoid/association,, :associations],
45
+ [%r,^mongoid,, :unit],
46
+ [%r,^integration,, :integration],
47
+ [%r,^rails,, :rails],
48
+ ]
49
+
50
+ RUN_PRIORITY = %i(
51
+ unit attributes associations_referenced associations
52
+ integration rails
53
+ )
54
+
55
+ def spec_organizer
56
+ Mrss::SpecOrganizer.new(
57
+ root: ROOT,
58
+ classifiers: CLASSIFIERS,
59
+ priority_order: RUN_PRIORITY,
60
+ )
61
+ end
62
+
63
+ task :ci do
64
+ spec_organizer.run
65
+ end
66
+
36
67
  task :default => :spec
37
68
 
38
69
  desc "Generate all documentation"
@@ -364,15 +364,12 @@ module Mongoid
364
364
  Binding.new(_base, _target, _association)
365
365
  end
366
366
 
367
- # Returns the "#{criteria}"# object for the target class with its documents set
368
- # to target.
369
- #
370
- # @example Get a criteria for the relation.
371
- # relation.criteria
367
+ # Returns the +Criteria+ object for the target class with its
368
+ # documents set to the list of target documents in the association.
372
369
  #
373
370
  # @return [ Criteria ] A new criteria.
374
371
  def criteria
375
- @criteria ||= _association.criteria(_base, _target)
372
+ _association.criteria(_base, _target)
376
373
  end
377
374
 
378
375
  # Deletes one document from the target and unscoped.
@@ -448,8 +445,8 @@ module Mongoid
448
445
  end
449
446
  end
450
447
 
451
- # Apply the association ordering or the default scoping to the provided
452
- # documents.
448
+ # Apply the association ordering and default scoping (defined on
449
+ # association's target class) to the provided documents.
453
450
  #
454
451
  # @example Apply scoping.
455
452
  # person.addresses.scope(target)
@@ -460,7 +457,10 @@ module Mongoid
460
457
  #
461
458
  # @since 2.4.0
462
459
  def scope(docs)
463
- return docs unless _association.order || _association.klass.default_scoping?
460
+ unless _association.order || _association.klass.default_scoping?
461
+ return docs
462
+ end
463
+
464
464
  crit = _association.klass.order_by(_association.order)
465
465
  crit.embedded = true
466
466
  crit.documents = docs
@@ -18,7 +18,20 @@ module Mongoid
18
18
  include Threaded::Lifecycle
19
19
  include Marshalable
20
20
 
21
- attr_accessor :_base, :_association, :_target
21
+ # Model instance for the base of the association.
22
+ #
23
+ # For example, if a Post embeds_many Comments, _base is a particular
24
+ # instance of the Post model.
25
+ attr_accessor :_base
26
+
27
+ attr_accessor :_association
28
+
29
+ # Model instance for one to one associations, or array of model instances
30
+ # for one to many associations, for the target of the association.
31
+ #
32
+ # For example, if a Post embeds_many Comments, _target is an array of
33
+ # Comment models embedded in a particular Post.
34
+ attr_accessor :_target
22
35
 
23
36
  # Backwards compatibility with Mongoid beta releases.
24
37
  delegate :foreign_key, :inverse_foreign_key, to: :_association
@@ -158,6 +158,11 @@ module Mongoid
158
158
  # @since 1.0.0
159
159
  def write_attribute(name, value)
160
160
  field_name = database_field_name(name)
161
+
162
+ if attribute_missing?(field_name)
163
+ raise ActiveModel::MissingAttributeError, "Missing attribute: '#{name}'"
164
+ end
165
+
161
166
  if attribute_writable?(field_name)
162
167
  _assigning do
163
168
  validate_attribute_value(field_name, value)
@@ -175,6 +180,8 @@ module Mongoid
175
180
  end
176
181
  typed_value
177
182
  end
183
+ else
184
+ # TODO: MONGOID-5072
178
185
  end
179
186
  end
180
187
  alias :[]= :write_attribute
@@ -292,7 +299,7 @@ module Mongoid
292
299
  def read_raw_attribute(name)
293
300
  normalized = database_field_name(name.to_s)
294
301
  if attribute_missing?(normalized)
295
- raise ActiveModel::MissingAttributeError, "Missing attribute: '#{name}'."
302
+ raise ActiveModel::MissingAttributeError, "Missing attribute: '#{name}'"
296
303
  end
297
304
  if hash_dot_syntax?(normalized)
298
305
  attributes.__nested__(normalized)
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Mongoid
4
- VERSION = "7.0.12"
4
+ VERSION = "7.0.13"
5
5
  end
@@ -13,7 +13,14 @@ module Mongoid
13
13
  end
14
14
 
15
15
  def app_name
16
- Rails::Application.subclasses.first.parent.to_s.underscore
16
+ app_cls = Rails.application.class
17
+ parent = begin
18
+ # Rails 6.1+
19
+ app_cls.module_parent_name
20
+ rescue NoMethodError
21
+ app_cls.parent.to_s
22
+ end
23
+ parent.underscore
17
24
  end
18
25
 
19
26
  def create_config_file
@@ -13,102 +13,173 @@ describe 'Mongoid application tests' do
13
13
  end
14
14
 
15
15
  require 'fileutils'
16
- require 'support/child_process_helper'
16
+ require 'mrss/child_process_helper'
17
17
  require 'open-uri'
18
18
 
19
19
  FileUtils.mkdir_p(TMP_BASE)
20
20
  end
21
21
 
22
- context 'demo application - sinatra' do
23
- it 'runs' do
24
- clone_application(
25
- 'https://github.com/mongoid/mongoid-demo',
26
- subdir: 'sinatra-minimal',
27
- ) do
22
+ context 'demo application' do
23
+ context 'sinatra' do
24
+ it 'runs' do
25
+ clone_application(
26
+ 'https://github.com/mongoid/mongoid-demo',
27
+ subdir: 'sinatra-minimal',
28
+ ) do
28
29
 
29
- process = ChildProcess.build(*%w(bundle exec ruby app.rb))
30
- process.environment.update(clean_env)
31
- process.io.inherit!
32
- process.start
33
-
34
- begin
35
30
  # JRuby needs a long timeout
36
- wait_for_port(4567, 20)
37
- sleep 1
38
-
39
- uri = URI.parse('http://localhost:4567/posts')
40
- resp = JSON.parse(uri.open.read)
41
- ensure
42
- Process.kill('TERM', process.pid)
43
- status = process.wait
44
- end
31
+ start_app(%w(bundle exec ruby app.rb), 4567, 40) do |port|
32
+ uri = URI.parse('http://localhost:4567/posts')
33
+ resp = JSON.parse(uri.open.read)
45
34
 
46
- resp.should == []
35
+ resp.should == []
47
36
 
48
- status.should == 0
37
+ end
38
+ end
49
39
  end
50
40
  end
51
- end
52
41
 
53
- context 'demo application - rails-api' do
54
- ['~> 6.0.0'].each do |rails_version|
55
- context "with rails #{rails_version}" do
56
- it 'runs' do
57
- clone_application(
58
- 'https://github.com/mongoid/mongoid-demo',
59
- subdir: 'rails-api',
60
- rails_version: rails_version,
61
- ) do
62
-
63
- process = ChildProcess.build(*%w(bundle exec rails s))
64
- process.environment.update(clean_env)
65
- process.io.inherit!
66
- process.start
67
-
68
- begin
69
- # JRuby needs a long timeout
70
- wait_for_port(3000, 30)
71
- sleep 1
72
-
73
- uri = URI.parse('http://localhost:3000/posts')
74
- resp = JSON.parse(uri.open.read)
75
- ensure
76
- Process.kill('TERM', process.pid)
77
- status = process.wait
78
- end
42
+ context 'rails-api' do
43
+ it 'runs' do
44
+ clone_application(
45
+ 'https://github.com/mongoid/mongoid-demo',
46
+ subdir: 'rails-api',
47
+ ) do
79
48
 
80
- resp.should == []
49
+ # JRuby needs a long timeout
50
+ start_app(%w(bundle exec rails s), 3000, 50) do |port|
51
+ uri = URI.parse('http://localhost:3000/posts')
52
+ resp = JSON.parse(uri.open.read)
81
53
 
82
- # 143 = 128 + 15
83
- [0, 15, 143].should include(status)
54
+ resp.should == []
84
55
  end
85
56
  end
86
57
  end
87
58
  end
88
59
  end
89
60
 
61
+ def start_app(cmd, port, timeout)
62
+ process = ChildProcess.build(*cmd)
63
+ process.environment.update(clean_env)
64
+ process.io.inherit!
65
+ process.start
66
+
67
+ begin
68
+ wait_for_port(port, timeout, process)
69
+ sleep 1
70
+
71
+ rv = yield port
72
+ ensure
73
+ # The process may have already died (due to an error exit) -
74
+ # in this case killing it will raise an exception.
75
+ Process.kill('TERM', process.pid) rescue nil
76
+ status = process.wait
77
+ end
78
+
79
+ # Exit should be either success or SIGTERM
80
+ [0, 15, 128 + 15].should include(status)
81
+
82
+ rv
83
+ end
84
+
90
85
  context 'new application - rails' do
91
- ['~> 5.1.0', '~> 5.2.0', '~> 6.0.0'].each do |rails_version|
92
- context "with rails #{rails_version}" do
93
- it 'creates' do
94
- ChildProcessHelper.check_call(%w(gem uni rails -a))
95
- ChildProcessHelper.check_call(%w(gem install rails --no-document -v) + [rails_version])
96
-
97
- Dir.chdir(TMP_BASE) do
98
- FileUtils.rm_rf('mongoid-test')
99
- ChildProcessHelper.check_call(%w(rails new mongoid-test --skip-spring), env: clean_env)
100
-
101
- Dir.chdir('mongoid-test') do
102
- adjust_app_gemfile
103
- ChildProcessHelper.check_call(%w(bundle install), env: clean_env)
104
-
105
- ChildProcessHelper.check_call(%w(rails g model post), env: clean_env)
106
- ChildProcessHelper.check_call(%w(rails g model comment post:belongs_to), env: clean_env)
107
-
108
- # https://jira.mongodb.org/browse/MONGOID-4885
109
- comment_text = File.read('app/models/comment.rb')
110
- comment_text.should =~ /belongs_to :post/
111
- comment_text.should_not =~ /embedded_in :post/
86
+ it 'creates' do
87
+ install_rails
88
+
89
+ Dir.chdir(TMP_BASE) do
90
+ FileUtils.rm_rf('mongoid-test')
91
+ Mrss::ChildProcessHelper.check_call(%w(rails new mongoid-test --skip-spring --skip-active-record), env: clean_env)
92
+
93
+ Dir.chdir('mongoid-test') do
94
+ adjust_app_gemfile
95
+ Mrss::ChildProcessHelper.check_call(%w(bundle install), env: clean_env)
96
+
97
+ Mrss::ChildProcessHelper.check_call(%w(rails g model post), env: clean_env)
98
+ Mrss::ChildProcessHelper.check_call(%w(rails g model comment post:belongs_to), env: clean_env)
99
+
100
+ # https://jira.mongodb.org/browse/MONGOID-4885
101
+ comment_text = File.read('app/models/comment.rb')
102
+ comment_text.should =~ /belongs_to :post/
103
+ comment_text.should_not =~ /embedded_in :post/
104
+ end
105
+ end
106
+ end
107
+
108
+ it 'generates Mongoid config' do
109
+ install_rails
110
+
111
+ Dir.chdir(TMP_BASE) do
112
+ FileUtils.rm_rf('mongoid-test-config')
113
+ Mrss::ChildProcessHelper.check_call(%w(rails new mongoid-test-config --skip-spring --skip-active-record), env: clean_env)
114
+
115
+ Dir.chdir('mongoid-test-config') do
116
+ adjust_app_gemfile
117
+ Mrss::ChildProcessHelper.check_call(%w(bundle install), env: clean_env)
118
+
119
+ mongoid_config_file = File.join(TMP_BASE,'mongoid-test-config/config/mongoid.yml')
120
+
121
+ File.exist?(mongoid_config_file).should be false
122
+ Mrss::ChildProcessHelper.check_call(%w(rails g mongoid:config), env: clean_env)
123
+ File.exist?(mongoid_config_file).should be true
124
+
125
+ config_text = File.read(mongoid_config_file)
126
+ config_text.should =~ /mongoid_test_config_development/
127
+ config_text.should =~ /mongoid_test_config_test/
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ def install_rails
134
+ Mrss::ChildProcessHelper.check_call(%w(gem uni rails -a))
135
+ if (rails_version = SpecConfig.instance.rails_version) == 'master'
136
+ else
137
+ Mrss::ChildProcessHelper.check_call(%w(gem install rails --no-document -v) + [rails_version])
138
+ end
139
+ end
140
+
141
+ context 'local test applications' do
142
+ let(:client) { Mongoid.default_client }
143
+
144
+ describe 'create_indexes rake task' do
145
+
146
+ APP_PATH = File.join(File.dirname(__FILE__), '../../test-apps/rails-api')
147
+
148
+ %w(development production).each do |rails_env|
149
+ context "in #{rails_env}" do
150
+
151
+ %w(classic zeitwerk).each do |autoloader|
152
+ context "with #{autoloader} autoloader" do
153
+
154
+ let(:env) do
155
+ clean_env.merge(RAILS_ENV: rails_env, AUTOLOADER: autoloader)
156
+ end
157
+
158
+ before do
159
+ Dir.chdir(APP_PATH) do
160
+ remove_bundler_req
161
+ Mrss::ChildProcessHelper.check_call(%w(bundle install), env: env)
162
+ write_mongoid_yml
163
+ end
164
+
165
+ client['posts'].drop
166
+ client['posts'].create
167
+ end
168
+
169
+ it 'creates an index' do
170
+ index = client['posts'].indexes.detect do |index|
171
+ index['key'] == {'subject' => 1}
172
+ end
173
+ index.should be nil
174
+
175
+ Mrss::ChildProcessHelper.check_call(%w(bundle exec rake db:mongoid:create_indexes),
176
+ cwd: APP_PATH, env: env)
177
+
178
+ index = client['posts'].indexes.detect do |index|
179
+ index['key'] == {'subject' => 1}
180
+ end
181
+ index.should be_a(Hash)
182
+ end
112
183
  end
113
184
  end
114
185
  end
@@ -116,35 +187,70 @@ describe 'Mongoid application tests' do
116
187
  end
117
188
  end
118
189
 
119
- def clone_application(repo_url, subdir: nil, rails_version: nil)
190
+ def clone_application(repo_url, subdir: nil)
120
191
  Dir.chdir(TMP_BASE) do
121
192
  FileUtils.rm_rf(File.basename(repo_url))
122
- ChildProcessHelper.check_call(%w(git clone) + [repo_url])
193
+ Mrss::ChildProcessHelper.check_call(%w(git clone) + [repo_url])
123
194
  Dir.chdir(File.join(*[File.basename(repo_url), subdir].compact)) do
124
- adjust_app_gemfile(rails_version: rails_version)
125
- ChildProcessHelper.check_call(%w(bundle install), env: clean_env)
195
+ adjust_app_gemfile
196
+ adjust_rails_defaults
197
+ Mrss::ChildProcessHelper.check_call(%w(bundle install), env: clean_env)
126
198
  puts `git diff`
127
199
 
128
- config = {'development' => {'clients' => {'default' => {'uri' => SpecConfig.instance.uri_str}}}}
129
- File.open('config/mongoid.yml', 'w') do |f|
130
- f << YAML.dump(config)
131
- end
200
+ write_mongoid_yml
132
201
 
133
202
  yield
134
203
  end
135
204
  end
136
205
  end
137
206
 
138
- def adjust_app_gemfile(rails_version: nil)
139
- lock_lines = IO.readlines('Gemfile.lock')
140
- # Get rid of the bundled with line so that whatever bundler is installed
141
- # on the system is usable with the application.
142
- if i = lock_lines.index("BUNDLED WITH\n")
143
- lock_lines.slice!(i, 2)
144
- File.open('Gemfile.lock', 'w') do |f|
145
- f << lock_lines.join
207
+ def parse_mongodb_uri(uri)
208
+ pre, query = uri.split('?', 2)
209
+ if pre =~ %r,\A(mongodb(?:.*?))://([^/]+)(?:/(.*))?\z,
210
+ protocol = $1
211
+ hosts = $2
212
+ database = $3
213
+ if database == ''
214
+ database = nil
146
215
  end
216
+ else
217
+ raise ArgumentError, "Invalid MongoDB URI: #{uri}"
218
+ end
219
+ if query == ''
220
+ query = nil
221
+ end
222
+ {
223
+ protocol: protocol,
224
+ hosts: hosts,
225
+ database: database,
226
+ query: query,
227
+ }
228
+ end
229
+
230
+ def build_mongodb_uri(parts)
231
+ "#{parts.fetch(:protocol)}://#{parts.fetch(:hosts)}/#{parts[:database]}?#{parts[:query]}"
232
+ end
233
+
234
+ def write_mongoid_yml
235
+ # HACK: the driver does not provide a MongoDB URI parser and assembler,
236
+ # and the Ruby standard library URI module doesn't handle multiple hosts.
237
+ parts = parse_mongodb_uri(SpecConfig.instance.uri_str)
238
+ parts[:database] = 'mongoid_test'
239
+ uri = build_mongodb_uri(parts)
240
+ p uri
241
+ env_config = {'clients' => {'default' => {
242
+ # TODO massive hack, will fail if uri specifies a database name or
243
+ # any uri options
244
+ 'uri' => uri,
245
+ }}}
246
+ config = {'development' => env_config, 'production' => env_config}
247
+ File.open('config/mongoid.yml', 'w') do |f|
248
+ f << YAML.dump(config)
147
249
  end
250
+ end
251
+
252
+ def adjust_app_gemfile(rails_version: SpecConfig.instance.rails_version)
253
+ remove_bundler_req
148
254
 
149
255
  gemfile_lines = IO.readlines('Gemfile')
150
256
  gemfile_lines.delete_if do |line|
@@ -155,27 +261,67 @@ describe 'Mongoid application tests' do
155
261
  gemfile_lines.delete_if do |line|
156
262
  line =~ /rails/
157
263
  end
158
- gemfile_lines << "gem 'rails', '#{rails_version}'\n"
264
+ if rails_version == 'master'
265
+ gemfile_lines << "gem 'rails', git: 'https://github.com/rails/rails'\n"
266
+ else
267
+ gemfile_lines << "gem 'rails', '~> #{rails_version}.0'\n"
268
+ end
159
269
  end
160
270
  File.open('Gemfile', 'w') do |f|
161
271
  f << gemfile_lines.join
162
272
  end
163
273
  end
164
274
 
275
+ def adjust_rails_defaults(rails_version: SpecConfig.instance.rails_version)
276
+ if File.exist?('config/application.rb')
277
+ lines = IO.readlines('config/application.rb')
278
+ lines.each do |line|
279
+ line.gsub!(/config.load_defaults \d\.\d/, "config.load_defaults #{rails_version}")
280
+ end
281
+ File.open('config/application.rb', 'w') do |f|
282
+ f << lines.join
283
+ end
284
+ end
285
+
286
+ if rails_version == '5.1'
287
+ secrets = {
288
+ 'development' => {
289
+ 'secret_key_base' => 'abracadabra',
290
+ 'my_secret_token' => 'very_secret',
291
+ },
292
+ }
293
+ File.open('config/secrets.yml', 'w') do |f|
294
+ f << YAML.dump(secrets)
295
+ end
296
+ end
297
+ end
298
+
299
+ def remove_bundler_req
300
+ lock_lines = IO.readlines('Gemfile.lock')
301
+ # Get rid of the bundled with line so that whatever bundler is installed
302
+ # on the system is usable with the application.
303
+ if i = lock_lines.index("BUNDLED WITH\n")
304
+ lock_lines.slice!(i, 2)
305
+ File.open('Gemfile.lock', 'w') do |f|
306
+ f << lock_lines.join
307
+ end
308
+ end
309
+ end
310
+
165
311
  def remove_spring
166
312
  # Spring produces this error in Evergreen:
167
313
  # /data/mci/280eb2ecf4fd69208e2106cd3af526f1/src/rubies/ruby-2.7.0/lib/ruby/gems/2.7.0/gems/spring-2.1.0/lib/spring/client/run.rb:26:
168
314
  # in `initialize': too long unix socket path (126bytes given but 108bytes max) (ArgumentError)
169
315
  # Is it trying to create unix sockets in current directory?
170
316
  # https://stackoverflow.com/questions/30302021/rails-runner-without-spring
171
- ChildProcessHelper.check_call(%w(bin/spring binstub --remove --all), env: clean_env)
317
+ Mrss::ChildProcessHelper.check_call(%w(bin/spring binstub --remove --all), env: clean_env)
172
318
  end
173
319
 
174
320
  def clean_env
175
321
  @clean_env ||= Hash[ENV.keys.grep(/BUNDLE|RUBYOPT/).map { |k| [k, nil ] }]
176
322
  end
177
323
 
178
- def wait_for_port(port, timeout)
324
+ def wait_for_port(port, timeout, process)
179
325
  deadline = Time.now + timeout
180
326
  loop do
181
327
  begin
@@ -183,6 +329,9 @@ describe 'Mongoid application tests' do
183
329
  return
184
330
  end
185
331
  rescue IOError, SystemCallError
332
+ unless process.alive?
333
+ raise "Process #{process} died while waiting for port #{port}"
334
+ end
186
335
  if Time.now > deadline
187
336
  raise
188
337
  end