mongoid 7.0.3 → 7.0.8

Sign up to get free protection for your applications and to get access to all the features.
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