shoulda-matchers 2.8.0.rc1 → 2.8.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +3 -0
  3. data/.hound_config/ruby.yml +5 -0
  4. data/.travis.yml +4 -0
  5. data/.yardopts +1 -1
  6. data/Appraisals +5 -2
  7. data/Gemfile +1 -1
  8. data/Gemfile.lock +7 -5
  9. data/NEWS.md +24 -0
  10. data/README.md +9 -0
  11. data/gemfiles/3.0.gemfile +1 -1
  12. data/gemfiles/3.0.gemfile.lock +7 -5
  13. data/gemfiles/3.1.gemfile +1 -1
  14. data/gemfiles/3.1.gemfile.lock +7 -5
  15. data/gemfiles/3.1_1.9.2.gemfile +1 -3
  16. data/gemfiles/3.1_1.9.2.gemfile.lock +7 -12
  17. data/gemfiles/3.2.gemfile +1 -1
  18. data/gemfiles/3.2.gemfile.lock +7 -5
  19. data/gemfiles/3.2_1.9.2.gemfile +1 -3
  20. data/gemfiles/3.2_1.9.2.gemfile.lock +5 -10
  21. data/gemfiles/4.0.0.gemfile +1 -1
  22. data/gemfiles/4.0.0.gemfile.lock +7 -5
  23. data/gemfiles/4.0.1.gemfile +1 -1
  24. data/gemfiles/4.0.1.gemfile.lock +7 -5
  25. data/gemfiles/4.1.gemfile +1 -1
  26. data/gemfiles/4.1.gemfile.lock +7 -5
  27. data/gemfiles/4.2.gemfile +5 -3
  28. data/gemfiles/4.2.gemfile.lock +26 -8
  29. data/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +2 -0
  30. data/lib/shoulda/matchers/active_model.rb +3 -2
  31. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +44 -32
  32. data/lib/shoulda/matchers/active_model/helpers.rb +8 -22
  33. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +66 -19
  34. data/lib/shoulda/matchers/active_model/strict_validator.rb +51 -0
  35. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +14 -13
  36. data/lib/shoulda/matchers/active_model/validator.rb +113 -0
  37. data/lib/shoulda/matchers/active_model/validator_with_captured_range_error.rb +12 -0
  38. data/lib/shoulda/matchers/active_record/association_matcher.rb +19 -1
  39. data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +23 -2
  40. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +14 -11
  41. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +1 -0
  42. data/lib/shoulda/matchers/active_record/uniqueness/model.rb +1 -0
  43. data/lib/shoulda/matchers/active_record/uniqueness/namespace.rb +1 -0
  44. data/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb +1 -0
  45. data/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +1 -0
  46. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +22 -15
  47. data/lib/shoulda/matchers/matcher_context.rb +1 -0
  48. data/lib/shoulda/matchers/rails_shim.rb +22 -0
  49. data/lib/shoulda/matchers/util.rb +5 -0
  50. data/lib/shoulda/matchers/version.rb +1 -1
  51. data/script/update_gem_in_all_appraisals +15 -0
  52. data/shoulda-matchers.gemspec +1 -1
  53. data/spec/support/unit/helpers/active_model_helpers.rb +3 -1
  54. data/spec/support/unit/helpers/active_model_versions.rb +8 -0
  55. data/spec/support/unit/helpers/active_record_versions.rb +16 -0
  56. data/spec/support/unit/helpers/rails_versions.rb +4 -4
  57. data/spec/support/unit/matchers/fail_with_message_matcher.rb +9 -8
  58. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +74 -0
  59. data/spec/unit/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +75 -0
  60. data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +24 -0
  61. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +78 -5
  62. data/spec/unit/shoulda/matchers/active_record/association_matcher_spec.rb +31 -2
  63. data/spec/unit_spec_helper.rb +2 -0
  64. metadata +11 -8
  65. data/lib/shoulda/matchers/active_model/exception_message_finder.rb +0 -58
  66. data/lib/shoulda/matchers/active_model/validation_message_finder.rb +0 -69
  67. data/spec/unit/shoulda/matchers/active_model/exception_message_finder_spec.rb +0 -111
  68. data/spec/unit/shoulda/matchers/active_model/validation_message_finder_spec.rb +0 -129
@@ -263,6 +263,7 @@ module Shoulda
263
263
  "#{model_class.name} to #{description}"
264
264
  end
265
265
 
266
+ # @private
266
267
  class DecoratedColumn < SimpleDelegator
267
268
  def initialize(model, column)
268
269
  @model = model
@@ -1,6 +1,7 @@
1
1
  module Shoulda
2
2
  module Matchers
3
3
  module ActiveRecord
4
+ # @private
4
5
  module Uniqueness
5
6
  # @private
6
7
  class Model
@@ -1,6 +1,7 @@
1
1
  module Shoulda
2
2
  module Matchers
3
3
  module ActiveRecord
4
+ # @private
4
5
  module Uniqueness
5
6
  # @private
6
7
  class Namespace
@@ -3,6 +3,7 @@ require 'thread'
3
3
  module Shoulda
4
4
  module Matchers
5
5
  module ActiveRecord
6
+ # @private
6
7
  module Uniqueness
7
8
  # @private
8
9
  class TestModelCreator
@@ -3,6 +3,7 @@ require 'thread'
3
3
  module Shoulda
4
4
  module Matchers
5
5
  module ActiveRecord
6
+ # @private
6
7
  module Uniqueness
7
8
  # @private
8
9
  module TestModels
@@ -374,21 +374,7 @@ module Shoulda
374
374
  # Assume the scope is a foreign key if the field is nil
375
375
  previous_value ||= correct_type_for_column(@subject.class.columns_hash[scope.to_s])
376
376
 
377
- next_value =
378
- if @subject.class.respond_to?(:defined_enums) && @subject.defined_enums[scope.to_s]
379
- available_values = @subject.defined_enums[scope.to_s].reject do |key, _|
380
- key == previous_value
381
- end
382
- available_values.keys.last
383
- elsif scope.to_s =~ /_type$/ && model_class?(previous_value)
384
- Uniqueness::TestModels.create(previous_value).to_s
385
- elsif previous_value.respond_to?(:next)
386
- previous_value.next
387
- elsif previous_value.respond_to?(:to_datetime)
388
- previous_value.to_datetime.next
389
- else
390
- previous_value.to_s.next
391
- end
377
+ next_value = next_value_for(scope, previous_value)
392
378
 
393
379
  @subject.__send__("#{scope}=", next_value)
394
380
 
@@ -418,6 +404,27 @@ module Shoulda
418
404
  end
419
405
  end
420
406
 
407
+ def next_value_for(scope, previous_value)
408
+ if @subject.class.respond_to?(:defined_enums) && @subject.defined_enums[scope.to_s]
409
+ available_values = available_enum_values_for(scope, previous_value)
410
+ available_values.keys.last
411
+ elsif scope.to_s =~ /_type$/ && model_class?(previous_value)
412
+ Uniqueness::TestModels.create(previous_value).to_s
413
+ elsif previous_value.respond_to?(:next)
414
+ previous_value.next
415
+ elsif previous_value.respond_to?(:to_datetime)
416
+ previous_value.to_datetime.next
417
+ else
418
+ previous_value.to_s.next
419
+ end
420
+ end
421
+
422
+ def available_enum_values_for(scope, previous_value)
423
+ @subject.defined_enums[scope.to_s].reject do |key, _|
424
+ key == previous_value
425
+ end
426
+ end
427
+
421
428
  def class_name
422
429
  @subject.class.name
423
430
  end
@@ -1,5 +1,6 @@
1
1
  module Shoulda
2
2
  module Matchers
3
+ # @private
3
4
  class MatcherContext
4
5
  def initialize(context)
5
6
  @context = context
@@ -65,6 +65,28 @@ module Shoulda
65
65
  end
66
66
  end
67
67
 
68
+ def self.generate_validation_message(record, attribute, type, model_name, options)
69
+ if record && record.errors.respond_to?(:generate_message)
70
+ record.errors.generate_message(attribute.to_sym, type, options)
71
+ else
72
+ simply_generate_validation_message(attribute, type, model_name, options)
73
+ end
74
+ rescue RangeError
75
+ simply_generate_validation_message(attribute, type, model_name, options)
76
+ end
77
+
78
+ def self.simply_generate_validation_message(attribute, type, model_name, options)
79
+ default_translation_keys = [
80
+ :"activerecord.errors.models.#{model_name}.#{type}",
81
+ :"activerecord.errors.messages.#{type}",
82
+ :"errors.attributes.#{attribute}.#{type}",
83
+ :"errors.messages.#{type}"
84
+ ]
85
+ primary_translation_key = :"activerecord.errors.models.#{model_name}.attributes.#{attribute}.#{type}"
86
+ translate_options = { default: default_translation_keys }.merge(options)
87
+ I18n.translate(primary_translation_key, translate_options)
88
+ end
89
+
68
90
  def self.active_record_major_version
69
91
  ::ActiveRecord::VERSION::MAJOR
70
92
  end
@@ -23,6 +23,11 @@ module Shoulda
23
23
  end
24
24
  end
25
25
  end
26
+
27
+ def self.indent(string, width)
28
+ indentation = ' ' * width
29
+ string.split(/[\n\r]/).map { |line| indentation + line }.join("\n")
30
+ end
26
31
  end
27
32
  end
28
33
  end
@@ -1,6 +1,6 @@
1
1
  module Shoulda
2
2
  module Matchers
3
3
  # @private
4
- VERSION = '2.8.0.rc1'.freeze
4
+ VERSION = '2.8.0.rc2'.freeze
5
5
  end
6
6
  end
@@ -0,0 +1,15 @@
1
+ #!/bin/bash
2
+
3
+ SUPPORTED_VERSIONS=$(<script/SUPPORTED_VERSIONS)
4
+ gem="$1"
5
+
6
+ update-gem-for-version() {
7
+ local version="$1"
8
+ (export RBENV_VERSION=$version; bundle update "$gem" && bundle exec appraisal update "$gem")
9
+ }
10
+
11
+ for version in $SUPPORTED_VERSIONS; do
12
+ echo
13
+ echo "*** Updating $gem for $version ***"
14
+ update-gem-for-version $version
15
+ done
@@ -5,7 +5,7 @@ Gem::Specification.new do |s|
5
5
  s.name = "shoulda-matchers"
6
6
  s.version = Shoulda::Matchers::VERSION.dup
7
7
  s.authors = ["Tammer Saleh", "Joe Ferris", "Ryan McGeary", "Dan Croak",
8
- "Matt Jankowski", "Stafford Brunk"]
8
+ "Matt Jankowski", "Stafford Brunk", "Elliot Winkler"]
9
9
  s.date = Time.now.strftime("%Y-%m-%d")
10
10
  s.email = "support@thoughtbot.com"
11
11
  s.homepage = "http://thoughtbot.com/community/"
@@ -7,8 +7,10 @@ module UnitTests
7
7
  def custom_validation(options = {}, &block)
8
8
  attribute_name = options.fetch(:attribute_name, :attr)
9
9
  attribute_type = options.fetch(:attribute_type, :integer)
10
+ column_options = options.fetch(:column_options, {})
11
+ attribute_options = { type: attribute_type, options: column_options }
10
12
 
11
- define_model(:example, attribute_name => attribute_type) do
13
+ define_model(:example, attribute_name => attribute_options) do
12
14
  validate :custom_validation
13
15
 
14
16
  define_method(:custom_validation, &block)
@@ -5,6 +5,10 @@ module UnitTests
5
5
  example_group.extend(self)
6
6
  end
7
7
 
8
+ def active_model_version
9
+ Tests::Version.new(::ActiveModel::VERSION::STRING)
10
+ end
11
+
8
12
  def active_model_3_1?
9
13
  (::ActiveModel::VERSION::MAJOR == 3 && ::ActiveModel::VERSION::MINOR >= 1) || active_model_4_0?
10
14
  end
@@ -16,5 +20,9 @@ module UnitTests
16
20
  def active_model_4_0?
17
21
  ::ActiveModel::VERSION::MAJOR == 4
18
22
  end
23
+
24
+ def active_model_supports_strict?
25
+ active_model_version >= 3.2
26
+ end
19
27
  end
20
28
  end
@@ -0,0 +1,16 @@
1
+ module UnitTests
2
+ module ActiveRecordVersions
3
+ def self.configure_example_group(example_group)
4
+ example_group.include(self)
5
+ example_group.extend(self)
6
+ end
7
+
8
+ def active_record_version
9
+ Tests::Version.new(ActiveRecord::VERSION::STRING)
10
+ end
11
+
12
+ def active_record_can_raise_range_error?
13
+ active_record_version >= 4.2
14
+ end
15
+ end
16
+ end
@@ -6,19 +6,19 @@ module UnitTests
6
6
  end
7
7
 
8
8
  def rails_version
9
- Gem::Version.new(Rails::VERSION::STRING)
9
+ Tests::Version.new(Rails::VERSION::STRING)
10
10
  end
11
11
 
12
12
  def rails_3_x?
13
- Gem::Requirement.new('~> 3.0').satisfied_by?(rails_version)
13
+ rails_version =~ '~> 3.0'
14
14
  end
15
15
 
16
16
  def rails_4_x?
17
- Gem::Requirement.new('~> 4.0').satisfied_by?(rails_version)
17
+ rails_version =~ '~> 4.0'
18
18
  end
19
19
 
20
20
  def rails_gte_4_1?
21
- Gem::Requirement.new('>= 4.1').satisfied_by?(rails_version)
21
+ rails_version >= 4.1
22
22
  end
23
23
 
24
24
  def active_record_supports_enum?
@@ -20,15 +20,17 @@ module UnitTests
20
20
  end
21
21
 
22
22
  def failure_message
23
- msg = "Expectation should have failed with message '#{expected}'"
23
+ lines = ['Expectation should have failed with message:']
24
+ lines << Shoulda::Matchers::Util.indent(expected, 2)
24
25
 
25
26
  if @actual
26
- msg << ",\nactually failed with '#{@actual}'"
27
+ lines << 'Actually failed with:'
28
+ lines << Shoulda::Matchers::Util.indent(@actual, 2)
27
29
  else
28
- msg << ", but did not fail."
30
+ lines << 'However, the expectation did not fail.'
29
31
  end
30
32
 
31
- msg
33
+ lines.join("\n")
32
34
  end
33
35
 
34
36
  def failure_message_for_should
@@ -36,10 +38,9 @@ module UnitTests
36
38
  end
37
39
 
38
40
  def failure_message_when_negated
39
- msg = "Expectation should not have failed with message '#{expected}'"
40
- msg << ", but did."
41
-
42
- msg
41
+ lines = ['Expectation should not have failed with message:']
42
+ lines << Shoulda::Matchers::Util.indent(expected, 2)
43
+ lines.join("\n")
43
44
  end
44
45
 
45
46
  def failure_message_for_should_not
@@ -227,4 +227,78 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher, type: :model do
227
227
  end
228
228
  end
229
229
  end
230
+
231
+ if active_record_can_raise_range_error?
232
+ context 'when the value is outside of the range of the column' do
233
+ context 'not qualified with strict' do
234
+ it 'rejects, failing with the correct message' do
235
+ attribute_options = { type: :integer, options: { limit: 2 } }
236
+ record = define_model(:example, attr: attribute_options).new
237
+ assertion = -> { expect(record).to allow_value(100000).for(:attr) }
238
+ message = <<-MESSAGE.strip_heredoc.strip
239
+ Did not expect errors when attr is set to 100000,
240
+ got RangeError: "100000 is out of range for ActiveRecord::Type::Integer with limit 2"
241
+ MESSAGE
242
+ expect(&assertion).to fail_with_message(message)
243
+ end
244
+
245
+ context 'qualified with a message' do
246
+ it 'ignores any specified message, failing with the correct message' do
247
+ attribute_options = { type: :integer, options: { limit: 2 } }
248
+ record = define_model(:example, attr: attribute_options).new
249
+ assertion = -> do
250
+ expect(record).
251
+ to allow_value(100000).
252
+ for(:attr).
253
+ with_message('some message')
254
+ end
255
+ message = <<-MESSAGE.strip_heredoc.strip
256
+ Did not expect errors to include "some message" when attr is set to 100000,
257
+ got RangeError: "100000 is out of range for ActiveRecord::Type::Integer with limit 2"
258
+ MESSAGE
259
+ expect(&assertion).to fail_with_message(message)
260
+ end
261
+ end
262
+ end
263
+
264
+ if active_model_supports_strict?
265
+ context 'qualified with strict' do
266
+ it 'rejects, failing with the correct message' do
267
+ attribute_options = { type: :integer, options: { limit: 2 } }
268
+ record = define_model(:example, attr: attribute_options).new
269
+ assertion = -> do
270
+ expect(record).
271
+ to allow_value(100000).
272
+ for(:attr).
273
+ strict
274
+ end
275
+ message = <<-MESSAGE.strip_heredoc.strip
276
+ Did not expect an exception to have been raised when attr is set to 100000,
277
+ got RangeError: "100000 is out of range for ActiveRecord::Type::Integer with limit 2"
278
+ MESSAGE
279
+ expect(&assertion).to fail_with_message(message)
280
+ end
281
+
282
+ context 'qualified with a message' do
283
+ it 'ignores any specified message' do
284
+ attribute_options = { type: :integer, options: { limit: 2 } }
285
+ record = define_model(:example, attr: attribute_options).new
286
+ assertion = -> do
287
+ expect(record).
288
+ to allow_value(100000).
289
+ for(:attr).
290
+ with_message('some message').
291
+ strict
292
+ end
293
+ message = <<-MESSAGE.strip_heredoc.strip
294
+ Did not expect exception to include "some message" when attr is set to 100000,
295
+ got RangeError: "100000 is out of range for ActiveRecord::Type::Integer with limit 2"
296
+ MESSAGE
297
+ expect(&assertion).to fail_with_message(message)
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end
303
+ end
230
304
  end
@@ -79,7 +79,82 @@ describe Shoulda::Matchers::ActiveModel::DisallowValueMatcher, type: :model do
79
79
  end
80
80
  end
81
81
 
82
+ if active_record_can_raise_range_error?
83
+ context 'when the value is outside of the range of the column' do
84
+ context 'not qualified with strict' do
85
+ it 'accepts, failing with the correct message' do
86
+ attribute_options = { type: :integer, options: { limit: 2 } }
87
+ record = define_model(:example, attr: attribute_options).new
88
+ assertion = -> { expect(record).not_to disallow_value(100000).for(:attr) }
89
+ message = <<-MESSAGE.strip_heredoc.strip
90
+ Did not expect errors when attr is set to 100000,
91
+ got RangeError: "100000 is out of range for ActiveRecord::Type::Integer with limit 2"
92
+ MESSAGE
93
+ expect(&assertion).to fail_with_message(message)
94
+ end
95
+
96
+ context 'qualified with a message' do
97
+ it 'ignores any specified message, failing with the correct message' do
98
+ attribute_options = { type: :integer, options: { limit: 2 } }
99
+ record = define_model(:example, attr: attribute_options).new
100
+ assertion = -> do
101
+ expect(record).
102
+ not_to disallow_value(100000).
103
+ for(:attr).
104
+ with_message('some message')
105
+ end
106
+ message = <<-MESSAGE.strip_heredoc.strip
107
+ Did not expect errors to include "some message" when attr is set to 100000,
108
+ got RangeError: "100000 is out of range for ActiveRecord::Type::Integer with limit 2"
109
+ MESSAGE
110
+ expect(&assertion).to fail_with_message(message)
111
+ end
112
+ end
113
+ end
114
+
115
+ if active_model_supports_strict?
116
+ context 'qualified with strict' do
117
+ it 'accepts, failing with the correct message' do
118
+ attribute_options = { type: :integer, options: { limit: 2 } }
119
+ record = define_model(:example, attr: attribute_options).new
120
+ assertion = -> do
121
+ expect(record).
122
+ not_to disallow_value(100000).
123
+ for(:attr).
124
+ strict
125
+ end
126
+ message = <<-MESSAGE.strip_heredoc.strip
127
+ Did not expect an exception to have been raised when attr is set to 100000,
128
+ got RangeError: "100000 is out of range for ActiveRecord::Type::Integer with limit 2"
129
+ MESSAGE
130
+ expect(&assertion).to fail_with_message(message)
131
+ end
132
+
133
+ context 'qualified with a message' do
134
+ it 'ignores any specified message' do
135
+ attribute_options = { type: :integer, options: { limit: 2 } }
136
+ record = define_model(:example, attr: attribute_options).new
137
+ assertion = -> do
138
+ expect(record).
139
+ not_to disallow_value(100000).
140
+ for(:attr).
141
+ with_message('some message').
142
+ strict
143
+ end
144
+ message = <<-MESSAGE.strip_heredoc.strip
145
+ Did not expect exception to include "some message" when attr is set to 100000,
146
+ got RangeError: "100000 is out of range for ActiveRecord::Type::Integer with limit 2"
147
+ MESSAGE
148
+ expect(&assertion).to fail_with_message(message)
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
155
+
82
156
  def matcher(value)
83
157
  described_class.new(value)
84
158
  end
159
+ alias_method :disallow_value, :matcher
85
160
  end