shoulda-matchers 2.8.0 → 3.0.0.rc1

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 (171) hide show
  1. checksums.yaml +4 -4
  2. data/.hound_config/ruby.yml +7 -0
  3. data/.travis.yml +11 -54
  4. data/Appraisals +45 -100
  5. data/CONTRIBUTING.md +51 -7
  6. data/Gemfile +7 -19
  7. data/Gemfile.lock +60 -134
  8. data/Guardfile +5 -0
  9. data/NEWS.md +203 -0
  10. data/README.md +95 -50
  11. data/Rakefile +1 -0
  12. data/doc_config/yard/templates/default/layout/html/setup.rb +1 -1
  13. data/gemfiles/4.0.0.gemfile +10 -7
  14. data/gemfiles/4.0.0.gemfile.lock +103 -79
  15. data/gemfiles/4.0.1.gemfile +10 -7
  16. data/gemfiles/4.0.1.gemfile.lock +109 -83
  17. data/gemfiles/4.1.gemfile +10 -7
  18. data/gemfiles/4.1.gemfile.lock +109 -85
  19. data/gemfiles/4.2.gemfile +10 -9
  20. data/gemfiles/4.2.gemfile.lock +86 -78
  21. data/lib/shoulda/matchers.rb +13 -18
  22. data/lib/shoulda/matchers/action_controller.rb +4 -1
  23. data/lib/shoulda/matchers/action_controller/flash_store.rb +95 -0
  24. data/lib/shoulda/matchers/action_controller/{strong_parameters_matcher.rb → permit_matcher.rb} +147 -30
  25. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +1 -1
  26. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +1 -1
  27. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +1 -1
  28. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +1 -1
  29. data/lib/shoulda/matchers/action_controller/route_matcher.rb +5 -1
  30. data/lib/shoulda/matchers/action_controller/route_params.rb +15 -6
  31. data/lib/shoulda/matchers/action_controller/session_store.rb +34 -0
  32. data/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +30 -136
  33. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +28 -109
  34. data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +103 -0
  35. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +1 -12
  36. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +79 -10
  37. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +10 -0
  38. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +21 -0
  39. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +24 -0
  40. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +22 -5
  41. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +29 -10
  42. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +27 -10
  43. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +27 -12
  44. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +56 -20
  45. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +3 -11
  46. data/lib/shoulda/matchers/active_model/validation_message_finder.rb +65 -0
  47. data/lib/shoulda/matchers/active_record/association_matcher.rb +40 -6
  48. data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +21 -7
  49. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +11 -40
  50. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -1
  51. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +2 -6
  52. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +137 -22
  53. data/lib/shoulda/matchers/configuration.rb +20 -0
  54. data/lib/shoulda/matchers/doublespeak.rb +11 -1
  55. data/lib/shoulda/matchers/doublespeak/double.rb +29 -11
  56. data/lib/shoulda/matchers/doublespeak/double_collection.rb +4 -3
  57. data/lib/shoulda/matchers/doublespeak/method_call.rb +35 -0
  58. data/lib/shoulda/matchers/doublespeak/object_double.rb +7 -2
  59. data/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +4 -3
  60. data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +3 -3
  61. data/lib/shoulda/matchers/doublespeak/world.rb +21 -1
  62. data/lib/shoulda/matchers/integrations.rb +43 -0
  63. data/lib/shoulda/matchers/integrations/configuration.rb +68 -0
  64. data/lib/shoulda/matchers/integrations/configuration_error.rb +9 -0
  65. data/lib/shoulda/matchers/integrations/inclusion.rb +20 -0
  66. data/lib/shoulda/matchers/integrations/libraries.rb +15 -0
  67. data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +31 -0
  68. data/lib/shoulda/matchers/integrations/libraries/active_model.rb +26 -0
  69. data/lib/shoulda/matchers/integrations/libraries/active_record.rb +26 -0
  70. data/lib/shoulda/matchers/integrations/libraries/missing_library.rb +19 -0
  71. data/lib/shoulda/matchers/integrations/libraries/rails.rb +30 -0
  72. data/lib/shoulda/matchers/integrations/rails.rb +12 -0
  73. data/lib/shoulda/matchers/integrations/registry.rb +28 -0
  74. data/lib/shoulda/matchers/integrations/test_frameworks.rb +16 -0
  75. data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +37 -0
  76. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +36 -0
  77. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +37 -0
  78. data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +40 -0
  79. data/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +29 -0
  80. data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +36 -0
  81. data/lib/shoulda/matchers/rails_shim.rb +0 -40
  82. data/lib/shoulda/matchers/version.rb +1 -1
  83. data/script/SUPPORTED_VERSIONS +1 -1
  84. data/script/update_gems_in_all_appraisals +14 -0
  85. data/shoulda-matchers.gemspec +2 -2
  86. data/spec/acceptance/active_model_integration_spec.rb +4 -1
  87. data/spec/acceptance/independent_matchers_spec.rb +6 -6
  88. data/spec/acceptance/multiple_libraries_integration_spec.rb +52 -0
  89. data/spec/acceptance/rails_integration_spec.rb +15 -5
  90. data/spec/acceptance_spec_helper.rb +8 -0
  91. data/spec/doublespeak_spec_helper.rb +14 -0
  92. data/spec/support/acceptance/adds_shoulda_matchers_to_project.rb +110 -0
  93. data/spec/support/acceptance/helpers.rb +2 -0
  94. data/spec/support/acceptance/helpers/base_helpers.rb +6 -1
  95. data/spec/support/acceptance/helpers/command_helpers.rb +6 -2
  96. data/spec/support/acceptance/helpers/minitest_helpers.rb +0 -8
  97. data/spec/support/acceptance/helpers/n_unit_helpers.rb +25 -0
  98. data/spec/support/acceptance/helpers/rspec_helpers.rb +2 -0
  99. data/spec/support/acceptance/helpers/step_helpers.rb +13 -19
  100. data/spec/support/acceptance/matchers/have_output.rb +1 -1
  101. data/spec/support/tests/bundle.rb +1 -1
  102. data/spec/support/tests/command_runner.rb +25 -13
  103. data/spec/support/tests/current_bundle.rb +47 -0
  104. data/spec/support/tests/database.rb +28 -0
  105. data/spec/support/tests/database_adapters/postgresql.rb +25 -0
  106. data/spec/support/tests/database_adapters/sqlite3.rb +26 -0
  107. data/spec/support/tests/database_configuration.rb +33 -0
  108. data/spec/support/tests/database_configuration_registry.rb +28 -0
  109. data/spec/support/tests/filesystem.rb +25 -2
  110. data/spec/support/unit/helpers/active_record_versions.rb +12 -0
  111. data/spec/support/unit/helpers/class_builder.rb +6 -2
  112. data/spec/support/unit/helpers/column_type_helpers.rb +26 -0
  113. data/spec/support/unit/helpers/controller_builder.rb +0 -28
  114. data/spec/support/unit/helpers/database_helpers.rb +18 -0
  115. data/spec/support/unit/helpers/model_builder.rb +38 -6
  116. data/spec/support/unit/helpers/rails_versions.rb +2 -2
  117. data/spec/support/unit/matchers/fail_with_message_including_matcher.rb +9 -8
  118. data/spec/support/unit/matchers/fail_with_message_matcher.rb +1 -1
  119. data/spec/support/unit/rails_application.rb +29 -13
  120. data/spec/support/unit/record_validating_confirmation_builder.rb +1 -2
  121. data/spec/support/unit/shared_examples/set_session_or_flash.rb +355 -0
  122. data/spec/unit/shoulda/matchers/action_controller/permit_matcher_spec.rb +433 -0
  123. data/spec/unit/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +1 -5
  124. data/spec/unit/shoulda/matchers/action_controller/route_matcher_spec.rb +37 -0
  125. data/spec/unit/shoulda/matchers/action_controller/set_flash_matcher_spec.rb +23 -147
  126. data/spec/unit/shoulda/matchers/action_controller/set_session_matcher_spec.rb +8 -285
  127. data/spec/unit/shoulda/matchers/action_controller/set_session_or_flash_matcher_spec.rb +562 -0
  128. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +81 -14
  129. data/spec/unit/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +16 -8
  130. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +101 -9
  131. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +39 -1
  132. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +39 -1
  133. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +39 -0
  134. data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +0 -17
  135. data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +0 -17
  136. data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +0 -17
  137. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +838 -271
  138. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +0 -19
  139. data/spec/unit/shoulda/matchers/active_record/association_matcher_spec.rb +93 -0
  140. data/spec/unit/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +3 -3
  141. data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +25 -0
  142. data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +905 -0
  143. data/spec/unit/shoulda/matchers/doublespeak/double_collection_spec.rb +17 -11
  144. data/spec/unit/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb +1 -1
  145. data/spec/unit/shoulda/matchers/doublespeak/double_spec.rb +144 -43
  146. data/spec/unit/shoulda/matchers/doublespeak/object_double_spec.rb +1 -1
  147. data/spec/unit/shoulda/matchers/doublespeak/proxy_implementation_spec.rb +36 -11
  148. data/spec/unit/shoulda/matchers/doublespeak/stub_implementation_spec.rb +29 -16
  149. data/spec/unit/shoulda/matchers/doublespeak/world_spec.rb +8 -5
  150. data/spec/unit/shoulda/matchers/doublespeak_spec.rb +1 -1
  151. data/spec/unit_spec_helper.rb +15 -14
  152. data/spec/warnings_spy.rb +1 -1
  153. metadata +68 -29
  154. data/docs.watchr +0 -5
  155. data/gemfiles/3.0.gemfile +0 -26
  156. data/gemfiles/3.0.gemfile.lock +0 -173
  157. data/gemfiles/3.1.gemfile +0 -32
  158. data/gemfiles/3.1.gemfile.lock +0 -212
  159. data/gemfiles/3.1_1.9.2.gemfile +0 -32
  160. data/gemfiles/3.1_1.9.2.gemfile.lock +0 -212
  161. data/gemfiles/3.2.gemfile +0 -33
  162. data/gemfiles/3.2.gemfile.lock +0 -212
  163. data/gemfiles/3.2_1.9.2.gemfile +0 -31
  164. data/gemfiles/3.2_1.9.2.gemfile.lock +0 -207
  165. data/lib/shoulda/matchers/assertion_error.rb +0 -27
  166. data/lib/shoulda/matchers/doublespeak/structs.rb +0 -10
  167. data/lib/shoulda/matchers/integrations/nunit_test_case_detection.rb +0 -39
  168. data/lib/shoulda/matchers/integrations/rspec.rb +0 -19
  169. data/lib/shoulda/matchers/integrations/test_unit.rb +0 -34
  170. data/spec/unit/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb +0 -331
  171. data/spec/unit/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +0 -564
@@ -45,6 +45,44 @@ describe Shoulda::Matchers::ActiveModel::NumericalityMatchers::OddNumberMatcher
45
45
  end
46
46
  end
47
47
 
48
+ context 'asserting strict validation when validating strictly' do
49
+ it 'accepts' do
50
+ expect(validating_odd_number(strict: true)).to subject.strict
51
+ end
52
+ end
53
+
54
+ context 'asserting non-strict validation when validating strictly' do
55
+ it 'rejects' do
56
+ pending 'This needs to be fixed'
57
+ expect(validating_odd_number(strict: true)).not_to subject
58
+ end
59
+ end
60
+
61
+ context 'asserting strict validation when not validating strictly' do
62
+ it 'rejects' do
63
+ expect(validating_odd_number).not_to subject.strict
64
+ end
65
+ end
66
+
67
+ context 'qualified with on and validating with on' do
68
+ it 'accepts' do
69
+ expect(validating_odd_number(on: :customizable)).
70
+ to subject.on(:customizable)
71
+ end
72
+ end
73
+
74
+ context 'qualified with on but not validating with on' do
75
+ it 'accepts since the validation never considers a context' do
76
+ expect(validating_odd_number).to subject.on(:customizable)
77
+ end
78
+ end
79
+
80
+ context 'not qualified with on but validating with on' do
81
+ it 'rejects since the validation never runs' do
82
+ expect(validating_odd_number(on: :customizable)).
83
+ not_to subject
84
+ end
85
+ end
48
86
 
49
87
  def validating_odd_number(options = {})
50
88
  define_model :example, attr: :string do
@@ -56,4 +94,4 @@ describe Shoulda::Matchers::ActiveModel::NumericalityMatchers::OddNumberMatcher
56
94
  define_model(:example, attr: :string).new
57
95
  end
58
96
 
59
- end
97
+ end
@@ -45,6 +45,45 @@ describe Shoulda::Matchers::ActiveModel::NumericalityMatchers::OnlyIntegerMatche
45
45
  end
46
46
  end
47
47
 
48
+ context 'asserting strict validation when validating strictly' do
49
+ it 'accepts' do
50
+ expect(validating_only_integer(strict: true)).to subject.strict
51
+ end
52
+ end
53
+
54
+ context 'asserting non-strict validation when validating strictly' do
55
+ it 'rejects' do
56
+ pending 'This needs to be fixed'
57
+ expect(validating_only_integer(strict: true)).not_to subject
58
+ end
59
+ end
60
+
61
+ context 'asserting strict validation when not validating strictly' do
62
+ it 'rejects' do
63
+ expect(validating_only_integer).not_to subject.strict
64
+ end
65
+ end
66
+
67
+ context 'qualified with on and validating with on' do
68
+ it 'accepts' do
69
+ expect(validating_only_integer(on: :customizable)).
70
+ to subject.on(:customizable)
71
+ end
72
+ end
73
+
74
+ context 'qualified with on but not validating with on' do
75
+ it 'accepts since the validation never considers a context' do
76
+ expect(validating_only_integer).to subject.on(:customizable)
77
+ end
78
+ end
79
+
80
+ context 'not qualified with on but validating with on' do
81
+ it 'rejects since the validation never runs' do
82
+ expect(validating_only_integer(on: :customizable)).
83
+ not_to subject
84
+ end
85
+ end
86
+
48
87
  def validating_only_integer(options = {})
49
88
  define_model :example, attr: :string do
50
89
  validates_numericality_of :attr, { only_integer: true }.merge(options)
@@ -1,22 +1,5 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
- describe Shoulda::Matchers::ActiveModel, type: :model do
4
- describe '#ensure_exclusion_of' do
5
- it 'is aliased to #validate_exclusion_of' do
6
- allow(matchers).to receive(:validate_exclusion_of)
7
-
8
- silence_warnings do
9
- matchers.ensure_exclusion_of(:attr)
10
- expect(matchers).to have_received(:validate_exclusion_of).with(:attr)
11
- end
12
- end
13
- end
14
-
15
- def matchers
16
- @_matchers ||= Object.new.extend(described_class)
17
- end
18
- end
19
-
20
3
  describe Shoulda::Matchers::ActiveModel::ValidateExclusionOfMatcher, type: :model do
21
4
  context 'an attribute which must be excluded from a range' do
22
5
  it 'accepts ensuring the correct range' do
@@ -1,22 +1,5 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
- describe Shoulda::Matchers::ActiveModel, type: :model do
4
- describe '#ensure_inclusion_of' do
5
- it 'is aliased to #validate_inclusion_of' do
6
- allow(matchers).to receive(:validate_inclusion_of)
7
-
8
- silence_warnings do
9
- matchers.ensure_inclusion_of(:attr)
10
- expect(matchers).to have_received(:validate_inclusion_of)
11
- end
12
- end
13
- end
14
-
15
- def matchers
16
- @_matchers ||= Object.new.extend(described_class)
17
- end
18
- end
19
-
20
3
  describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :model do
21
4
  shared_context 'for a generic attribute' do
22
5
  def self.testing_values_of_option(option_name, &block)
@@ -1,22 +1,5 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
- describe Shoulda::Matchers::ActiveModel, type: :model do
4
- describe '#ensure_length_of' do
5
- it 'is aliased to #validate_length_of' do
6
- allow(matchers).to receive(:validate_length_of)
7
-
8
- silence_warnings do
9
- matchers.ensure_length_of(:attr)
10
- expect(matchers).to have_received(:validate_length_of).with(:attr)
11
- end
12
- end
13
- end
14
-
15
- def matchers
16
- @_matchers ||= Object.new.extend(described_class)
17
- end
18
- end
19
-
20
3
  describe Shoulda::Matchers::ActiveModel::ValidateLengthOfMatcher, type: :model do
21
4
  context 'an attribute with a non-zero minimum length validation' do
22
5
  it 'accepts ensuring the correct minimum length' do
@@ -1,425 +1,992 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
3
  describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :model do
4
- context 'with a model with a numericality validation' do
5
- it 'accepts' do
6
- expect(validating_numericality).to matcher
4
+ class << self
5
+ def all_qualifiers
6
+ [
7
+ {
8
+ category: :comparison,
9
+ name: :is_greater_than,
10
+ argument: 1,
11
+ validation_name: :greater_than,
12
+ validation_value: 1,
13
+ },
14
+ {
15
+ category: :comparison,
16
+ name: :is_greater_than_or_equal_to,
17
+ argument: 1,
18
+ validation_name: :greater_than_or_equal_to,
19
+ validation_value: 1,
20
+ },
21
+ {
22
+ category: :comparison,
23
+ name: :is_less_than,
24
+ argument: 1,
25
+ validation_name: :less_than,
26
+ validation_value: 1,
27
+ },
28
+ {
29
+ category: :comparison,
30
+ name: :is_less_than_or_equal_to,
31
+ argument: 1,
32
+ validation_name: :less_than_or_equal_to,
33
+ validation_value: 1,
34
+ },
35
+ {
36
+ category: :comparison,
37
+ name: :is_equal_to,
38
+ argument: 1,
39
+ validation_name: :equal_to,
40
+ validation_value: 1,
41
+ },
42
+ {
43
+ category: :cardinality,
44
+ name: :odd,
45
+ validation_name: :odd,
46
+ validation_value: true,
47
+ },
48
+ {
49
+ category: :cardinality,
50
+ name: :even,
51
+ validation_name: :even,
52
+ validation_value: true,
53
+ },
54
+ {
55
+ name: :only_integer,
56
+ validation_name: :only_integer,
57
+ validation_value: true,
58
+ },
59
+ {
60
+ name: :on,
61
+ argument: :customizable,
62
+ validation_name: :on,
63
+ validation_value: :customizable
64
+ }
65
+ ]
66
+ end
67
+
68
+ def qualifiers_under(category)
69
+ all_qualifiers.select do |qualifier|
70
+ qualifier[:category] == category
71
+ end
7
72
  end
8
73
 
9
- it 'does not override the default message with a blank' do
10
- expect(validating_numericality).to matcher.with_message(nil)
74
+ def mutually_exclusive_qualifiers
75
+ qualifiers_under(:cardinality) + qualifiers_under(:comparison)
11
76
  end
12
- end
13
77
 
14
- context 'with a model without a numericality validation' do
15
- it 'rejects' do
16
- expect(not_validating_numericality).not_to matcher
78
+ def non_mutually_exclusive_qualifiers
79
+ all_qualifiers - mutually_exclusive_qualifiers
17
80
  end
18
81
 
19
- it 'rejects with the ActiveRecord :not_a_number message' do
20
- the_matcher = matcher
21
- expect do
22
- expect(not_validating_numericality).to the_matcher
23
- end.to fail_with_message_including(
24
- 'Expected errors to include "is not a number"'
25
- )
82
+ def validations_by_qualifier
83
+ all_qualifiers.each_with_object({}) do |qualifier, hash|
84
+ hash[qualifier[:name]] = qualifier[:validation_name]
85
+ end
26
86
  end
27
87
 
28
- it 'rejects with the ActiveRecord :not_an_integer message' do
29
- the_matcher = matcher.only_integer
30
- expect do
31
- expect(not_validating_numericality).to the_matcher
32
- end.to fail_with_message_including(
33
- 'Expected errors to include "must be an integer"'
34
- )
88
+ def all_qualifier_combinations
89
+ combinations = []
90
+
91
+ ([nil] + mutually_exclusive_qualifiers).each do |mutually_exclusive_qualifier|
92
+ (0..non_mutually_exclusive_qualifiers.length).each do |n|
93
+ non_mutually_exclusive_qualifiers.combination(n) do |combination|
94
+ super_combination = (
95
+ [mutually_exclusive_qualifier] +
96
+ combination
97
+ )
98
+ super_combination.select!(&:present?)
99
+
100
+ if super_combination.any?
101
+ combinations << super_combination
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ combinations
35
108
  end
36
109
 
37
- it 'rejects with the ActiveRecord :odd message' do
38
- the_matcher = matcher.odd
39
- expect do
40
- expect(not_validating_numericality).to the_matcher
41
- end.to fail_with_message_including(
42
- 'Expected errors to include "must be odd"'
43
- )
110
+ def default_qualifier_arguments
111
+ all_qualifiers.each_with_object({}) do |qualifier, hash|
112
+ hash[qualifier[:name]] = qualifier[:argument]
113
+ end
44
114
  end
45
115
 
46
- it 'rejects with the ActiveRecord :even message' do
47
- the_matcher = matcher.even
48
- expect do
49
- expect(not_validating_numericality).to the_matcher
50
- end.to fail_with_message_including(
51
- 'Expected errors to include "must be even"'
52
- )
116
+ def default_validation_values
117
+ all_qualifiers.each_with_object({}) do |qualifier, hash|
118
+ hash[qualifier[:validation_name]] = qualifier[:validation_value]
119
+ end
53
120
  end
54
121
  end
55
122
 
56
- context 'with the allow_nil option' do
57
- it 'allows nil values for that attribute' do
58
- expect(validating_numericality(allow_nil: true)).to matcher.allow_nil
123
+ context 'qualified with nothing' do
124
+ context 'and validating numericality' do
125
+ it 'accepts' do
126
+ record = build_record_validating_numericality
127
+ expect(record).to validate_numericality
128
+ end
59
129
  end
60
130
 
61
- it 'rejects when the model does not allow nil' do
62
- the_matcher = matcher.allow_nil
63
- expect {
64
- expect(validating_numericality).to the_matcher
65
- }.to fail_with_message_including('Did not expect errors to include "is not a number"')
131
+ context 'and not validating anything' do
132
+ it 'rejects since it does not disallow non-numbers' do
133
+ record = build_record_validating_nothing
134
+ assertion = -> { expect(record).to validate_numericality }
135
+ expect(&assertion).to fail_with_message_including(
136
+ 'Expected errors to include "is not a number"'
137
+ )
138
+ end
66
139
  end
67
140
  end
68
141
 
69
- context 'with the only_integer option' do
70
- it 'allows integer values for that attribute' do
71
- expect(validating_numericality(only_integer: true)).to matcher.only_integer
142
+ context 'qualified with allow_nil' do
143
+ context 'and validating with allow_nil' do
144
+ it 'accepts' do
145
+ record = build_record_validating_numericality(allow_nil: true)
146
+ expect(record).to validate_numericality.allow_nil
147
+ end
72
148
  end
73
149
 
74
- it 'rejects when the model does not enforce integer values' do
75
- expect(validating_numericality).not_to matcher.only_integer
150
+ context 'and not validating with allow_nil' do
151
+ it 'rejects since it tries to treat nil as a number' do
152
+ record = build_record_validating_numericality
153
+ assertion = lambda do
154
+ expect(record).to validate_numericality.allow_nil
155
+ end
156
+ expect(&assertion).to fail_with_message_including(
157
+ %[Did not expect errors to include "is not a number" when #{attribute_name} is set to nil]
158
+ )
159
+ end
76
160
  end
161
+ end
77
162
 
78
- it 'rejects with the ActiveRecord :not_an_integer message' do
79
- the_matcher = matcher.only_integer
80
- expect do
81
- expect(validating_numericality).to the_matcher
82
- end.to fail_with_message_including(
83
- 'Expected errors to include "must be an integer"'
84
- )
163
+ context 'qualified with only_integer' do
164
+ context 'and validating with only_integer' do
165
+ it 'accepts' do
166
+ record = build_record_validating_numericality(only_integer: true)
167
+ expect(record).to validate_numericality.only_integer
168
+ end
85
169
  end
86
- end
87
170
 
88
- context 'with the odd option' do
89
- it 'allows odd number values for that attribute' do
90
- expect(validating_numericality(odd: true)).to matcher.odd
171
+ context 'and not validating with only_integer' do
172
+ it 'rejects since it does not disallow non-integers' do
173
+ record = build_record_validating_numericality
174
+ assertion = lambda do
175
+ expect(record).to validate_numericality.only_integer
176
+ end
177
+ expect(&assertion).to fail_with_message_including(
178
+ 'Expected errors to include "must be an integer"'
179
+ )
180
+ end
91
181
  end
182
+ end
92
183
 
93
- it 'rejects when the model does not enforce odd number values' do
94
- expect(validating_numericality).not_to matcher.odd
184
+ context 'qualified with odd' do
185
+ context 'and validating with odd' do
186
+ it 'accepts' do
187
+ record = build_record_validating_numericality(odd: true)
188
+ expect(record).to validate_numericality.odd
189
+ end
95
190
  end
96
191
 
97
- it 'rejects with the ActiveRecord :odd message' do
98
- the_matcher = matcher.odd
99
- expect do
100
- expect(validating_numericality).to the_matcher
101
- end.to fail_with_message_including(
102
- 'Expected errors to include "must be odd"'
103
- )
192
+ context 'and not validating with odd' do
193
+ it 'rejects since it does not disallow even numbers' do
194
+ record = build_record_validating_numericality
195
+ assertion = lambda do
196
+ expect(record).to validate_numericality.odd
197
+ end
198
+ expect(&assertion).to fail_with_message_including(
199
+ 'Expected errors to include "must be odd"'
200
+ )
201
+ end
104
202
  end
105
203
  end
106
204
 
107
- context 'with the even option' do
108
- it 'allows even number values for that attribute' do
109
- expect(validating_numericality(even: true)).to matcher.even
205
+ context 'qualified with even' do
206
+ context 'and validating with even' do
207
+ it 'allows even number values for that attribute' do
208
+ record = build_record_validating_numericality(even: true)
209
+ expect(record).to validate_numericality.even
210
+ end
110
211
  end
111
212
 
112
- it 'rejects when the model does not enforce even number values' do
113
- expect(validating_numericality).not_to matcher.even
213
+ context 'and not validating with even' do
214
+ it 'rejects since it does not disallow odd numbers' do
215
+ record = build_record_validating_numericality
216
+ assertion = -> { expect(record).to validate_numericality.even }
217
+ expect(&assertion).to fail_with_message_including(
218
+ 'Expected errors to include "must be even"'
219
+ )
220
+ end
114
221
  end
222
+ end
115
223
 
116
- it 'rejects with the ActiveRecord :even message' do
117
- the_matcher = matcher.even
118
- expect do
119
- expect(validating_numericality).to the_matcher
120
- end.to fail_with_message_including(
121
- 'Expected errors to include "must be even"'
122
- )
224
+ context 'qualified with is_less_than_or_equal_to' do
225
+ context 'and validating with less_than_or_equal_to' do
226
+ it 'accepts' do
227
+ record = build_record_validating_numericality(
228
+ less_than_or_equal_to: 18
229
+ )
230
+ expect(record).to validate_numericality.is_less_than_or_equal_to(18)
231
+ end
232
+
233
+ context 'if the given value is right at the allowed max value for the column' do
234
+ it 'does not raise an error' do
235
+ record = build_record_with_integer_column_of_limit(2,
236
+ less_than_or_equal_to: 32767
237
+ )
238
+ assertion = lambda do
239
+ expect(record).
240
+ to validate_numericality.
241
+ is_less_than_or_equal_to(32767)
242
+ end
243
+ expect(&assertion).not_to raise_error
244
+ end
245
+ end
123
246
  end
124
- end
125
247
 
126
- context 'qualified with less_than_or_equal_to' do
127
- it 'does not raise an error if the given value is right at the allowed max value for the column' do
128
- record = record_with_integer_column_of_limit(:attr, 2, less_than_or_equal_to: 32767)
129
- assertion = -> {
130
- expect(record).to validate_numericality_of(:attr).is_less_than_or_equal_to(32767)
131
- }
132
- expect(&assertion).not_to raise_error
248
+ context 'and not validating with less_than_or_equal_to' do
249
+ it 'rejects since it does not disallow numbers greater than the value' do
250
+ record = build_record_validating_numericality
251
+ assertion = lambda do
252
+ expect(record).
253
+ to validate_numericality.
254
+ is_less_than_or_equal_to(18)
255
+ end
256
+ expect(&assertion).to fail_with_message_including(
257
+ 'Expected errors to include "must be less than or equal to 18"'
258
+ )
259
+ end
133
260
  end
134
261
  end
135
262
 
136
- context 'qualified with less_than' do
137
- it 'does not raise an error if the given value is right at the allowed max value for the column' do
138
- record = record_with_integer_column_of_limit(:attr, 2, less_than: 32767)
139
- assertion = -> {
140
- expect(record).to validate_numericality_of(:attr).is_less_than(32767)
141
- }
142
- expect(&assertion).not_to raise_error
263
+ context 'qualified with is_less_than' do
264
+ context 'and validating with less_than' do
265
+ it 'accepts' do
266
+ record = build_record_validating_numericality(less_than: 18)
267
+ expect(record).
268
+ to validate_numericality.
269
+ is_less_than(18)
270
+ end
271
+
272
+ context 'if the given value is right at the allowed max value for the column' do
273
+ it 'does not raise an error' do
274
+ record = build_record_with_integer_column_of_limit(2, less_than: 32767)
275
+ assertion = lambda do
276
+ expect(record).to validate_numericality.is_less_than(32767)
277
+ end
278
+ expect(&assertion).not_to raise_error
279
+ end
280
+ end
281
+ end
282
+
283
+ context 'and not validating with less_than' do
284
+ it 'rejects since it does not disallow numbers greater than or equal to the value' do
285
+ record = build_record_validating_numericality
286
+ assertion = lambda do
287
+ expect(record).
288
+ to validate_numericality.
289
+ is_less_than(18)
290
+ end
291
+ expect(&assertion).to fail_with_message_including(
292
+ 'Expected errors to include "must be less than 18"'
293
+ )
294
+ end
143
295
  end
144
296
  end
145
297
 
146
- context 'qualified with equal_to' do
147
- it 'does not raise an error if the given value is right at the allowed min value for the column' do
148
- record = record_with_integer_column_of_limit(:attr, 2, equal_to: -32768)
149
- assertion = -> {
150
- expect(record).to validate_numericality_of(:attr).is_equal_to(-32768)
151
- }
152
- expect(&assertion).not_to raise_error
298
+ context 'qualified with is_equal_to' do
299
+ context 'and validating with equal_to' do
300
+ it 'accepts' do
301
+ record = build_record_validating_numericality(equal_to: 18)
302
+ expect(record).to validate_numericality.is_equal_to(18)
303
+ end
304
+
305
+ context 'if the given value is right at the allowed min value for the column' do
306
+ it 'does not raise an error' do
307
+ record = build_record_with_integer_column_of_limit(2, equal_to: -32768)
308
+ assertion = lambda do
309
+ expect(record).to validate_numericality.is_equal_to(-32768)
310
+ end
311
+ expect(&assertion).not_to raise_error
312
+ end
313
+ end
314
+
315
+ context 'if the given value is right at the allowed max value for the column' do
316
+ it 'does not raise an error' do
317
+ record = build_record_with_integer_column_of_limit(2, equal_to: 32767)
318
+ assertion = lambda do
319
+ expect(record).to validate_numericality.is_equal_to(32767)
320
+ end
321
+ expect(&assertion).not_to raise_error
322
+ end
323
+ end
153
324
  end
154
325
 
155
- it 'does not raise an error if the given value is right at the allowed max value for the column' do
156
- record = record_with_integer_column_of_limit(:attr, 2, equal_to: 32767)
157
- assertion = -> {
158
- expect(record).to validate_numericality_of(:attr).is_equal_to(32767)
159
- }
160
- expect(&assertion).not_to raise_error
326
+ context 'and not validating with equal_to' do
327
+ it 'rejects since it does not disallow numbers that are not the value' do
328
+ record = build_record_validating_numericality
329
+ assertion = lambda do
330
+ expect(record).to validate_numericality.is_equal_to(18)
331
+ end
332
+ expect(&assertion).to fail_with_message_including(
333
+ 'Expected errors to include "must be equal to 18"'
334
+ )
335
+ end
161
336
  end
162
337
  end
163
338
 
164
- context 'qualified with greater_than_or_equal to' do
165
- it 'does not raise an error if the given value is right at the allowed min value for the column' do
166
- record = record_with_integer_column_of_limit(:attr, 2,
167
- greater_than_or_equal_to: -32768
168
- )
169
- assertion = -> {
339
+ context 'qualified with is_greater_than_or_equal to' do
340
+ context 'validating with greater_than_or_equal_to' do
341
+ it 'accepts' do
342
+ record = build_record_validating_numericality(
343
+ greater_than_or_equal_to: 18
344
+ )
170
345
  expect(record).
171
- to validate_numericality_of(:attr).
172
- is_greater_than_or_equal_to(-32768)
173
- }
174
- expect(&assertion).not_to raise_error
346
+ to validate_numericality.
347
+ is_greater_than_or_equal_to(18)
348
+ end
349
+
350
+ context 'if the given value is right at the allowed min value for the column' do
351
+ it 'does not raise an error' do
352
+ record = build_record_with_integer_column_of_limit(2,
353
+ greater_than_or_equal_to: -32768
354
+ )
355
+ assertion = lambda do
356
+ expect(record).
357
+ to validate_numericality_of(:attr).
358
+ is_greater_than_or_equal_to(-32768)
359
+ end
360
+ expect(&assertion).not_to raise_error
361
+ end
362
+ end
363
+ end
364
+
365
+ context 'not validating with greater_than_or_equal_to' do
366
+ it 'rejects since it does not disallow numbers that are less than the value' do
367
+ record = build_record_validating_numericality
368
+ assertion = lambda do
369
+ expect(record).
370
+ to validate_numericality.
371
+ is_greater_than_or_equal_to(18)
372
+ end
373
+ expect(&assertion).to fail_with_message_including(
374
+ 'Expected errors to include "must be greater than or equal to 18"'
375
+ )
376
+ end
175
377
  end
176
378
  end
177
379
 
178
- context 'qualified with greater_than' do
179
- it 'does not raise an error if the given value is right at the allowed min value for the column' do
180
- record = record_with_integer_column_of_limit(:attr, 2,
181
- greater_than: -32768
182
- )
183
- assertion = -> {
380
+ context 'qualified with is_greater_than' do
381
+ context 'and validating with greater_than' do
382
+ it 'accepts' do
383
+ record = build_record_validating_numericality(greater_than: 18)
184
384
  expect(record).
185
- to validate_numericality_of(:attr).
186
- is_greater_than(-32768)
187
- }
188
- expect(&assertion).not_to raise_error
385
+ to validate_numericality.
386
+ is_greater_than(18)
387
+ end
388
+
389
+ context 'if the given value is right at the allowed min value for the column' do
390
+ it 'does not raise an error' do
391
+ record = build_record_with_integer_column_of_limit(2,
392
+ greater_than: -32768
393
+ )
394
+ assertion = lambda do
395
+ expect(record).
396
+ to validate_numericality_of(:attr).
397
+ is_greater_than(-32768)
398
+ end
399
+ expect(&assertion).not_to raise_error
400
+ end
401
+ end
402
+ end
403
+
404
+ context 'and not validating with greater_than' do
405
+ it 'rejects since it does not disallow numbers that are less than or equal to the value' do
406
+ record = build_record_validating_numericality
407
+ assertion = lambda do
408
+ expect(record).
409
+ to validate_numericality.
410
+ is_greater_than(18)
411
+ end
412
+ expect(&assertion).to fail_with_message_including(
413
+ 'Expected errors to include "must be greater than 18"'
414
+ )
415
+ end
416
+ end
417
+ end
418
+
419
+ context 'qualified with with_message' do
420
+ context 'and validating with the same message' do
421
+ it 'accepts' do
422
+ record = build_record_validating_numericality(message: 'custom')
423
+ expect(record).to validate_numericality.with_message(/custom/)
424
+ end
425
+ end
426
+
427
+ context 'and validating with a different message' do
428
+ it 'rejects' do
429
+ record = build_record_validating_numericality(message: 'custom')
430
+ expect(record).not_to validate_numericality.with_message(/wrong/)
431
+ end
432
+ end
433
+
434
+ context 'and no message is provided' do
435
+ it 'ignores the qualifier' do
436
+ record = build_record_validating_numericality
437
+ expect(record).to validate_numericality.with_message(nil)
438
+ end
189
439
  end
190
440
  end
191
441
 
192
- context 'with multiple options together' do
193
- context 'the success cases' do
194
- it do
195
- expect(validating_numericality(only_integer: true, greater_than: 18))
196
- .to matcher.only_integer.is_greater_than(18)
442
+ context 'qualified with strict' do
443
+ context 'and validating strictly' do
444
+ it 'accepts' do
445
+ record = build_record_validating_numericality(strict: true)
446
+ expect(record).to validate_numericality.strict
197
447
  end
448
+ end
198
449
 
199
- it do
200
- expect(validating_numericality(even: true, greater_than: 18))
201
- .to matcher.even.is_greater_than(18)
450
+ context 'and not validating strictly' do
451
+ it 'rejects since ActiveModel::StrictValidationFailed is never raised' do
452
+ record = build_record_validating_numericality(attribute_name: :attr)
453
+ assertion = lambda do
454
+ expect(record).to validate_numericality_of(:attr).strict
455
+ end
456
+ expect(&assertion).to fail_with_message_including(
457
+ 'Expected exception to include "Attr is not a number"'
458
+ )
202
459
  end
203
- it do
204
- expect(validating_numericality(odd: true, less_than_or_equal_to: 99))
205
- .to matcher.odd.is_less_than_or_equal_to(99)
460
+ end
461
+ end
462
+
463
+ context 'qualified with on and validating with on' do
464
+ it 'accepts' do
465
+ record = build_record_validating_numericality(on: :customizable)
466
+ expect(record).to validate_numericality.on(:customizable)
467
+ end
468
+ end
469
+
470
+ context 'qualified with on but not validating with on' do
471
+ it 'accepts since the validation never considers a context' do
472
+ record = build_record_validating_numericality
473
+ expect(record).to validate_numericality.on(:customizable)
474
+ end
475
+ end
476
+
477
+ context 'not qualified with on but validating with on' do
478
+ it 'rejects since the validation never runs' do
479
+ record = build_record_validating_numericality(on: :customizable)
480
+ assertion = lambda do
481
+ expect(record).to validate_numericality
206
482
  end
483
+ expect(&assertion).to fail_with_message_including(
484
+ 'Expected errors to include "is not a number"'
485
+ )
486
+ end
487
+ end
207
488
 
208
- it do
209
- expect(validating_numericality(
210
- only_integer: true,
211
- greater_than: 18,
212
- less_than: 99)
213
- ).to matcher.only_integer.is_greater_than(18).is_less_than(99)
489
+ context 'with combinations of qualifiers together' do
490
+ all_qualifier_combinations.each do |combination|
491
+ if combination.size > 1
492
+ it do
493
+ validation_options = build_validation_options(for: combination)
494
+ record = build_record_validating_numericality(validation_options)
495
+ validate_numericality = self.validate_numericality
496
+ apply_qualifiers!(for: combination, to: validate_numericality)
497
+ expect(record).to validate_numericality
498
+ end
214
499
  end
215
500
  end
216
501
 
217
- context 'the failure cases with different validators' do
218
- it do
219
- expect(validating_numericality(even: true, greater_than: 18))
220
- .not_to matcher.only_integer.is_greater_than(18)
502
+ context 'when the qualifiers do not match the validation options' do
503
+ specify 'such as validating even but testing that only_integer is validated' do
504
+ record = build_record_validating_numericality(
505
+ even: true,
506
+ greater_than: 18
507
+ )
508
+ assertion = lambda do
509
+ expect(record).
510
+ to validate_numericality.
511
+ only_integer.
512
+ is_greater_than(18)
513
+ end
514
+ message = format_message_according_to_rails_version(
515
+ <<-MESSAGE.strip_heredoc
516
+ Expected errors to include "must be an integer" when attr is set to 0.1,
517
+ got errors:
518
+ * "must be greater than 18" (attribute: attr, value: "0.1")
519
+ MESSAGE
520
+ )
521
+ expect(&assertion).to fail_with_message(message)
221
522
  end
222
523
 
223
- it do
224
- expect(validating_numericality(greater_than: 18))
225
- .not_to matcher.only_integer.is_greater_than(18)
524
+ specify 'such as not validating only_integer but testing that only_integer is validated' do
525
+ record = build_record_validating_numericality(greater_than: 18)
526
+ assertion = lambda do
527
+ expect(record).
528
+ to validate_numericality.
529
+ only_integer.
530
+ is_greater_than(18)
531
+ end
532
+ message = format_message_according_to_rails_version(
533
+ <<-MESSAGE.strip_heredoc
534
+ Expected errors to include "must be an integer" when attr is set to 0.1,
535
+ got errors:
536
+ * "must be greater than 18" (attribute: attr, value: "0.1")
537
+ MESSAGE
538
+ )
539
+ expect(&assertion).to fail_with_message(message)
226
540
  end
227
541
 
228
- it do
229
- expect(
230
- validating_numericality(even: true, greater_than_or_equal_to: 18)
231
- ).not_to matcher.even.is_greater_than(18)
542
+ specify 'such as validating greater_than_or_equal_to (+ even) but testing that greater_than is validated' do
543
+ record = build_record_validating_numericality(
544
+ even: true,
545
+ greater_than_or_equal_to: 18
546
+ )
547
+ assertion = lambda do
548
+ expect(record).
549
+ to validate_numericality.
550
+ even.
551
+ is_greater_than(18)
552
+ end
553
+ message = format_message_according_to_rails_version(
554
+ <<-MESSAGE.strip_heredoc
555
+ Expected errors to include "must be greater than 18" when attr is set to 18,
556
+ got no errors
557
+ MESSAGE
558
+ )
559
+ expect(&assertion).to fail_with_message(message)
232
560
  end
233
561
 
234
- it do
235
- expect(validating_numericality(odd: true, greater_than: 18))
236
- .not_to matcher.even.is_greater_than(18)
562
+ specify 'such as validating odd (+ greater_than) but testing that even is validated' do
563
+ record = build_record_validating_numericality(
564
+ odd: true,
565
+ greater_than: 18
566
+ )
567
+ assertion = lambda do
568
+ expect(record).
569
+ to validate_numericality.
570
+ even.
571
+ is_greater_than(18)
572
+ end
573
+ message = format_message_according_to_rails_version(
574
+ <<-MESSAGE.strip_heredoc
575
+ Expected errors to include "must be even" when attr is set to 1,
576
+ got errors:
577
+ * "must be greater than 18" (attribute: attr, value: "1")
578
+ MESSAGE
579
+ )
580
+ expect(&assertion).to fail_with_message(message)
237
581
  end
238
582
 
239
- it do
240
- expect(validating_numericality(
241
- odd: true,
242
- greater_than_or_equal_to: 99
243
- )
244
- ).not_to matcher.odd.is_less_than_or_equal_to(99)
583
+ specify 'such as validating greater_than_or_equal_to (+ odd) but testing that is_less_than_or_equal_to is validated' do
584
+ record = build_record_validating_numericality(
585
+ odd: true,
586
+ greater_than_or_equal_to: 99
587
+ )
588
+ assertion = lambda do
589
+ expect(record).
590
+ to validate_numericality.
591
+ odd.
592
+ is_less_than_or_equal_to(99)
593
+ end
594
+ message = format_message_according_to_rails_version(
595
+ <<-MESSAGE.strip_heredoc
596
+ Expected errors to include "must be less than or equal to 99" when attr is set to 101,
597
+ got no errors
598
+ MESSAGE
599
+ )
600
+ expect(&assertion).to fail_with_message(message)
245
601
  end
246
602
 
247
- it do
248
- expect(validating_numericality(
249
- only_integer: true,
250
- greater_than_or_equal_to: 18,
251
- less_than: 99
252
- )
253
- ).not_to matcher.only_integer.is_greater_than(18).is_less_than(99)
603
+ specify 'such as validating greater_than_or_equal_to (+ only_integer + less_than) but testing that greater_than is validated' do
604
+ record = build_record_validating_numericality(
605
+ only_integer: true,
606
+ greater_than_or_equal_to: 18,
607
+ less_than: 99
608
+ )
609
+ assertion = lambda do
610
+ expect(record).
611
+ to validate_numericality.
612
+ only_integer.
613
+ is_greater_than(18).
614
+ is_less_than(99)
615
+ end
616
+ message = format_message_according_to_rails_version(
617
+ <<-MESSAGE.strip_heredoc
618
+ Expected errors to include "must be greater than 18" when attr is set to 18,
619
+ got no errors
620
+ MESSAGE
621
+ )
622
+ expect(&assertion).to fail_with_message(message)
254
623
  end
255
624
  end
256
625
 
257
- context 'the failure cases with wrong values' do
258
- it do
259
- expect(validating_numericality(only_integer: true, greater_than: 19))
260
- .not_to matcher.only_integer.is_greater_than(18)
626
+ context 'when qualifiers match the validation options but the values are different' do
627
+ specify 'such as testing greater_than (+ only_integer) with lower value' do
628
+ record = build_record_validating_numericality(
629
+ only_integer: true,
630
+ greater_than: 19
631
+ )
632
+ assertion = lambda do
633
+ expect(record).
634
+ to validate_numericality.
635
+ only_integer.
636
+ is_greater_than(18)
637
+ end
638
+ message = format_message_according_to_rails_version(
639
+ <<-MESSAGE.strip_heredoc
640
+ Expected errors to include "must be greater than 18" when attr is set to 18,
641
+ got errors:
642
+ * "must be greater than 19" (attribute: attr, value: "19")
643
+ MESSAGE
644
+ )
645
+ expect(&assertion).to fail_with_message(message)
261
646
  end
262
647
 
263
- it do
264
- expect(validating_numericality(only_integer: true, greater_than: 17))
265
- .not_to matcher.only_integer.is_greater_than(18)
648
+ specify 'such as testing greater_than (+ only_integer) with higher value' do
649
+ record = build_record_validating_numericality(
650
+ only_integer: true,
651
+ greater_than: 17
652
+ )
653
+ assertion = lambda do
654
+ expect(record).
655
+ to validate_numericality.
656
+ only_integer.
657
+ is_greater_than(18)
658
+ end
659
+ message = format_message_according_to_rails_version(
660
+ <<-MESSAGE.strip_heredoc
661
+ Expected errors to include "must be greater than 18" when attr is set to 18,
662
+ got no errors
663
+ MESSAGE
664
+ )
665
+ expect(&assertion).to fail_with_message(message)
266
666
  end
267
667
 
268
- it do
269
- expect(validating_numericality(even: true, greater_than: 20))
270
- .not_to matcher.even.is_greater_than(18)
668
+ specify 'such as testing greater_than (+ even) with lower value' do
669
+ record = build_record_validating_numericality(
670
+ even: true,
671
+ greater_than: 20
672
+ )
673
+ assertion = lambda do
674
+ expect(record).
675
+ to validate_numericality.
676
+ even.
677
+ is_greater_than(18)
678
+ end
679
+ message = format_message_according_to_rails_version(
680
+ <<-MESSAGE.strip_heredoc
681
+ Expected errors to include "must be greater than 18" when attr is set to 18,
682
+ got errors:
683
+ * "must be greater than 20" (attribute: attr, value: "20")
684
+ MESSAGE
685
+ )
686
+ expect(&assertion).to fail_with_message(message)
271
687
  end
272
688
 
273
- it do
274
- expect(validating_numericality(even: true, greater_than: 16))
275
- .not_to matcher.even.is_greater_than(18)
689
+ specify 'such as testing greater than (+ even) with higher value' do
690
+ record = build_record_validating_numericality(
691
+ even: true,
692
+ greater_than: 16
693
+ )
694
+ assertion = lambda do
695
+ expect(record).
696
+ to validate_numericality.
697
+ even.
698
+ is_greater_than(18)
699
+ end
700
+ message = format_message_according_to_rails_version(
701
+ <<-MESSAGE.strip_heredoc
702
+ Expected errors to include "must be greater than 18" when attr is set to 18,
703
+ got no errors
704
+ MESSAGE
705
+ )
706
+ expect(&assertion).to fail_with_message(message)
276
707
  end
277
708
 
278
- it do
279
- expect(validating_numericality(odd: true, less_than_or_equal_to: 101))
280
- .not_to matcher.odd.is_less_than_or_equal_to(99)
709
+ specify 'such as testing less_than_or_equal_to (+ odd) with lower value' do
710
+ record = build_record_validating_numericality(
711
+ odd: true,
712
+ less_than_or_equal_to: 101
713
+ )
714
+ assertion = lambda do
715
+ expect(record).
716
+ to validate_numericality.
717
+ odd.
718
+ is_less_than_or_equal_to(99)
719
+ end
720
+ message = format_message_according_to_rails_version(
721
+ <<-MESSAGE.strip_heredoc
722
+ Expected errors to include "must be less than or equal to 99" when attr is set to 101,
723
+ got no errors
724
+ MESSAGE
725
+ )
726
+ expect(&assertion).to fail_with_message(message)
281
727
  end
282
728
 
283
- it do
284
- expect(validating_numericality(odd: true, less_than_or_equal_to: 97))
285
- .not_to matcher.odd.is_less_than_or_equal_to(99)
729
+ specify 'such as testing less_than_or_equal_to (+ odd) with higher value' do
730
+ record = build_record_validating_numericality(
731
+ odd: true,
732
+ less_than_or_equal_to: 97
733
+ )
734
+ assertion = lambda do
735
+ expect(record).
736
+ to validate_numericality.
737
+ odd.
738
+ is_less_than_or_equal_to(99)
739
+ end
740
+ message = format_message_according_to_rails_version(
741
+ <<-MESSAGE.strip_heredoc
742
+ Expected errors to include "must be less than or equal to 99" when attr is set to 101,
743
+ got errors:
744
+ * "must be less than or equal to 97" (attribute: attr, value: "101")
745
+ MESSAGE
746
+ )
747
+ expect(&assertion).to fail_with_message(message)
286
748
  end
287
749
 
288
- it do
289
- expect(validating_numericality(only_integer: true,
290
- greater_than: 19,
291
- less_than: 99))
292
- .not_to matcher.only_integer.is_greater_than(18).is_less_than(99)
750
+ specify 'such as testing greater_than (+ only_integer + less_than) with lower value' do
751
+ record = build_record_validating_numericality(
752
+ only_integer: true,
753
+ greater_than: 19,
754
+ less_than: 99
755
+ )
756
+ assertion = lambda do
757
+ expect(record).
758
+ to validate_numericality.
759
+ only_integer.
760
+ is_greater_than(18).
761
+ is_less_than(99)
762
+ end
763
+ message = format_message_according_to_rails_version(
764
+ <<-MESSAGE.strip_heredoc
765
+ Expected errors to include "must be greater than 18" when attr is set to 18,
766
+ got errors:
767
+ * "must be greater than 19" (attribute: attr, value: "19")
768
+ MESSAGE
769
+ )
770
+ expect(&assertion).to fail_with_message(message)
293
771
  end
294
772
 
295
- it do
296
- expect(validating_numericality(only_integer: true,
297
- greater_than: 18,
298
- less_than: 100))
299
- .not_to matcher.only_integer.is_greater_than(18).is_less_than(99)
773
+ specify 'such as testing less_than (+ only_integer + greater_than) with higher value' do
774
+ record = build_record_validating_numericality(
775
+ only_integer: true,
776
+ greater_than: 18,
777
+ less_than: 100
778
+ )
779
+ assertion = lambda do
780
+ expect(record).
781
+ to validate_numericality.
782
+ only_integer.
783
+ is_greater_than(18).
784
+ is_less_than(99)
785
+ end
786
+ message = format_message_according_to_rails_version(
787
+ <<-MESSAGE.strip_heredoc
788
+ Expected errors to include "must be less than 99" when attr is set to 100,
789
+ got errors:
790
+ * "must be less than 100" (attribute: attr, value: "100")
791
+ MESSAGE
792
+ )
793
+ expect(&assertion).to fail_with_message(message)
300
794
  end
301
795
  end
302
796
  end
303
797
 
304
798
  context 'with large numbers' do
305
799
  it do
306
- expect(validating_numericality(greater_than: 100_000))
307
- .to matcher.is_greater_than(100_000)
800
+ record = build_record_validating_numericality(greater_than: 100_000)
801
+ expect(record).to validate_numericality.is_greater_than(100_000)
308
802
  end
309
803
 
310
804
  it do
311
- expect(validating_numericality(less_than: 100_000))
312
- .to matcher.is_less_than(100_000)
805
+ record = build_record_validating_numericality(less_than: 100_000)
806
+ expect(record).to validate_numericality.is_less_than(100_000)
313
807
  end
314
808
 
315
809
  it do
316
- expect(validating_numericality(greater_than_or_equal_to: 100_000))
317
- .to matcher.is_greater_than_or_equal_to(100_000)
810
+ record = build_record_validating_numericality(
811
+ greater_than_or_equal_to: 100_000
812
+ )
813
+ expect(record).
814
+ to validate_numericality.
815
+ is_greater_than_or_equal_to(100_000)
318
816
  end
319
817
 
320
818
  it do
321
- expect(validating_numericality(less_than_or_equal_to: 100_000))
322
- .to matcher.is_less_than_or_equal_to(100_000)
323
- end
324
- end
325
-
326
- context 'with a custom validation message' do
327
- it 'accepts when the messages match' do
328
- expect(validating_numericality(message: 'custom')).
329
- to matcher.with_message(/custom/)
330
- end
331
-
332
- it 'rejects when the messages do not match' do
333
- expect(validating_numericality(message: 'custom')).
334
- not_to matcher.with_message(/wrong/)
819
+ record = build_record_validating_numericality(
820
+ less_than_or_equal_to: 100_000
821
+ )
822
+ expect(record).
823
+ to validate_numericality.
824
+ is_less_than_or_equal_to(100_000)
335
825
  end
336
826
  end
337
827
 
338
828
  context 'when the subject is stubbed' do
339
- it 'retains stubs on submatchers' do
340
- subject = define_model :example, attr: :string do
829
+ it 'retains that stub while the validate_numericality is matching' do
830
+ model = define_model :example, attr: :string do
341
831
  validates_numericality_of :attr, odd: true
342
832
  before_validation :set_attr!
343
833
  def set_attr!; self.attr = 5 end
344
- end.new
834
+ end
345
835
 
346
- allow(subject).to receive(:set_attr!)
347
- expect(subject).to matcher.odd
836
+ record = model.new
837
+ allow(record).to receive(:set_attr!)
838
+
839
+ expect(record).to validate_numericality_of(:attr).odd
348
840
  end
349
841
  end
350
842
 
351
843
  describe '#description' do
352
- context 'without submatchers' do
353
- it { expect(matcher.description).to eq 'only allow numbers for attr' }
844
+ context 'qualified with nothing' do
845
+ it 'describes that it allows numbers' do
846
+ matcher = validate_numericality_of(:attr)
847
+ expect(matcher.description).to eq 'only allow numbers for attr'
848
+ end
354
849
  end
355
850
 
356
- context 'with only integer option' do
357
- it do
358
- expect(matcher.only_integer.description)
359
- .to eq 'only allow integers for attr'
851
+ context 'qualified with only_integer' do
852
+ it 'describes that it allows integers' do
853
+ matcher = validate_numericality_of(:attr).only_integer
854
+ expect(matcher.description).to eq 'only allow integers for attr'
360
855
  end
361
856
  end
362
857
 
363
- [:odd, :even].each do |type|
364
- context "with #{type} option" do
365
- it do
366
- expect(matcher.__send__(type).description)
367
- .to eq "only allow #{type} numbers for attr"
858
+ qualifiers_under(:cardinality).each do |qualifier|
859
+ context "qualified with #{qualifier[:name]}" do
860
+ it "describes that it allows #{qualifier[:name]} numbers" do
861
+ matcher = validate_numericality_of(:attr).__send__(qualifier[:name])
862
+ expect(matcher.description).
863
+ to eq "only allow #{qualifier[:name]} numbers for attr"
368
864
  end
369
865
  end
370
866
  end
371
867
 
372
- [:is_greater_than,
373
- :is_greater_than_or_equal_to,
374
- :is_less_than,
375
- :is_less_than_or_equal_to,
376
- :is_equal_to ].each do |comparison|
377
- context "with #{comparison} option" do
378
- it do
379
- expect(matcher.__send__(comparison, 18).description)
380
- .to eq(
381
- 'only allow numbers for attr which are ' +
382
- "#{comparison.to_s.sub('is_', '').gsub('_', ' ')} 18"
868
+ qualifiers_under(:comparison).each do |qualifier|
869
+ comparison_phrase = qualifier[:validation_name].to_s.gsub('_', ' ')
870
+
871
+ context "qualified with #{qualifier[:name]}" do
872
+ it "describes that it allows numbers #{comparison_phrase} a certain value" do
873
+ matcher = validate_numericality_of(:attr).
874
+ __send__(qualifier[:name], 18)
875
+
876
+ expect(matcher.description).to eq(
877
+ "only allow numbers for attr which are #{comparison_phrase} 18"
383
878
  )
384
879
  end
385
880
  end
386
881
  end
387
882
 
388
- context 'with odd, is_greater_than_or_equal_to option' do
389
- it do
390
- expect(matcher.odd.is_greater_than_or_equal_to(18).description)
391
- .to eq(
392
- 'only allow odd numbers for attr ' +
393
- 'which are greater than or equal to 18'
883
+ context 'qualified with odd + is_greater_than_or_equal_to' do
884
+ it "describes that it allows odd numbers greater than or equal to a certain value" do
885
+ matcher = validate_numericality_of(:attr).
886
+ odd.
887
+ is_greater_than_or_equal_to(18)
888
+
889
+ expect(matcher.description).to eq(
890
+ 'only allow odd numbers for attr which are greater than or equal to 18'
891
+ )
892
+ end
893
+ end
894
+
895
+ context 'qualified with only integer + is_greater_than + less_than_or_equal_to' do
896
+ it 'describes that it allows integer greater than one value and less than or equal to another' do
897
+ matcher = validate_numericality_of(:attr).
898
+ only_integer.
899
+ is_greater_than(18).
900
+ is_less_than_or_equal_to(100)
901
+
902
+ expect(matcher.description).to eq(
903
+ 'only allow integers for attr which are greater than 18 and less than or equal to 100'
904
+ )
905
+ end
906
+ end
907
+
908
+ context 'qualified with strict' do
909
+ it 'describes that it relies upon a strict validation' do
910
+ matcher = validate_numericality_of(:attr).strict
911
+ expect(matcher.description).to eq(
912
+ 'only allow numbers for attr, strictly'
913
+ )
914
+ end
915
+
916
+ context 'and qualified with a comparison qualifier' do
917
+ it 'places the comparison description after "strictly"' do
918
+ matcher = validate_numericality_of(:attr).is_less_than(18).strict
919
+ expect(matcher.description).to eq(
920
+ 'only allow numbers for attr, strictly, which are less than 18'
394
921
  )
922
+ end
395
923
  end
396
924
  end
925
+ end
397
926
 
398
- context 'with only integer, is_greater_than and less_than_or_equal_to option' do
399
- it { expect(matcher.only_integer.is_greater_than(18).is_less_than_or_equal_to(100).description).
400
- to eq "only allow integers for attr which are greater than 18 and less than or equal to 100" }
927
+ def build_validation_options(args)
928
+ combination = args.fetch(:for)
929
+
930
+ combination.each_with_object({}) do |qualifier, hash|
931
+ value = self.class.default_validation_values.fetch(qualifier[:validation_name])
932
+ hash[qualifier[:validation_name]] = value
401
933
  end
402
934
  end
403
935
 
936
+ def apply_qualifiers!(args)
937
+ combination = args.fetch(:for)
938
+ matcher = args.fetch(:to)
939
+
940
+ combination.each do |qualifier|
941
+ args = self.class.default_qualifier_arguments.fetch(qualifier[:name])
942
+ matcher.__send__(qualifier[:name], *args)
943
+ end
944
+ end
945
+
946
+ def define_model_validating_numericality(options = {})
947
+ attribute_name = options.delete(:attribute_name) { self.attribute_name }
948
+
949
+ define_model 'Example', attribute_name => :string do |model|
950
+ model.validates_numericality_of(attribute_name, options)
951
+ end
952
+ end
404
953
 
405
- def validating_numericality(options = {})
406
- define_model :example, attr: :string do
407
- validates_numericality_of :attr, options
408
- end.new
954
+ def build_record_validating_numericality(options = {})
955
+ define_model_validating_numericality(options).new
409
956
  end
410
957
 
411
- def not_validating_numericality
412
- define_model(:example, attr: :string).new
958
+ def define_model_validating_nothing
959
+ define_model('Example', attribute_name => :string)
413
960
  end
414
961
 
415
- def matcher
416
- validate_numericality_of(:attr)
962
+ def build_record_validating_nothing
963
+ define_model_validating_nothing.new
417
964
  end
418
965
 
419
- def record_with_integer_column_of_limit(attribute, limit, validation_options = {})
966
+ def validate_numericality
967
+ validate_numericality_of(attribute_name)
968
+ end
969
+
970
+ def attribute_name
971
+ :attr
972
+ end
973
+
974
+ def define_model_with_integer_column_of_limit(limit, validation_options = {})
420
975
  column_options = { type: :integer, options: { limit: limit } }
421
- define_model :example, attribute => column_options do
422
- validates_numericality_of attribute, validation_options
423
- end.new
976
+ define_model :example, attribute_name => column_options do |model|
977
+ model.validates_numericality_of(attribute_name, validation_options)
978
+ end
979
+ end
980
+
981
+ def build_record_with_integer_column_of_limit(limit, validation_options = {})
982
+ define_model_with_integer_column_of_limit(limit, validation_options).new
983
+ end
984
+
985
+ def format_message_according_to_rails_version(message)
986
+ if rails_gte_4_2?
987
+ message
988
+ else
989
+ message.gsub(/"((?:\d+)(?:\.\d+)?)"/, '\1')
990
+ end
424
991
  end
425
992
  end