mcmire-shoulda-matchers 2.5.0 → 2.6.1.docs.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +9 -0
  4. data/.yardopts +2 -1
  5. data/Appraisals +62 -22
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +3 -3
  8. data/NEWS.md +87 -4
  9. data/README.md +2 -2
  10. data/Rakefile +18 -0
  11. data/features/activemodel_integration.feature +15 -0
  12. data/features/rails_integration.feature +1 -1
  13. data/features/step_definitions/activemodel_steps.rb +21 -0
  14. data/features/step_definitions/rails_steps.rb +5 -4
  15. data/gemfiles/3.0.gemfile +6 -3
  16. data/gemfiles/3.0.gemfile.lock +14 -4
  17. data/gemfiles/3.1.gemfile +10 -4
  18. data/gemfiles/3.1.gemfile.lock +32 -5
  19. data/gemfiles/3.1_1.9.2.gemfile +21 -0
  20. data/gemfiles/3.1_1.9.2.gemfile.lock +191 -0
  21. data/gemfiles/3.2.gemfile +9 -4
  22. data/gemfiles/3.2.gemfile.lock +28 -5
  23. data/gemfiles/4.0.0.gemfile +11 -3
  24. data/gemfiles/4.0.0.gemfile.lock +42 -5
  25. data/gemfiles/4.0.1.gemfile +11 -3
  26. data/gemfiles/4.0.1.gemfile.lock +42 -5
  27. data/gemfiles/4.1.gemfile +37 -0
  28. data/gemfiles/4.1.gemfile.lock +216 -0
  29. data/lib/shoulda/matchers/action_controller/callback_matcher.rb +202 -0
  30. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +1 -1
  31. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +2 -1
  32. data/lib/shoulda/matchers/action_controller/strong_parameters_matcher.rb +165 -0
  33. data/lib/shoulda/matchers/action_controller.rb +2 -0
  34. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +26 -4
  35. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +6 -0
  36. data/lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb +2 -0
  37. data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +60 -18
  38. data/lib/shoulda/matchers/active_model/errors.rb +43 -1
  39. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +14 -3
  40. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +2 -1
  41. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +100 -45
  42. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +38 -5
  43. data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +27 -20
  44. data/lib/shoulda/matchers/active_record/association_matcher.rb +12 -2
  45. data/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +41 -0
  46. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +24 -1
  47. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -1
  48. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +1 -1
  49. data/lib/shoulda/matchers/active_record.rb +1 -0
  50. data/lib/shoulda/matchers/assertion_error.rb +8 -3
  51. data/lib/shoulda/matchers/doublespeak/double.rb +75 -0
  52. data/lib/shoulda/matchers/doublespeak/double_collection.rb +55 -0
  53. data/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +28 -0
  54. data/lib/shoulda/matchers/doublespeak/object_double.rb +33 -0
  55. data/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +31 -0
  56. data/lib/shoulda/matchers/doublespeak/structs.rb +10 -0
  57. data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +35 -0
  58. data/lib/shoulda/matchers/doublespeak/world.rb +39 -0
  59. data/lib/shoulda/matchers/doublespeak.rb +28 -0
  60. data/lib/shoulda/matchers/error.rb +20 -1
  61. data/lib/shoulda/matchers/independent/delegate_matcher/stubbed_target.rb +35 -0
  62. data/lib/shoulda/matchers/independent/delegate_matcher.rb +293 -0
  63. data/lib/shoulda/matchers/independent.rb +10 -0
  64. data/lib/shoulda/matchers/integrations/nunit_test_case_detection.rb +38 -0
  65. data/lib/shoulda/matchers/integrations/rspec.rb +13 -14
  66. data/lib/shoulda/matchers/integrations/test_unit.rb +19 -15
  67. data/lib/shoulda/matchers/integrations.rb +13 -0
  68. data/lib/shoulda/matchers/rails_shim.rb +16 -0
  69. data/lib/shoulda/matchers/version.rb +1 -1
  70. data/lib/shoulda/matchers.rb +15 -3
  71. data/spec/shoulda/matchers/action_controller/callback_matcher_spec.rb +82 -0
  72. data/spec/shoulda/matchers/action_controller/redirect_to_matcher_spec.rb +2 -2
  73. data/spec/shoulda/matchers/action_controller/render_template_matcher_spec.rb +5 -5
  74. data/spec/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +4 -4
  75. data/spec/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +38 -11
  76. data/spec/shoulda/matchers/action_controller/respond_with_matcher_spec.rb +1 -1
  77. data/spec/shoulda/matchers/action_controller/set_session_matcher_spec.rb +1 -1
  78. data/spec/shoulda/matchers/action_controller/set_the_flash_matcher_spec.rb +6 -6
  79. data/spec/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb +314 -0
  80. data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +32 -0
  81. data/spec/shoulda/matchers/active_model/ensure_inclusion_of_matcher_spec.rb +553 -211
  82. data/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +22 -0
  83. data/spec/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +38 -0
  84. data/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +42 -36
  85. data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +15 -1
  86. data/spec/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +4 -0
  87. data/spec/shoulda/matchers/active_record/have_db_index_matcher_spec.rb +4 -0
  88. data/spec/shoulda/matchers/doublespeak/double_collection_spec.rb +102 -0
  89. data/spec/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb +21 -0
  90. data/spec/shoulda/matchers/doublespeak/double_spec.rb +144 -0
  91. data/spec/shoulda/matchers/doublespeak/object_double_spec.rb +77 -0
  92. data/spec/shoulda/matchers/doublespeak/proxy_implementation_spec.rb +40 -0
  93. data/spec/shoulda/matchers/doublespeak/stub_implementation_spec.rb +88 -0
  94. data/spec/shoulda/matchers/doublespeak/world_spec.rb +88 -0
  95. data/spec/shoulda/matchers/doublespeak_spec.rb +19 -0
  96. data/spec/shoulda/matchers/independent/delegate_matcher/stubbed_target_spec.rb +43 -0
  97. data/spec/shoulda/matchers/independent/delegate_matcher_spec.rb +250 -0
  98. data/spec/spec_helper.rb +15 -0
  99. data/spec/support/activemodel_helpers.rb +6 -2
  100. data/spec/support/controller_builder.rb +29 -1
  101. data/spec/support/rails_versions.rb +4 -0
  102. data/spec/support/test_application.rb +1 -1
  103. metadata +59 -10
@@ -0,0 +1,165 @@
1
+ require 'delegate'
2
+
3
+ begin
4
+ require 'strong_parameters'
5
+ rescue LoadError
6
+ end
7
+
8
+ require 'active_support/hash_with_indifferent_access'
9
+
10
+ module Shoulda
11
+ module Matchers
12
+ module ActionController
13
+ # The `permit` matcher tests usage of strong parameters, specifically
14
+ # its #permit method.
15
+ #
16
+ # class UsersController < ApplicationController
17
+ # def create
18
+ # user = User.new(user_params)
19
+ #
20
+ # if user.save
21
+ # # ...
22
+ # else
23
+ # # ...
24
+ # end
25
+ # end
26
+ #
27
+ # private
28
+ #
29
+ # def user_params
30
+ # require(:user).permit(:email, :password)
31
+ # end
32
+ # end
33
+ #
34
+ # # RSpec
35
+ # describe UsersController do
36
+ # it { should permit(:email, :password).for(:user) }
37
+ # end
38
+ #
39
+ # # Test::Unit
40
+ # class UsersControllerTest < ActionController::TestCase
41
+ # should permit(:email, :password).for(:user)
42
+ # end
43
+ #
44
+ def permit(*params)
45
+ StrongParametersMatcher.new(params).in_context(self)
46
+ end
47
+
48
+ # @private
49
+ class StrongParametersMatcher
50
+ attr_writer :stubbed_params
51
+
52
+ def initialize(expected_permitted_params)
53
+ @action = nil
54
+ @verb = nil
55
+ @request_params = {}
56
+ @expected_permitted_params = expected_permitted_params
57
+ set_double_collection
58
+ end
59
+
60
+ def for(action, options = {})
61
+ @action = action
62
+ @verb = options.fetch(:verb, default_verb)
63
+ @request_params = options.fetch(:params, {})
64
+ self
65
+ end
66
+
67
+ def in_context(context)
68
+ @context = context
69
+ self
70
+ end
71
+
72
+ def description
73
+ "permit #{verb.upcase} ##{action} to receive parameters #{param_names_as_sentence}"
74
+ end
75
+
76
+ def matches?(controller)
77
+ @controller = controller
78
+ ensure_action_and_verb_present!
79
+
80
+ Doublespeak.with_doubles_activated do
81
+ context.__send__(verb, action, request_params)
82
+ end
83
+
84
+ unpermitted_params.empty?
85
+ end
86
+
87
+ def failure_message
88
+ "Expected controller to permit #{unpermitted_params.to_sentence}, but it did not."
89
+ end
90
+ alias failure_message_for_should failure_message
91
+
92
+ def failure_message_when_negated
93
+ "Expected controller not to permit #{verified_permitted_params.to_sentence}, but it did."
94
+ end
95
+ alias failure_message_for_should_not failure_message_when_negated
96
+
97
+ private
98
+
99
+ attr_reader :controller, :double_collection, :action, :verb,
100
+ :request_params, :expected_permitted_params, :context
101
+
102
+ def set_double_collection
103
+ @double_collection =
104
+ Doublespeak.register_double_collection(::ActionController::Parameters)
105
+
106
+ @double_collection.register_stub(:require).to_return { |params| params }
107
+ @double_collection.register_proxy(:permit)
108
+ end
109
+
110
+ def actual_permitted_params
111
+ double_collection.calls_to(:permit).inject([]) do |all_param_names, call|
112
+ all_param_names + call.args
113
+ end.flatten
114
+ end
115
+
116
+ def permit_called?
117
+ actual_permitted_params.any?
118
+ end
119
+
120
+ def unpermitted_params
121
+ expected_permitted_params - actual_permitted_params
122
+ end
123
+
124
+ def verified_permitted_params
125
+ expected_permitted_params & actual_permitted_params
126
+ end
127
+
128
+ def ensure_action_and_verb_present!
129
+ if action.blank?
130
+ raise ActionNotDefinedError
131
+ end
132
+
133
+ if verb.blank?
134
+ raise VerbNotDefinedError
135
+ end
136
+ end
137
+
138
+ def default_verb
139
+ case action
140
+ when :create then :post
141
+ when :update then RailsShim.verb_for_update
142
+ end
143
+ end
144
+
145
+ def param_names_as_sentence
146
+ expected_permitted_params.map(&:inspect).to_sentence
147
+ end
148
+
149
+ # @private
150
+ class ActionNotDefinedError < StandardError
151
+ def message
152
+ 'You must specify the controller action using the #for method.'
153
+ end
154
+ end
155
+
156
+ # @private
157
+ class VerbNotDefinedError < StandardError
158
+ def message
159
+ 'You must specify an HTTP verb when using a non-RESTful action. For example: for(:authorize, verb: :post)'
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -8,6 +8,8 @@ require 'shoulda/matchers/action_controller/route_matcher'
8
8
  require 'shoulda/matchers/action_controller/redirect_to_matcher'
9
9
  require 'shoulda/matchers/action_controller/render_template_matcher'
10
10
  require 'shoulda/matchers/action_controller/rescue_from_matcher'
11
+ require 'shoulda/matchers/action_controller/callback_matcher'
12
+ require 'shoulda/matchers/action_controller/strong_parameters_matcher'
11
13
 
12
14
  module Shoulda
13
15
  module Matchers
@@ -169,6 +169,7 @@ module Shoulda
169
169
  self.values_to_match = values
170
170
  self.message_finder_factory = ValidationMessageFinder
171
171
  self.options = {}
172
+ self.after_setting_value_callback = -> {}
172
173
  end
173
174
 
174
175
  def for(attribute)
@@ -184,9 +185,12 @@ module Shoulda
184
185
 
185
186
  def with_message(message, options={})
186
187
  self.options[:expected_message] = message
188
+ self.options[:expected_message_values] = options.fetch(:values, {})
189
+
187
190
  if options.key?(:against)
188
191
  self.attribute_to_check_message_against = options[:against]
189
192
  end
193
+
190
194
  self
191
195
  end
192
196
 
@@ -195,12 +199,17 @@ module Shoulda
195
199
  self
196
200
  end
197
201
 
202
+ # @private
203
+ def _after_setting_value(&callback)
204
+ self.after_setting_value_callback = callback
205
+ end
206
+
198
207
  def matches?(instance)
199
208
  self.instance = instance
200
209
 
201
210
  values_to_match.none? do |value|
202
211
  self.value = value
203
- instance.__send__("#{attribute_to_set}=", value)
212
+ set_value(value)
204
213
  errors_match?
205
214
  end
206
215
  end
@@ -223,7 +232,12 @@ module Shoulda
223
232
 
224
233
  attr_accessor :values_to_match, :message_finder_factory,
225
234
  :instance, :attribute_to_set, :attribute_to_check_message_against,
226
- :context, :value, :matched_error
235
+ :context, :value, :matched_error, :after_setting_value_callback
236
+
237
+ def set_value(value)
238
+ instance.__send__("#{attribute_to_set}=", value)
239
+ after_setting_value_callback.call
240
+ end
227
241
 
228
242
  def errors_match?
229
243
  has_messages? && errors_for_attribute_match?
@@ -295,10 +309,18 @@ module Shoulda
295
309
  def default_attribute_message
296
310
  default_error_message(
297
311
  options[:expected_message],
312
+ default_attribute_message_values
313
+ )
314
+ end
315
+
316
+ def default_attribute_message_values
317
+ defaults = {
298
318
  model_name: model_name,
299
319
  instance: instance,
300
- attribute: attribute_to_set
301
- )
320
+ attribute: attribute_to_set,
321
+ }
322
+
323
+ defaults.merge(options[:expected_message_values])
302
324
  end
303
325
 
304
326
  def model_name
@@ -1,3 +1,5 @@
1
+ require 'forwardable'
2
+
1
3
  module Shoulda
2
4
  module Matchers
3
5
  module ActiveModel
@@ -40,6 +42,10 @@ module Shoulda
40
42
  @allow_matcher.strict
41
43
  self
42
44
  end
45
+
46
+ private
47
+
48
+ attr_reader :allow_matcher
43
49
  end
44
50
  end
45
51
  end
@@ -56,6 +56,8 @@ module Shoulda
56
56
  # Use `with_message` if you are using a custom validation message.
57
57
  #
58
58
  # class Game
59
+ # include ActiveModel::Model
60
+ #
59
61
  # validates_exclusion_of :weapon,
60
62
  # in: ['pistol', 'paintball gun', 'stick'],
61
63
  # message: 'You chose a puny weapon'
@@ -1,3 +1,5 @@
1
+ require 'bigdecimal'
2
+
1
3
  module Shoulda
2
4
  module Matchers
3
5
  module ActiveModel
@@ -216,7 +218,7 @@ module Shoulda
216
218
  class EnsureInclusionOfMatcher < ValidationMatcher
217
219
  ARBITRARY_OUTSIDE_STRING = 'shouldamatchersteststring'
218
220
  ARBITRARY_OUTSIDE_FIXNUM = 123456789
219
- ARBITRARY_OUTSIDE_DECIMAL = 0.123456789
221
+ ARBITRARY_OUTSIDE_DECIMAL = BigDecimal.new('0.123456789')
220
222
  BOOLEAN_ALLOWS_BOOLEAN_MESSAGE = <<EOT
221
223
  You are using `ensure_inclusion_of` to assert that a boolean column allows
222
224
  boolean values and disallows non-boolean ones. Assuming you are using
@@ -230,11 +232,6 @@ You are using `ensure_inclusion_of` to assert that a boolean column allows nil.
230
232
  Be aware that it is not possible to fully test this, as anything other than
231
233
  true, false or nil will be converted to false. Hence, you should consider
232
234
  removing this test and the corresponding validation.
233
- EOT
234
- BOOLEAN_ALLOWS_NIL_WITH_NOT_NULL_MESSAGE = <<EOT
235
- You have specified that your model's #{@attribute} should ensure inclusion of nil.
236
- However, #{@attribute} is a boolean column which does not allow null values.
237
- Hence, this test will fail and there is no way to make it pass.
238
235
  EOT
239
236
 
240
237
  def initialize(attribute)
@@ -292,13 +289,9 @@ EOT
292
289
  if @range
293
290
  @low_message ||= :inclusion
294
291
  @high_message ||= :inclusion
295
-
296
- disallows_lower_value &&
297
- allows_minimum_value &&
298
- disallows_higher_value &&
299
- allows_maximum_value
292
+ matches_for_range?
300
293
  elsif @array
301
- if allows_all_values_in_array? && allows_blank_value? && allows_nil_value? && disallows_value_outside_of_array?
294
+ if matches_for_array?
302
295
  true
303
296
  else
304
297
  @failure_message = "#{@array} doesn't match array in validation"
@@ -309,6 +302,20 @@ EOT
309
302
 
310
303
  private
311
304
 
305
+ def matches_for_range?
306
+ disallows_lower_value &&
307
+ allows_minimum_value &&
308
+ disallows_higher_value &&
309
+ allows_maximum_value
310
+ end
311
+
312
+ def matches_for_array?
313
+ allows_all_values_in_array? &&
314
+ allows_blank_value? &&
315
+ allows_nil_value? &&
316
+ disallows_value_outside_of_array?
317
+ end
318
+
312
319
  def allows_blank_value?
313
320
  if @options.key?(:allow_blank)
314
321
  blank_values = ['', ' ', "\n", "\r", "\t", "\f"]
@@ -353,7 +360,7 @@ EOT
353
360
  end
354
361
 
355
362
  def disallows_value_outside_of_array?
356
- if attribute_column.type == :boolean
363
+ if attribute_type == :boolean
357
364
  case @array
358
365
  when [true, false]
359
366
  Shoulda::Matchers.warn BOOLEAN_ALLOWS_BOOLEAN_MESSAGE
@@ -363,7 +370,7 @@ EOT
363
370
  Shoulda::Matchers.warn BOOLEAN_ALLOWS_NIL_MESSAGE
364
371
  return true
365
372
  else
366
- raise NonNullableBooleanError, BOOLEAN_ALLOWS_NIL_WITH_NOT_NULL_MESSAGE
373
+ raise NonNullableBooleanError.create(@attribute)
367
374
  end
368
375
  end
369
376
  end
@@ -380,10 +387,10 @@ EOT
380
387
  end
381
388
 
382
389
  def outside_values
383
- case attribute_column.type
390
+ case attribute_type
384
391
  when :boolean
385
392
  boolean_outside_values
386
- when :integer, :float
393
+ when :fixnum
387
394
  [ARBITRARY_OUTSIDE_FIXNUM]
388
395
  when :decimal
389
396
  [ARBITRARY_OUTSIDE_DECIMAL]
@@ -401,15 +408,50 @@ EOT
401
408
  else raise CouldNotDetermineValueOutsideOfArray
402
409
  end
403
410
 
404
- if attribute_column.null
411
+ if attribute_allows_nil?
405
412
  values << nil
406
413
  end
407
414
 
408
415
  values
409
416
  end
410
417
 
418
+ def attribute_type
419
+ if attribute_column
420
+ column_type_to_attribute_type(attribute_column.type)
421
+ else
422
+ value_to_attribute_type(@subject.__send__(@attribute))
423
+ end
424
+ end
425
+
426
+ def attribute_allows_nil?
427
+ if attribute_column
428
+ attribute_column.null
429
+ else
430
+ true
431
+ end
432
+ end
433
+
411
434
  def attribute_column
412
- @subject.class.columns_hash[@attribute.to_s]
435
+ if @subject.class.respond_to?(:columns_hash)
436
+ @subject.class.columns_hash[@attribute.to_s]
437
+ end
438
+ end
439
+
440
+ def column_type_to_attribute_type(type)
441
+ case type
442
+ when :boolean, :decimal then type
443
+ when :integer, :float then :fixnum
444
+ else :default
445
+ end
446
+ end
447
+
448
+ def value_to_attribute_type(value)
449
+ case value
450
+ when true, false then :boolean
451
+ when BigDecimal then :decimal
452
+ when Fixnum then :fixnum
453
+ else :default
454
+ end
413
455
  end
414
456
  end
415
457
  end
@@ -3,8 +3,50 @@ module Shoulda
3
3
  module ActiveModel
4
4
  # @private
5
5
  class CouldNotDetermineValueOutsideOfArray < RuntimeError; end
6
+
6
7
  # @private
7
- class NonNullableBooleanError < Shoulda::Matchers::Error; end
8
+ class NonNullableBooleanError < Shoulda::Matchers::Error
9
+ def self.create(attribute)
10
+ super(attribute: attribute)
11
+ end
12
+
13
+ attr_accessor :attribute
14
+
15
+ def message
16
+ <<-EOT.strip
17
+ You have specified that your model's #{attribute} should ensure inclusion of nil.
18
+ However, #{attribute} is a boolean column which does not allow null values.
19
+ Hence, this test will fail and there is no way to make it pass.
20
+ EOT
21
+ end
22
+ end
23
+
24
+ # @private
25
+ class CouldNotSetPasswordError < Shoulda::Matchers::Error
26
+ def self.create(model)
27
+ super(model: model)
28
+ end
29
+
30
+ attr_accessor :model
31
+
32
+ def message
33
+ <<-EOT.strip
34
+ The validation failed because your #{model_name} model declares `has_secure_password`, and
35
+ `validate_presence_of` was called on a #{record_name} which has `password` already set to a value.
36
+ Please use a #{record_name} with an empty `password` instead.
37
+ EOT
38
+ end
39
+
40
+ private
41
+
42
+ def model_name
43
+ model.name
44
+ end
45
+
46
+ def record_name
47
+ model_name.humanize.downcase
48
+ end
49
+ end
8
50
  end
9
51
  end
10
52
  end
@@ -4,6 +4,14 @@ module Shoulda
4
4
  module NumericalityMatchers
5
5
  # @private
6
6
  class ComparisonMatcher < ValidationMatcher
7
+ ERROR_MESSAGES = {
8
+ :> => :greater_than,
9
+ :>= => :greater_than_or_equal_to,
10
+ :< => :less_than,
11
+ :<= => :less_than_or_equal_to,
12
+ :== => :equal_to
13
+ }
14
+
7
15
  def initialize(numericality_matcher, value, operator)
8
16
  unless numericality_matcher.respond_to? :diff_to_compare
9
17
  raise ArgumentError, 'numericality_matcher is invalid'
@@ -11,7 +19,8 @@ module Shoulda
11
19
  @numericality_matcher = numericality_matcher
12
20
  @value = value
13
21
  @operator = operator
14
- @message = nil
22
+ @message = ERROR_MESSAGES[operator]
23
+ @comparison_combos = comparison_combos
15
24
  end
16
25
 
17
26
  def for(attribute)
@@ -64,8 +73,10 @@ module Shoulda
64
73
  end
65
74
 
66
75
  def all_bounds_correct?
67
- comparison_combos.all? do |diff, checker_type|
68
- __send__(checker_type, @value + diff, @message)
76
+ @comparison_combos.all? do |diff, checker_type|
77
+ __send__(checker_type, @value + diff) do |matcher|
78
+ matcher.with_message(@message, values: { count: @value })
79
+ end
69
80
  end
70
81
  end
71
82
  end
@@ -29,7 +29,8 @@ module Shoulda
29
29
  # class Artillery
30
30
  # include ActiveModel::Model
31
31
  #
32
- # validates_absence_of :arms, message: "We're fresh outta arms here, soldier!"
32
+ # validates_absence_of :arms,
33
+ # message: "We're fresh outta arms here, soldier!"
33
34
  # end
34
35
  #
35
36
  # # RSpec