mongoid 7.0.3 → 7.0.8

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 (102) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -0
  3. data.tar.gz.sig +1 -0
  4. data/LICENSE +1 -0
  5. data/README.md +3 -2
  6. data/Rakefile +12 -0
  7. data/lib/mongoid.rb +2 -1
  8. data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
  9. data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
  10. data/lib/mongoid/association/proxy.rb +1 -1
  11. data/lib/mongoid/association/relatable.rb +23 -21
  12. data/lib/mongoid/atomic.rb +13 -3
  13. data/lib/mongoid/atomic/paths/embedded.rb +1 -1
  14. data/lib/mongoid/attributes.rb +28 -20
  15. data/lib/mongoid/attributes/dynamic.rb +15 -14
  16. data/lib/mongoid/config/environment.rb +21 -8
  17. data/lib/mongoid/copyable.rb +5 -1
  18. data/lib/mongoid/criteria.rb +7 -1
  19. data/lib/mongoid/criteria/modifiable.rb +13 -2
  20. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  21. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
  22. data/lib/mongoid/criteria/queryable/key.rb +67 -8
  23. data/lib/mongoid/criteria/queryable/mergeable.rb +5 -4
  24. data/lib/mongoid/criteria/queryable/selectable.rb +3 -4
  25. data/lib/mongoid/criteria/queryable/selector.rb +9 -31
  26. data/lib/mongoid/extensions/hash.rb +4 -2
  27. data/lib/mongoid/extensions/regexp.rb +1 -1
  28. data/lib/mongoid/extensions/string.rb +5 -3
  29. data/lib/mongoid/fields.rb +2 -1
  30. data/lib/mongoid/matchable.rb +14 -15
  31. data/lib/mongoid/matchable/all.rb +4 -3
  32. data/lib/mongoid/matchable/default.rb +71 -24
  33. data/lib/mongoid/matchable/regexp.rb +2 -2
  34. data/lib/mongoid/persistable/pushable.rb +11 -2
  35. data/lib/mongoid/persistence_context.rb +6 -6
  36. data/lib/mongoid/positional.rb +1 -1
  37. data/lib/mongoid/query_cache.rb +3 -2
  38. data/lib/mongoid/validatable/macros.rb +1 -1
  39. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  40. data/lib/mongoid/version.rb +2 -1
  41. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
  42. data/spec/README.md +18 -0
  43. data/spec/app/models/delegating_patient.rb +16 -0
  44. data/spec/app/models/other_owner_object.rb +2 -0
  45. data/spec/integration/app_spec.rb +192 -0
  46. data/spec/integration/associations/embedded_spec.rb +62 -0
  47. data/spec/integration/criteria/time_with_zone_spec.rb +32 -0
  48. data/spec/integration/document_spec.rb +22 -0
  49. data/spec/integration/matchable_spec.rb +680 -0
  50. data/spec/lite_spec_helper.rb +15 -5
  51. data/spec/mongoid/association/embedded/embedded_in_spec.rb +58 -0
  52. data/spec/mongoid/association/embedded/embeds_many_models.rb +53 -0
  53. data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
  54. data/spec/mongoid/association/embedded/embeds_one_dnl_models.rb +6 -0
  55. data/spec/mongoid/association/embedded/embeds_one_models.rb +51 -0
  56. data/spec/mongoid/association/embedded/embeds_one_spec.rb +46 -0
  57. data/spec/mongoid/association/referenced/belongs_to_spec.rb +23 -6
  58. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +2 -1
  59. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +2 -1
  60. data/spec/mongoid/association/referenced/has_one_spec.rb +12 -2
  61. data/spec/mongoid/attributes/dynamic_spec.rb +153 -0
  62. data/spec/mongoid/attributes_spec.rb +19 -7
  63. data/spec/mongoid/clients/factory_spec.rb +2 -2
  64. data/spec/mongoid/clients/options_spec.rb +4 -4
  65. data/spec/mongoid/clients/sessions_spec.rb +20 -7
  66. data/spec/mongoid/clients/transactions_spec.rb +36 -15
  67. data/spec/mongoid/clients_spec.rb +2 -2
  68. data/spec/mongoid/contextual/atomic_spec.rb +20 -10
  69. data/spec/mongoid/contextual/geo_near_spec.rb +1 -0
  70. data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
  71. data/spec/mongoid/contextual/mongo_spec.rb +76 -53
  72. data/spec/mongoid/criteria/modifiable_spec.rb +59 -10
  73. data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +54 -0
  74. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
  75. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
  76. data/spec/mongoid/criteria/queryable/key_spec.rb +48 -6
  77. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +762 -0
  78. data/spec/mongoid/criteria/queryable/selectable_spec.rb +5 -224
  79. data/spec/mongoid/criteria/queryable/selector_spec.rb +37 -0
  80. data/spec/mongoid/criteria_spec.rb +7 -2
  81. data/spec/mongoid/document_fields_spec.rb +88 -0
  82. data/spec/mongoid/document_persistence_context_spec.rb +33 -0
  83. data/spec/mongoid/extensions/string_spec.rb +35 -7
  84. data/spec/mongoid/indexable_spec.rb +6 -4
  85. data/spec/mongoid/matchable/default_spec.rb +10 -3
  86. data/spec/mongoid/matchable/regexp_spec.rb +2 -2
  87. data/spec/mongoid/matchable_spec.rb +2 -2
  88. data/spec/mongoid/persistable/pushable_spec.rb +55 -1
  89. data/spec/mongoid/query_cache_spec.rb +2 -1
  90. data/spec/mongoid/relations/proxy_spec.rb +1 -1
  91. data/spec/mongoid/scopable_spec.rb +2 -1
  92. data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
  93. data/spec/mongoid/tasks/database_spec.rb +1 -1
  94. data/spec/mongoid/validatable/uniqueness_spec.rb +33 -6
  95. data/spec/spec_helper.rb +4 -37
  96. data/spec/support/child_process_helper.rb +76 -0
  97. data/spec/support/cluster_config.rb +158 -0
  98. data/spec/support/constraints.rb +29 -19
  99. data/spec/support/expectations.rb +17 -3
  100. data/spec/support/spec_config.rb +12 -4
  101. metadata +525 -464
  102. metadata.gz.sig +2 -0
@@ -7,8 +7,8 @@ module Mongoid
7
7
  # Does the supplied query match the attribute?
8
8
  #
9
9
  # @example Does this match?
10
- # matcher._matches?(/^Em/)
11
- # matcher._matches?(BSON::Regex::Raw.new("^Em"))
10
+ # matcher._matches?(/\AEm/)
11
+ # matcher._matches?(BSON::Regex::Raw.new("\\AEm"))
12
12
  #
13
13
  # @param [ BSON::Regexp::Raw, Regexp ] regexp The regular expression object.
14
14
  #
@@ -22,7 +22,13 @@ module Mongoid
22
22
  def add_to_set(adds)
23
23
  prepare_atomic_operation do |ops|
24
24
  process_atomic_operations(adds) do |field, value|
25
- existing = send(field) || (attributes[field] ||= [])
25
+ existing = send(field) || attributes[field]
26
+ if existing.nil?
27
+ attributes[field] = []
28
+ # Read the value out of attributes:
29
+ # https://jira.mongodb.org/browse/MONGOID-4874
30
+ existing = attributes[field]
31
+ end
26
32
  values = [ value ].flatten(1)
27
33
  values.each do |val|
28
34
  existing.push(val) unless existing.include?(val)
@@ -49,7 +55,10 @@ module Mongoid
49
55
  def push(pushes)
50
56
  prepare_atomic_operation do |ops|
51
57
  process_atomic_operations(pushes) do |field, value|
52
- existing = send(field) || (attributes[field] ||= [])
58
+ existing = send(field) || begin
59
+ attributes[field] ||= []
60
+ attributes[field]
61
+ end
53
62
  values = [ value ].flatten(1)
54
63
  values.each{ |val| existing.push(val) }
55
64
  ops[atomic_attribute_name(field)] = { "$each" => values }
@@ -116,6 +116,12 @@ module Mongoid
116
116
  client.with(client_options))
117
117
  end
118
118
 
119
+ def client_name
120
+ @client_name ||= options[:client] ||
121
+ Threaded.client_override ||
122
+ storage_options && __evaluate__(storage_options[:client])
123
+ end
124
+
119
125
  # Determine if this persistence context is equal to another.
120
126
  #
121
127
  # @example Compare two persistence contexts.
@@ -133,12 +139,6 @@ module Mongoid
133
139
 
134
140
  private
135
141
 
136
- def client_name
137
- @client_name ||= options[:client] ||
138
- Threaded.client_override ||
139
- storage_options && __evaluate__(storage_options[:client])
140
- end
141
-
142
142
  def set_options!(opts)
143
143
  @options ||= opts.each.reduce({}) do |_options, (key, value)|
144
144
  unless VALID_OPTIONS.include?(key.to_sym)
@@ -64,7 +64,7 @@ module Mongoid
64
64
  matches = position.scan(/\.\d+\./)
65
65
  if matches.size == 1
66
66
  keys.each do |kk|
67
- if position =~ /^#{kk}\.\d+\.(.*)/
67
+ if position =~ /\A#{kk}\.\d+\.(.*)\z/
68
68
  return "#{kk}.$.#{$1}"
69
69
  end
70
70
  end
@@ -166,7 +166,8 @@ module Mongoid
166
166
  @coll_name ||= result.namespace.sub("#{database.name}.", '') if result.namespace
167
167
  documents = result.documents
168
168
  if @cursor_id.zero? && !@after_first_batch
169
- (@cached_documents ||= []).concat(documents)
169
+ @cached_documents ||= []
170
+ @cached_documents.concat(documents)
170
171
  end
171
172
  @after_first_batch = true
172
173
  documents
@@ -258,7 +259,7 @@ module Mongoid
258
259
  end
259
260
 
260
261
  def system_collection?
261
- collection.namespace =~ /^system./
262
+ collection.namespace =~ /\Asystem./
262
263
  end
263
264
  end
264
265
 
@@ -48,7 +48,7 @@ module Mongoid
48
48
  # include Mongoid::Document
49
49
  # field :title
50
50
  #
51
- # validates_format_of :title, with: /^[a-z0-9 \-_]*$/i
51
+ # validates_format_of :title, with: /\A[a-z0-9 \-_]*\z/i
52
52
  # end
53
53
  #
54
54
  # @param [ Array ] args The names of the fields to validate.
@@ -148,7 +148,7 @@ module Mongoid
148
148
  #
149
149
  # @since 2.3.0
150
150
  def filter(value)
151
- !case_sensitive? && value ? /\A#{Regexp.escape(value.to_s)}$/i : value
151
+ !case_sensitive? && value ? /\A#{Regexp.escape(value.to_s)}\z/i : value
152
152
  end
153
153
 
154
154
  # Scope the criteria to the scope options provided.
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+
2
3
  module Mongoid
3
- VERSION = "7.0.3"
4
+ VERSION = "7.0.8"
4
5
  end
@@ -13,7 +13,7 @@ class <%= class_name %><%= " < #{options[:parent].classify}" if options[:parent]
13
13
  field :<%= attribute.name %>, type: <%= attribute.type_class %>
14
14
  <% end -%>
15
15
  <% attributes.select{|attr| attr.reference? }.each do |attribute| -%>
16
- embedded_in :<%= attribute.name%>
16
+ belongs_to :<%= attribute.name%>
17
17
  <% end -%>
18
18
  end
19
19
  <% end -%>
@@ -0,0 +1,18 @@
1
+ # Running Mongoid Tests
2
+
3
+ ## Overview
4
+ ### Quick Start
5
+ Spin up a MongoDB deployment against which to run the Mongoid specs. Mongoid specs support a variety of MongoDB topologies, but the simplest is a single MongoDB instance:
6
+
7
+ # Launch mongod in one terminal
8
+ mkdir /tmp/mdb
9
+ mongod --dbpath /tmp/mdb
10
+
11
+ Run the test suite in a separate terminal:
12
+
13
+ rake
14
+
15
+
16
+ ## Caveats
17
+ ### "Too many open files" error
18
+ On MacOS, you may encounter a "Too many open files" error on the MongoDB server when running the tests. If this happens, stop the server, run the command `ulimit -n 10000` in the same terminal session as the server, and restart the server. This will increase the number of files that can be opened. Then, re-run the tests.
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ class DelegatingPatient
5
+ include Mongoid::Document
6
+
7
+ embeds_one :email
8
+
9
+ # Instance level delegation
10
+ delegate :address, to: :email
11
+
12
+ class << self
13
+ # Class level delegation
14
+ delegate :default_client, to: ::Mongoid
15
+ end
16
+ end
@@ -0,0 +1,2 @@
1
+ class OtherOwnerObject
2
+ end
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'spec_helper'
5
+
6
+ BASE = File.join(File.dirname(__FILE__), '../..')
7
+ TMP_BASE = File.join(BASE, 'tmp')
8
+
9
+ describe 'Mongoid application tests' do
10
+ before(:all) do
11
+ unless SpecConfig.instance.app_tests?
12
+ skip 'Set APP_TESTS=1 in environment to run application tests'
13
+ end
14
+
15
+ require 'fileutils'
16
+ require 'support/child_process_helper'
17
+ require 'open-uri'
18
+
19
+ FileUtils.mkdir_p(TMP_BASE)
20
+ end
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
28
+
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
+ # 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
45
+
46
+ resp.should == []
47
+
48
+ status.should == 0
49
+ end
50
+ end
51
+ end
52
+
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
79
+
80
+ resp.should == []
81
+
82
+ # 143 = 128 + 15
83
+ [0, 15, 143].should include(status)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ 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/
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ def clone_application(repo_url, subdir: nil, rails_version: nil)
120
+ Dir.chdir(TMP_BASE) do
121
+ FileUtils.rm_rf(File.basename(repo_url))
122
+ ChildProcessHelper.check_call(%w(git clone) + [repo_url])
123
+ 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)
126
+ puts `git diff`
127
+
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
132
+
133
+ yield
134
+ end
135
+ end
136
+ end
137
+
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
146
+ end
147
+ end
148
+
149
+ gemfile_lines = IO.readlines('Gemfile')
150
+ gemfile_lines.delete_if do |line|
151
+ line =~ /mongoid/
152
+ end
153
+ gemfile_lines << "gem 'mongoid', path: '#{File.expand_path(BASE)}'\n"
154
+ if rails_version
155
+ gemfile_lines.delete_if do |line|
156
+ line =~ /rails/
157
+ end
158
+ gemfile_lines << "gem 'rails', '#{rails_version}'\n"
159
+ end
160
+ File.open('Gemfile', 'w') do |f|
161
+ f << gemfile_lines.join
162
+ end
163
+ end
164
+
165
+ def remove_spring
166
+ # Spring produces this error in Evergreen:
167
+ # /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
+ # in `initialize': too long unix socket path (126bytes given but 108bytes max) (ArgumentError)
169
+ # Is it trying to create unix sockets in current directory?
170
+ # https://stackoverflow.com/questions/30302021/rails-runner-without-spring
171
+ ChildProcessHelper.check_call(%w(bin/spring binstub --remove --all), env: clean_env)
172
+ end
173
+
174
+ def clean_env
175
+ @clean_env ||= Hash[ENV.keys.grep(/BUNDLE|RUBYOPT/).map { |k| [k, nil ] }]
176
+ end
177
+
178
+ def wait_for_port(port, timeout)
179
+ deadline = Time.now + timeout
180
+ loop do
181
+ begin
182
+ Socket.tcp('localhost', port, nil, nil, connect_timeout: 0.5) do |socket|
183
+ return
184
+ end
185
+ rescue IOError, SystemCallError
186
+ if Time.now > deadline
187
+ raise
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'spec_helper'
5
+ require_relative '../../mongoid/association/embedded/embeds_many_models'
6
+ require_relative '../../mongoid/association/embedded/embeds_one_models'
7
+
8
+ describe 'embedded associations' do
9
+
10
+ describe 'parent association' do
11
+ let(:parent) do
12
+ parent_cls.new
13
+ end
14
+
15
+ context 'embeds_one' do
16
+
17
+ shared_examples 'is set' do
18
+ it 'is set' do
19
+ parent.child = child_cls.new
20
+ parent.child.parent.should == parent
21
+ end
22
+ end
23
+
24
+ context 'class_name set without leading ::' do
25
+ let(:parent_cls) { EomParent }
26
+ let(:child_cls) { EomChild }
27
+
28
+ it_behaves_like 'is set'
29
+ end
30
+
31
+ context 'class_name set with leading ::' do
32
+ let(:parent_cls) { EomCcParent }
33
+ let(:child_cls) { EomCcChild }
34
+
35
+ it_behaves_like 'is set'
36
+ end
37
+ end
38
+
39
+ context 'embeds_many' do
40
+
41
+ let(:child) { parent.legislators.new }
42
+
43
+ shared_examples 'is set' do
44
+ it 'is set' do
45
+ child.congress.should == parent
46
+ end
47
+ end
48
+
49
+ context 'class_name set without leading ::' do
50
+ let(:parent_cls) { EmmCongress }
51
+
52
+ it_behaves_like 'is set'
53
+ end
54
+
55
+ context 'class_name set with leading ::' do
56
+ let(:parent_cls) { EmmCcCongress }
57
+
58
+ it_behaves_like 'is set'
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'spec_helper'
5
+
6
+ describe 'TimeWithZone in queries' do
7
+ context 'in a non-UTC time zone' do
8
+ let(:time_zone_name) { 'Pacific Time (US & Canada)' }
9
+
10
+ before do
11
+ time = Time.now
12
+ expect(time.utc_offset).not_to eq(time.in_time_zone(time_zone_name).utc_offset)
13
+ end
14
+
15
+ let(:time_in_zone) { Time.now.in_time_zone(time_zone_name) }
16
+
17
+ let(:view_lt) do
18
+ Agency.collection.find(updated_at: {'$lt' => time_in_zone + 10.minutes})
19
+ end
20
+
21
+ let(:view_gt) do
22
+ Agency.collection.find(updated_at: {'$gt' => time_in_zone - 10.minutes})
23
+ end
24
+
25
+ let!(:agency) { Agency.create!.reload }
26
+
27
+ it 'finds the document' do
28
+ view_lt.to_a.should == [agency.attributes]
29
+ view_gt.to_a.should == [agency.attributes]
30
+ end
31
+ end
32
+ end