mongoid 7.2.0 → 7.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +1 -1
  5. data/lib/mongoid/attributes.rb +8 -1
  6. data/lib/mongoid/criteria/queryable/selector.rb +0 -4
  7. data/lib/mongoid/document.rb +3 -2
  8. data/lib/mongoid/errors/mongoid_error.rb +1 -1
  9. data/lib/mongoid/interceptable.rb +3 -1
  10. data/lib/mongoid/matcher.rb +19 -43
  11. data/lib/mongoid/matcher/elem_match.rb +2 -1
  12. data/lib/mongoid/matcher/expression.rb +5 -14
  13. data/lib/mongoid/matcher/field_expression.rb +4 -5
  14. data/lib/mongoid/matcher/field_operator.rb +7 -11
  15. data/lib/mongoid/reloadable.rb +5 -0
  16. data/lib/mongoid/validatable/associated.rb +1 -1
  17. data/lib/mongoid/validatable/presence.rb +3 -3
  18. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  19. data/lib/mongoid/version.rb +1 -1
  20. data/lib/rails/generators/mongoid/config/config_generator.rb +8 -1
  21. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +1 -1
  22. data/spec/integration/app_spec.rb +174 -84
  23. data/spec/integration/callbacks_models.rb +49 -0
  24. data/spec/integration/callbacks_spec.rb +216 -0
  25. data/spec/integration/document_spec.rb +21 -0
  26. data/spec/integration/matcher_operator_data/elem_match.yml +46 -0
  27. data/spec/integration/matcher_operator_data/gt_types.yml +63 -0
  28. data/spec/integration/matcher_operator_data/gte_types.yml +15 -0
  29. data/spec/integration/matcher_operator_data/implicit_traversal.yml +96 -0
  30. data/spec/integration/matcher_operator_data/lt_types.yml +15 -0
  31. data/spec/integration/matcher_operator_data/lte_types.yml +15 -0
  32. data/spec/integration/matcher_operator_data/ne_types.yml +15 -0
  33. data/spec/lite_spec_helper.rb +3 -4
  34. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +50 -0
  35. data/spec/mongoid/atomic/paths_spec.rb +41 -0
  36. data/spec/mongoid/attributes_spec.rb +241 -0
  37. data/spec/mongoid/contextual/atomic_spec.rb +17 -4
  38. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +36 -0
  39. data/spec/mongoid/document_fields_spec.rb +26 -0
  40. data/spec/mongoid/document_query_spec.rb +51 -0
  41. data/spec/mongoid/errors/mongoid_error_spec.rb +20 -8
  42. data/spec/mongoid/matcher/extract_attribute_data/numeric_keys.yml +104 -0
  43. data/spec/mongoid/matcher/extract_attribute_data/traversal.yml +68 -88
  44. data/spec/mongoid/matcher/extract_attribute_spec.rb +3 -13
  45. data/spec/mongoid/persistable/settable_spec.rb +30 -0
  46. data/spec/mongoid/persistable_spec.rb +2 -2
  47. data/spec/shared/bin/get-mongodb-download-url +17 -0
  48. data/spec/shared/bin/s3-copy +45 -0
  49. data/spec/shared/bin/s3-upload +69 -0
  50. data/spec/shared/lib/mrss/cluster_config.rb +226 -0
  51. data/spec/shared/lib/mrss/constraints.rb +71 -6
  52. data/spec/shared/lib/mrss/docker_runner.rb +271 -0
  53. data/spec/shared/lib/mrss/lite_constraints.rb +16 -0
  54. data/spec/shared/lib/mrss/server_version_registry.rb +115 -0
  55. data/spec/shared/lib/mrss/spec_organizer.rb +32 -2
  56. data/spec/shared/lib/mrss/utils.rb +15 -0
  57. data/spec/shared/share/Dockerfile.erb +322 -0
  58. data/spec/shared/share/haproxy-1.conf +16 -0
  59. data/spec/shared/share/haproxy-2.conf +17 -0
  60. data/spec/shared/shlib/distro.sh +73 -0
  61. data/spec/shared/shlib/server.sh +317 -0
  62. data/spec/shared/shlib/set_env.sh +131 -0
  63. data/spec/spec_helper.rb +1 -1
  64. data/spec/support/models/customer.rb +11 -0
  65. data/spec/support/models/customer_address.rb +12 -0
  66. data/spec/support/models/dictionary.rb +6 -0
  67. data/spec/support/models/mop.rb +10 -0
  68. data/spec/support/spec_config.rb +8 -0
  69. metadata +554 -508
  70. metadata.gz.sig +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a81d403f49ffcc37e06200c91ab90a6205604730f7169ceb4a07d057ae61193
4
- data.tar.gz: 045ce8ed60ab931f1406e52f25f9e486d670ec1c76dff86ee3b299cbeca0adfe
3
+ metadata.gz: 5841b5911599ef45f1b350592ec4995d2a044e723b14ef03e16cf7508449d396
4
+ data.tar.gz: c6276353b2e03f57c166c96f7bbcee0362a391f91e19c294a7c76a3c3dad3fe6
5
5
  SHA512:
6
- metadata.gz: bebd7b18d65049cd7d0793319a65bf1868f3c9addf09c21c7b4dbf315deb2fda27cf1a8f27be5288245ab2b1c830d21752de9042d01fb9b31579cab7536ccdcf
7
- data.tar.gz: 4a4b231ce42eb8b5bf07eca5131ff4b9e5c838a0965996adcf9a529ad6c398f11e0c9e4e5d7cc005fe969e4851a50a2d6aab7c436af433a809c6090ff1927b9c
6
+ metadata.gz: 277666610a79f335d47add6f4fb24ecb4a47ea6a245f7d28cec43258876ba469463460a98a1eeeeee5f200a5ab58ed6da7c81dec3be9d617d5ba970194949e09
7
+ data.tar.gz: a564a89c548c566a230d03ad69f6323280c071a04f3754f7a2abdeb1da883a4fa6856e7b9e091fdfb0325017f160eb81c875169da54bc04c865e2499f4cd31e9
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -19,7 +19,7 @@ Compatibility
19
19
 
20
20
  Mongoid supports and is tested against:
21
21
 
22
- - MRI 2.3-2.7
22
+ - MRI 2.3-3.0
23
23
  - JRuby 9.2
24
24
  - MongoDB server 2.6-4.4
25
25
 
@@ -160,6 +160,11 @@ module Mongoid
160
160
  # @since 1.0.0
161
161
  def write_attribute(name, value)
162
162
  field_name = database_field_name(name)
163
+
164
+ if attribute_missing?(field_name)
165
+ raise ActiveModel::MissingAttributeError, "Missing attribute: '#{name}'"
166
+ end
167
+
163
168
  if attribute_writable?(field_name)
164
169
  _assigning do
165
170
  validate_attribute_value(field_name, value)
@@ -177,6 +182,8 @@ module Mongoid
177
182
  end
178
183
  typed_value
179
184
  end
185
+ else
186
+ # TODO: MONGOID-5072
180
187
  end
181
188
  end
182
189
  alias :[]= :write_attribute
@@ -294,7 +301,7 @@ module Mongoid
294
301
  def read_raw_attribute(name)
295
302
  normalized = database_field_name(name.to_s)
296
303
  if attribute_missing?(normalized)
297
- raise ActiveModel::MissingAttributeError, "Missing attribute: '#{name}'."
304
+ raise ActiveModel::MissingAttributeError, "Missing attribute: '#{name}'"
298
305
  end
299
306
  if hash_dot_syntax?(normalized)
300
307
  attributes.__nested__(normalized)
@@ -123,10 +123,6 @@ module Mongoid
123
123
  # {'foo' => {'$lt' => 5}}. This step should be done after all
124
124
  # value-based processing is complete.
125
125
  if key.is_a?(Key)
126
- if serializer && evolved_value != value
127
- raise NotImplementedError, "This method is not prepared to handle key being a Key and serializer being not nil"
128
- end
129
-
130
126
  evolved_value = key.transform_value(evolved_value)
131
127
  end
132
128
 
@@ -229,8 +229,9 @@ module Mongoid
229
229
  became = klass.new(clone_document)
230
230
  became._id = _id
231
231
  became.instance_variable_set(:@changed_attributes, changed_attributes)
232
- became.instance_variable_set(:@errors, ActiveModel::Errors.new(became))
233
- became.errors.instance_variable_set(:@messages, errors.instance_variable_get(:@messages))
232
+ new_errors = ActiveModel::Errors.new(became)
233
+ new_errors.copy!(errors)
234
+ became.instance_variable_set(:@errors, new_errors)
234
235
  became.instance_variable_set(:@new_record, new_record?)
235
236
  became.instance_variable_set(:@destroyed, destroyed?)
236
237
  became.changed_attributes[klass.discriminator_key] = self.class.discriminator_value
@@ -48,7 +48,7 @@ module Mongoid
48
48
  #
49
49
  # @return [ String ] A localized error message string.
50
50
  def translate(key, options)
51
- ::I18n.translate("#{BASE_KEY}.#{key}", options)
51
+ ::I18n.translate("#{BASE_KEY}.#{key}", **options)
52
52
  end
53
53
 
54
54
  # Create the problem.
@@ -234,9 +234,11 @@ module Mongoid
234
234
  # document.halted_callback_hook(filter)
235
235
  #
236
236
  # @param [ Symbol ] filter The callback that halted.
237
+ # @param [ Symbol ] name The name of the callback that was halted
238
+ # (requires Rails 6.1+)
237
239
  #
238
240
  # @since 3.0.3
239
- def halted_callback_hook(filter)
241
+ def halted_callback_hook(filter, name = nil)
240
242
  @before_callback_halted = true
241
243
  end
242
244
 
@@ -43,60 +43,36 @@ module Mongoid
43
43
  document = BSON::Document.new(document.send(:as_attributes))
44
44
  end
45
45
 
46
- src = document
47
- expanded = false
48
- exists = true
46
+ current = [document]
49
47
 
50
48
  key.to_s.split('.').each do |field|
51
- if (index = field.to_i).to_s == field
52
- # Array indexing
53
- if Array === src
54
- exists = index < src.length
55
- src = src[index]
56
- else
57
- # Trying to index something that is not an array
58
- exists = false
59
- src = nil
60
- end
61
- else
62
- case src
63
- when nil
64
- exists = false
49
+ new = []
50
+ current.each do |doc|
51
+ case doc
65
52
  when Hash
66
- exists = src.key?(field)
67
- src = src[field]
53
+ if doc.key?(field)
54
+ new << doc[field]
55
+ end
68
56
  when Array
69
- expanded = true
70
- exists = false
71
- new = []
72
- src.each do |doc|
73
- case doc
74
- when Hash
75
- if doc.key?(field)
76
- v = doc[field]
77
- case v
78
- when Array
79
- new += v
80
- else
81
- new += [v]
82
- end
83
- exists = true
57
+ if (index = field.to_i).to_s == field
58
+ if doc.length > index
59
+ new << doc[index]
60
+ end
61
+ end
62
+ doc.each do |subdoc|
63
+ if Hash === subdoc
64
+ if subdoc.key?(field)
65
+ new << subdoc[field]
84
66
  end
85
- else
86
- # Trying to hash index into a value that is not a hash
87
67
  end
88
68
  end
89
- src = new
90
- else
91
- # Trying to descend into a field that is not a hash using
92
- # dot notation.
93
- exists = false
94
- src = nil
95
69
  end
96
70
  end
71
+ current = new
72
+ break if current.empty?
97
73
  end
98
74
 
99
- [exists, src, expanded]
75
+ current
100
76
  end
101
77
  end
102
78
  end
@@ -15,7 +15,8 @@ module Mongoid
15
15
  # Validate the condition is valid, even though we will never attempt
16
16
  # matching it.
17
17
  condition.each do |k, v|
18
- if k.to_s.start_with?('$')
18
+ k = k.to_s
19
+ if k.start_with?('$')
19
20
  begin
20
21
  ExpressionOperator.get(k)
21
22
  rescue Mongoid::Errors::InvalidExpressionOperator
@@ -15,22 +15,13 @@ module Mongoid
15
15
  if k.start_with?('$')
16
16
  ExpressionOperator.get(k).matches?(document, expr_v)
17
17
  else
18
- exists, value, expanded = Matcher.extract_attribute(document, k)
19
- # The value may have been expanded into an array, but then
20
- # array may have been shrunk back to a scalar (or hash) when
21
- # path contained a numeric position.
22
- # Do not treat a hash as an array here (both are iterable).
23
- if expanded && Array === value
24
- if value == []
25
- # Empty array is technically equivalent to exists: false.
26
- FieldExpression.matches?(false, nil, expr_v)
27
- else
28
- value.any? do |v|
29
- FieldExpression.matches?(true, v, expr_v)
30
- end
18
+ values = Matcher.extract_attribute(document, k)
19
+ if values.length > 0
20
+ values.any? do |v|
21
+ FieldExpression.matches?(true, v, expr_v)
31
22
  end
32
23
  else
33
- FieldExpression.matches?(exists, value, expr_v)
24
+ FieldExpression.matches?(false, nil, expr_v)
34
25
  end
35
26
  end
36
27
  end
@@ -36,14 +36,13 @@ module Mongoid
36
36
  FieldOperator.get(k).matches?(exists, value, cond_v)
37
37
  end
38
38
  elsif Hash === value
39
- sub_exists, sub_value, expanded =
40
- Matcher.extract_attribute(value, k)
41
- if expanded
42
- sub_value.any? do |sub_v|
39
+ sub_values = Matcher.extract_attribute(value, k)
40
+ if sub_values.length > 0
41
+ sub_values.any? do |sub_v|
43
42
  Eq.matches?(true, sub_v, cond_v)
44
43
  end
45
44
  else
46
- Eq.matches?(sub_exists, sub_value, cond_v)
45
+ Eq.matches?(false, nil, cond_v)
47
46
  end
48
47
  else
49
48
  false
@@ -35,17 +35,13 @@ module Mongoid
35
35
  end
36
36
 
37
37
  module_function def apply_comparison_operator(operator, left, right)
38
- case left
39
- when Numeric
40
- case right
41
- when Numeric
42
- left.send(operator, right)
43
- else
44
- false
45
- end
46
- else
47
- false
48
- end
38
+ left.send(operator, right)
39
+ rescue ArgumentError, NoMethodError, TypeError
40
+ # We silence bogus comparison attempts, e.g. number to string
41
+ # comparisons.
42
+ # Several different exceptions may be produced depending on the types
43
+ # involved.
44
+ false
49
45
  end
50
46
  end
51
47
  end
@@ -21,6 +21,11 @@ module Mongoid
21
21
  #
22
22
  # @since 1.0.0
23
23
  def reload
24
+ if @atomic_selector
25
+ # Clear atomic_selector cache for sharded clusters. MONGOID-5076
26
+ remove_instance_variable('@atomic_selector')
27
+ end
28
+
24
29
  reloaded = _reload
25
30
  if Mongoid.raise_not_found_error && reloaded.empty?
26
31
  raise Errors::DocumentNotFound.new(self.class, _id, _id)
@@ -43,7 +43,7 @@ module Mongoid
43
43
  ensure
44
44
  document.exit_validate
45
45
  end
46
- document.errors.add(attribute, :invalid, options) unless valid
46
+ document.errors.add(attribute, :invalid, **options) unless valid
47
47
  end
48
48
  end
49
49
  end
@@ -34,15 +34,15 @@ module Mongoid
34
34
  document.errors.add(
35
35
  attribute,
36
36
  :blank_in_locale,
37
- options.merge(location: _locale)
37
+ **options.merge(location: _locale)
38
38
  ) if not_present?(_value)
39
39
  end
40
40
  elsif document.relations.has_key?(attribute.to_s)
41
41
  if relation_or_fk_missing?(document, attribute, value)
42
- document.errors.add(attribute, :blank, options)
42
+ document.errors.add(attribute, :blank, **options)
43
43
  end
44
44
  else
45
- document.errors.add(attribute, :blank, options) if not_present?(value)
45
+ document.errors.add(attribute, :blank, **options) if not_present?(value)
46
46
  end
47
47
  end
48
48
 
@@ -68,7 +68,7 @@ module Mongoid
68
68
  # @since 2.4.10
69
69
  def add_error(document, attribute, value)
70
70
  document.errors.add(
71
- attribute, :taken, options.except(:case_sensitive, :scope).merge(value: value)
71
+ attribute, :taken, **options.except(:case_sensitive, :scope).merge(value: value)
72
72
  )
73
73
  end
74
74
 
@@ -2,5 +2,5 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  module Mongoid
5
- VERSION = "7.2.0"
5
+ VERSION = "7.2.4"
6
6
  end
@@ -15,7 +15,14 @@ module Mongoid
15
15
  end
16
16
 
17
17
  def app_name
18
- Rails::Application.subclasses.first.parent.to_s.underscore
18
+ app_cls = Rails.application.class
19
+ parent = begin
20
+ # Rails 6.1+
21
+ app_cls.module_parent_name
22
+ rescue NoMethodError
23
+ app_cls.parent.to_s
24
+ end
25
+ parent.underscore
19
26
  end
20
27
 
21
28
  def create_config_file
@@ -57,7 +57,7 @@ development:
57
57
  # connect: :direct
58
58
 
59
59
  # Changes the default time in seconds the server monitors refresh their status
60
- # via ismaster commands. (default: 10)
60
+ # via hello commands. (default: 10)
61
61
  # heartbeat_frequency: 10
62
62
 
63
63
  # The time in seconds for selecting servers for a near read preference. (default: 0.015)
@@ -19,103 +19,125 @@ describe 'Mongoid application tests' do
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
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
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
29
+
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
- Mrss::ChildProcessHelper.check_call(%w(gem uni rails -a))
95
- Mrss::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
- Mrss::ChildProcessHelper.check_call(%w(rails new mongoid-test --skip-spring --skip-active-record), env: clean_env)
100
-
101
- Dir.chdir('mongoid-test') do
102
- adjust_app_gemfile
103
- Mrss::ChildProcessHelper.check_call(%w(bundle install), env: clean_env)
104
-
105
- Mrss::ChildProcessHelper.check_call(%w(rails g model post), env: clean_env)
106
- Mrss::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
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/
114
128
  end
115
129
  end
116
130
  end
117
131
  end
118
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
+
119
141
  context 'local test applications' do
120
142
  let(:client) { Mongoid.default_client }
121
143
 
@@ -150,7 +172,7 @@ describe 'Mongoid application tests' do
150
172
  end
151
173
  index.should be nil
152
174
 
153
- Mrss::ChildProcessHelper.check_call(%w(rake db:mongoid:create_indexes),
175
+ Mrss::ChildProcessHelper.check_call(%w(bundle exec rake db:mongoid:create_indexes),
154
176
  cwd: APP_PATH, env: env)
155
177
 
156
178
  index = client['posts'].indexes.detect do |index|
@@ -165,12 +187,13 @@ describe 'Mongoid application tests' do
165
187
  end
166
188
  end
167
189
 
168
- def clone_application(repo_url, subdir: nil, rails_version: nil)
190
+ def clone_application(repo_url, subdir: nil)
169
191
  Dir.chdir(TMP_BASE) do
170
192
  FileUtils.rm_rf(File.basename(repo_url))
171
193
  Mrss::ChildProcessHelper.check_call(%w(git clone) + [repo_url])
172
194
  Dir.chdir(File.join(*[File.basename(repo_url), subdir].compact)) do
173
- adjust_app_gemfile(rails_version: rails_version)
195
+ adjust_app_gemfile
196
+ adjust_rails_defaults
174
197
  Mrss::ChildProcessHelper.check_call(%w(bundle install), env: clean_env)
175
198
  puts `git diff`
176
199
 
@@ -181,11 +204,44 @@ describe 'Mongoid application tests' do
181
204
  end
182
205
  end
183
206
 
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
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
+
184
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
185
241
  env_config = {'clients' => {'default' => {
186
242
  # TODO massive hack, will fail if uri specifies a database name or
187
243
  # any uri options
188
- 'uri' => "#{SpecConfig.instance.uri_str}/mongoid_test",
244
+ 'uri' => uri,
189
245
  }}}
190
246
  config = {'development' => env_config, 'production' => env_config}
191
247
  File.open('config/mongoid.yml', 'w') do |f|
@@ -193,7 +249,7 @@ describe 'Mongoid application tests' do
193
249
  end
194
250
  end
195
251
 
196
- def adjust_app_gemfile(rails_version: nil)
252
+ def adjust_app_gemfile(rails_version: SpecConfig.instance.rails_version)
197
253
  remove_bundler_req
198
254
 
199
255
  gemfile_lines = IO.readlines('Gemfile')
@@ -205,14 +261,45 @@ describe 'Mongoid application tests' do
205
261
  gemfile_lines.delete_if do |line|
206
262
  line =~ /rails/
207
263
  end
208
- 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
209
269
  end
210
270
  File.open('Gemfile', 'w') do |f|
211
271
  f << gemfile_lines.join
212
272
  end
213
273
  end
214
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
+
215
299
  def remove_bundler_req
300
+ return unless File.file?('Gemfile.lock')
301
+ # TODO: Remove this method completely when we get rid of .lock files in
302
+ # mongoid-demo apps.
216
303
  lock_lines = IO.readlines('Gemfile.lock')
217
304
  # Get rid of the bundled with line so that whatever bundler is installed
218
305
  # on the system is usable with the application.
@@ -237,7 +324,7 @@ describe 'Mongoid application tests' do
237
324
  @clean_env ||= Hash[ENV.keys.grep(/BUNDLE|RUBYOPT/).map { |k| [k, nil ] }]
238
325
  end
239
326
 
240
- def wait_for_port(port, timeout)
327
+ def wait_for_port(port, timeout, process)
241
328
  deadline = Time.now + timeout
242
329
  loop do
243
330
  begin
@@ -245,6 +332,9 @@ describe 'Mongoid application tests' do
245
332
  return
246
333
  end
247
334
  rescue IOError, SystemCallError
335
+ unless process.alive?
336
+ raise "Process #{process} died while waiting for port #{port}"
337
+ end
248
338
  if Time.now > deadline
249
339
  raise
250
340
  end