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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +9 -0
- data/.yardopts +2 -1
- data/Appraisals +62 -22
- data/Gemfile +1 -1
- data/Gemfile.lock +3 -3
- data/NEWS.md +87 -4
- data/README.md +2 -2
- data/Rakefile +18 -0
- data/features/activemodel_integration.feature +15 -0
- data/features/rails_integration.feature +1 -1
- data/features/step_definitions/activemodel_steps.rb +21 -0
- data/features/step_definitions/rails_steps.rb +5 -4
- data/gemfiles/3.0.gemfile +6 -3
- data/gemfiles/3.0.gemfile.lock +14 -4
- data/gemfiles/3.1.gemfile +10 -4
- data/gemfiles/3.1.gemfile.lock +32 -5
- data/gemfiles/3.1_1.9.2.gemfile +21 -0
- data/gemfiles/3.1_1.9.2.gemfile.lock +191 -0
- data/gemfiles/3.2.gemfile +9 -4
- data/gemfiles/3.2.gemfile.lock +28 -5
- data/gemfiles/4.0.0.gemfile +11 -3
- data/gemfiles/4.0.0.gemfile.lock +42 -5
- data/gemfiles/4.0.1.gemfile +11 -3
- data/gemfiles/4.0.1.gemfile.lock +42 -5
- data/gemfiles/4.1.gemfile +37 -0
- data/gemfiles/4.1.gemfile.lock +216 -0
- data/lib/shoulda/matchers/action_controller/callback_matcher.rb +202 -0
- data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +1 -1
- data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +2 -1
- data/lib/shoulda/matchers/action_controller/strong_parameters_matcher.rb +165 -0
- data/lib/shoulda/matchers/action_controller.rb +2 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +26 -4
- data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +6 -0
- data/lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb +2 -0
- data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +60 -18
- data/lib/shoulda/matchers/active_model/errors.rb +43 -1
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +14 -3
- data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +2 -1
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +100 -45
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +38 -5
- data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +27 -20
- data/lib/shoulda/matchers/active_record/association_matcher.rb +12 -2
- data/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +41 -0
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +24 -1
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -1
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record.rb +1 -0
- data/lib/shoulda/matchers/assertion_error.rb +8 -3
- data/lib/shoulda/matchers/doublespeak/double.rb +75 -0
- data/lib/shoulda/matchers/doublespeak/double_collection.rb +55 -0
- data/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +28 -0
- data/lib/shoulda/matchers/doublespeak/object_double.rb +33 -0
- data/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +31 -0
- data/lib/shoulda/matchers/doublespeak/structs.rb +10 -0
- data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +35 -0
- data/lib/shoulda/matchers/doublespeak/world.rb +39 -0
- data/lib/shoulda/matchers/doublespeak.rb +28 -0
- data/lib/shoulda/matchers/error.rb +20 -1
- data/lib/shoulda/matchers/independent/delegate_matcher/stubbed_target.rb +35 -0
- data/lib/shoulda/matchers/independent/delegate_matcher.rb +293 -0
- data/lib/shoulda/matchers/independent.rb +10 -0
- data/lib/shoulda/matchers/integrations/nunit_test_case_detection.rb +38 -0
- data/lib/shoulda/matchers/integrations/rspec.rb +13 -14
- data/lib/shoulda/matchers/integrations/test_unit.rb +19 -15
- data/lib/shoulda/matchers/integrations.rb +13 -0
- data/lib/shoulda/matchers/rails_shim.rb +16 -0
- data/lib/shoulda/matchers/version.rb +1 -1
- data/lib/shoulda/matchers.rb +15 -3
- data/spec/shoulda/matchers/action_controller/callback_matcher_spec.rb +82 -0
- data/spec/shoulda/matchers/action_controller/redirect_to_matcher_spec.rb +2 -2
- data/spec/shoulda/matchers/action_controller/render_template_matcher_spec.rb +5 -5
- data/spec/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +4 -4
- data/spec/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +38 -11
- data/spec/shoulda/matchers/action_controller/respond_with_matcher_spec.rb +1 -1
- data/spec/shoulda/matchers/action_controller/set_session_matcher_spec.rb +1 -1
- data/spec/shoulda/matchers/action_controller/set_the_flash_matcher_spec.rb +6 -6
- data/spec/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb +314 -0
- data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +32 -0
- data/spec/shoulda/matchers/active_model/ensure_inclusion_of_matcher_spec.rb +553 -211
- data/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +22 -0
- data/spec/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +38 -0
- data/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +42 -36
- data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +15 -1
- data/spec/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +4 -0
- data/spec/shoulda/matchers/active_record/have_db_index_matcher_spec.rb +4 -0
- data/spec/shoulda/matchers/doublespeak/double_collection_spec.rb +102 -0
- data/spec/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb +21 -0
- data/spec/shoulda/matchers/doublespeak/double_spec.rb +144 -0
- data/spec/shoulda/matchers/doublespeak/object_double_spec.rb +77 -0
- data/spec/shoulda/matchers/doublespeak/proxy_implementation_spec.rb +40 -0
- data/spec/shoulda/matchers/doublespeak/stub_implementation_spec.rb +88 -0
- data/spec/shoulda/matchers/doublespeak/world_spec.rb +88 -0
- data/spec/shoulda/matchers/doublespeak_spec.rb +19 -0
- data/spec/shoulda/matchers/independent/delegate_matcher/stubbed_target_spec.rb +43 -0
- data/spec/shoulda/matchers/independent/delegate_matcher_spec.rb +250 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/activemodel_helpers.rb +6 -2
- data/spec/support/controller_builder.rb +29 -1
- data/spec/support/rails_versions.rb +4 -0
- data/spec/support/test_application.rb +1 -1
- 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
|
-
|
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
|
@@ -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
|
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
|
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
|
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
|
390
|
+
case attribute_type
|
384
391
|
when :boolean
|
385
392
|
boolean_outside_values
|
386
|
-
when :
|
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
|
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
|
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
|
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 =
|
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
|
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,
|
32
|
+
# validates_absence_of :arms,
|
33
|
+
# message: "We're fresh outta arms here, soldier!"
|
33
34
|
# end
|
34
35
|
#
|
35
36
|
# # RSpec
|