shoulda-matchers 6.5.0 → 8.0.0
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.
- checksums.yaml +4 -4
- data/README.md +9 -5
- data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +1 -5
- data/lib/shoulda/matchers/action_controller/route_matcher.rb +2 -2
- data/lib/shoulda/matchers/action_controller/route_params.rb +1 -1
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +4 -4
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +5 -5
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +3 -3
- data/lib/shoulda/matchers/active_model/errors.rb +2 -2
- data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_model/helpers.rb +3 -1
- data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +25 -2
- data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +25 -2
- data/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +46 -11
- data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +26 -3
- data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +25 -2
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +47 -18
- data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +26 -3
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +46 -11
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +43 -17
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +9 -12
- data/lib/shoulda/matchers/active_model/validator.rb +6 -2
- data/lib/shoulda/matchers/active_record/association_matcher.rb +119 -2
- data/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +1 -3
- data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +1 -3
- data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +7 -1
- data/lib/shoulda/matchers/active_record/have_attached_matcher.rb +164 -6
- data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +26 -2
- data/lib/shoulda/matchers/active_record/have_rich_text_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record/uniqueness/namespace.rb +19 -2
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +12 -22
- data/lib/shoulda/matchers/doublespeak/double.rb +4 -4
- data/lib/shoulda/matchers/doublespeak/method_call.rb +1 -1
- data/lib/shoulda/matchers/doublespeak/object_double.rb +3 -3
- data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +2 -4
- data/lib/shoulda/matchers/integrations/configuration.rb +0 -2
- data/lib/shoulda/matchers/matcher_collection.rb +99 -0
- data/lib/shoulda/matchers/rails_shim.rb +4 -8
- data/lib/shoulda/matchers/routing.rb +1 -1
- data/lib/shoulda/matchers/util/word_wrap.rb +4 -4
- data/lib/shoulda/matchers/version.rb +1 -1
- data/lib/shoulda/matchers.rb +1 -0
- data/shoulda-matchers.gemspec +5 -3
- metadata +22 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 223ff92a4826a7403e0f1016553453323108899c2d2f7029873ea9f99b68a1c0
|
|
4
|
+
data.tar.gz: 2014f3ded4620ce71736c035b8f73959a2a85c1adead8067692173fbca6907de
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3a23155ac210adef58ea71d3cead443b1277b0292972f1365c140b20c671de2f743c39516871ab0ce6ffe0e48b2298a86635533f9fb1f2838577320505b6f91f
|
|
7
|
+
data.tar.gz: 6999714a118645d1aff05c32ad2a7337111aa7c7501f42822a34ca28e31b5d7caa84d49138ecebf0c1dfad17e9f24fd25583e41b73f9e981c4b9ff1da673fd39
|
data/README.md
CHANGED
|
@@ -63,7 +63,7 @@ Start by including `shoulda-matchers` in your Gemfile:
|
|
|
63
63
|
|
|
64
64
|
```ruby
|
|
65
65
|
group :test do
|
|
66
|
-
gem 'shoulda-matchers', '~>
|
|
66
|
+
gem 'shoulda-matchers', '~> 8.0'
|
|
67
67
|
end
|
|
68
68
|
```
|
|
69
69
|
|
|
@@ -125,7 +125,7 @@ Otherwise, add `shoulda-matchers` to your Gemfile:
|
|
|
125
125
|
|
|
126
126
|
```ruby
|
|
127
127
|
group :test do
|
|
128
|
-
gem 'shoulda-matchers', '~>
|
|
128
|
+
gem 'shoulda-matchers', '~> 8.0'
|
|
129
129
|
end
|
|
130
130
|
```
|
|
131
131
|
|
|
@@ -341,6 +341,7 @@ RSpec.describe MySpecialModel, type: :model do
|
|
|
341
341
|
end
|
|
342
342
|
```
|
|
343
343
|
|
|
344
|
+
<a name="should-vs-is_expectedto"></a>
|
|
344
345
|
### `should` vs `is_expected.to`
|
|
345
346
|
|
|
346
347
|
In this README and throughout the documentation, you'll notice that we use the
|
|
@@ -503,14 +504,18 @@ machine, understanding the codebase, and creating a good pull request.
|
|
|
503
504
|
|
|
504
505
|
## Compatibility
|
|
505
506
|
|
|
506
|
-
Shoulda Matchers is tested and supported against Ruby 3.
|
|
507
|
-
|
|
507
|
+
Shoulda Matchers is tested and supported against Ruby 3.3+, Rails
|
|
508
|
+
7.2+, RSpec 3.x, and Minitest 5.x.
|
|
508
509
|
|
|
509
510
|
- For Ruby < 2.4 and Rails < 4.1 compatibility, please use [v3.1.3][v3.1.3].
|
|
510
511
|
- For Ruby < 3.0 and Rails < 6.1 compatibility, please use [v4.5.1][v4.5.1].
|
|
512
|
+
- For Rails < 7.1 compatibility, please use [v6.5.0][v6.5.0].
|
|
513
|
+
- For Ruby < 3.3 and Rails < 7.2 compatibility, please use [v7.0.1][v7.0.1].
|
|
511
514
|
|
|
512
515
|
[v3.1.3]: https://github.com/thoughtbot/shoulda-matchers/tree/v3.1.3
|
|
513
516
|
[v4.5.1]: https://github.com/thoughtbot/shoulda-matchers/tree/v4.5.1
|
|
517
|
+
[v6.5.0]: https://github.com/thoughtbot/shoulda-matchers/tree/v6.5.0
|
|
518
|
+
[v7.0.1]: https://github.com/thoughtbot/shoulda-matchers/tree/v7.0.1
|
|
514
519
|
|
|
515
520
|
## Versioning
|
|
516
521
|
|
|
@@ -559,5 +564,4 @@ We are [available for hire][hire].
|
|
|
559
564
|
[community]: https://thoughtbot.com/community?utm_source=github
|
|
560
565
|
[hire]: https://thoughtbot.com/hire-us?utm_source=github
|
|
561
566
|
|
|
562
|
-
|
|
563
567
|
<!-- END /templates/footer.md -->
|
|
@@ -111,11 +111,7 @@ module Shoulda
|
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
def rendered_with_expected_layout?
|
|
114
|
-
|
|
115
|
-
true
|
|
116
|
-
else
|
|
117
|
-
rendered_layouts.include?(@expected_layout)
|
|
118
|
-
end
|
|
114
|
+
@expected_layout.nil? || rendered_layouts.include?(@expected_layout)
|
|
119
115
|
end
|
|
120
116
|
|
|
121
117
|
def rendered_layouts
|
|
@@ -125,7 +125,7 @@ module Shoulda
|
|
|
125
125
|
# @return [RouteMatcher]
|
|
126
126
|
#
|
|
127
127
|
def route(method, path, port: nil)
|
|
128
|
-
RouteMatcher.new(self, method, path, port:
|
|
128
|
+
RouteMatcher.new(self, method, path, port:)
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
# @private
|
|
@@ -190,7 +190,7 @@ module Shoulda
|
|
|
190
190
|
def route_recognized?
|
|
191
191
|
context.send(
|
|
192
192
|
:assert_routing,
|
|
193
|
-
{ method
|
|
193
|
+
{ method:, path: },
|
|
194
194
|
params,
|
|
195
195
|
)
|
|
196
196
|
true
|
|
@@ -27,7 +27,7 @@ module Shoulda
|
|
|
27
27
|
|
|
28
28
|
def extract_params_from_string
|
|
29
29
|
controller, action = args[0].split('#')
|
|
30
|
-
params = (args[1] || {}).merge!(controller
|
|
30
|
+
params = (args[1] || {}).merge!(controller:, action:)
|
|
31
31
|
normalize_values(params)
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -204,9 +204,9 @@ module Shoulda
|
|
|
204
204
|
def attribute_changed_value_error
|
|
205
205
|
AttributeChangedValueError.create(
|
|
206
206
|
model: object.class,
|
|
207
|
-
attribute_name
|
|
208
|
-
value_written
|
|
209
|
-
value_read
|
|
207
|
+
attribute_name:,
|
|
208
|
+
value_written:,
|
|
209
|
+
value_read:,
|
|
210
210
|
)
|
|
211
211
|
end
|
|
212
212
|
|
|
@@ -217,7 +217,7 @@ module Shoulda
|
|
|
217
217
|
def attribute_does_not_exist_error
|
|
218
218
|
AttributeDoesNotExistError.create(
|
|
219
219
|
model: object.class,
|
|
220
|
-
attribute_name
|
|
220
|
+
attribute_name:,
|
|
221
221
|
value: value_written,
|
|
222
222
|
)
|
|
223
223
|
end
|
data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb
CHANGED
|
@@ -31,9 +31,9 @@ module Shoulda
|
|
|
31
31
|
@_attribute_setter ||= AttributeSetter.new(
|
|
32
32
|
matcher_name: :allow_value,
|
|
33
33
|
object: instance,
|
|
34
|
-
attribute_name
|
|
35
|
-
value
|
|
36
|
-
ignore_interference_by_writer
|
|
34
|
+
attribute_name:,
|
|
35
|
+
value:,
|
|
36
|
+
ignore_interference_by_writer:,
|
|
37
37
|
after_set_callback: after_setting_value_callback,
|
|
38
38
|
)
|
|
39
39
|
end
|
|
@@ -46,9 +46,9 @@ module Shoulda
|
|
|
46
46
|
@_validator ||= Validator.new(
|
|
47
47
|
instance,
|
|
48
48
|
attribute_to_check_message_against,
|
|
49
|
-
context
|
|
49
|
+
context:,
|
|
50
50
|
expects_strict: expects_strict?,
|
|
51
|
-
expected_message
|
|
51
|
+
expected_message:,
|
|
52
52
|
)
|
|
53
53
|
end
|
|
54
54
|
|
|
@@ -458,7 +458,7 @@ module Shoulda
|
|
|
458
458
|
message << '.'
|
|
459
459
|
else
|
|
460
460
|
message << " producing these validation errors:\n\n"
|
|
461
|
-
message << validator.
|
|
461
|
+
message << validator.formatted_validation_error_messages
|
|
462
462
|
end
|
|
463
463
|
end
|
|
464
464
|
|
|
@@ -672,8 +672,8 @@ pass, or do something else entirely.
|
|
|
672
672
|
|
|
673
673
|
def default_attribute_message_values
|
|
674
674
|
defaults = {
|
|
675
|
-
model_name
|
|
676
|
-
instance
|
|
675
|
+
model_name:,
|
|
676
|
+
instance:,
|
|
677
677
|
attribute: attribute_to_check_message_against,
|
|
678
678
|
}
|
|
679
679
|
|
|
@@ -7,7 +7,7 @@ module Shoulda
|
|
|
7
7
|
# @private
|
|
8
8
|
class NonNullableBooleanError < Shoulda::Matchers::Error
|
|
9
9
|
def self.create(attribute)
|
|
10
|
-
super(attribute:
|
|
10
|
+
super(attribute:)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
attr_accessor :attribute
|
|
@@ -24,7 +24,7 @@ Hence, this test will fail and there is no way to make it pass.
|
|
|
24
24
|
# @private
|
|
25
25
|
class CouldNotSetPasswordError < Shoulda::Matchers::Error
|
|
26
26
|
def self.create(model)
|
|
27
|
-
super(model:
|
|
27
|
+
super(model:)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
attr_accessor :model
|
|
@@ -7,8 +7,10 @@ module Shoulda
|
|
|
7
7
|
format_validation_errors(object.errors)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def format_validation_errors(errors)
|
|
10
|
+
def format_validation_errors(errors, attr = nil)
|
|
11
11
|
list_items = errors.to_hash.keys.map do |attribute|
|
|
12
|
+
next if attr && attr.to_sym != attribute.to_sym
|
|
13
|
+
|
|
12
14
|
messages = errors[attribute]
|
|
13
15
|
"* #{attribute}: #{messages}"
|
|
14
16
|
end
|
|
@@ -70,10 +70,33 @@ module Shoulda
|
|
|
70
70
|
# with_message("there shall be peace on Earth")
|
|
71
71
|
# end
|
|
72
72
|
#
|
|
73
|
+
# #### Multiple attributes
|
|
74
|
+
#
|
|
75
|
+
# You can pass multiple attributes to assert that each one has the
|
|
76
|
+
# validation. Any qualifier chained on the matcher is applied to
|
|
77
|
+
# every attribute uniformly.
|
|
78
|
+
#
|
|
79
|
+
# class Robot
|
|
80
|
+
# include ActiveModel::Model
|
|
81
|
+
# attr_accessor :arms, :legs
|
|
82
|
+
#
|
|
83
|
+
# validates_absence_of :arms, :legs
|
|
84
|
+
# end
|
|
85
|
+
#
|
|
86
|
+
# # RSpec
|
|
87
|
+
# RSpec.describe Robot, type: :model do
|
|
88
|
+
# it { should validate_absence_of(:arms, :legs) }
|
|
89
|
+
# end
|
|
90
|
+
#
|
|
91
|
+
# # Minitest (Shoulda)
|
|
92
|
+
# class RobotTest < ActiveSupport::TestCase
|
|
93
|
+
# should validate_absence_of(:arms, :legs)
|
|
94
|
+
# end
|
|
95
|
+
#
|
|
73
96
|
# @return [ValidateAbsenceOfMatcher}
|
|
74
97
|
#
|
|
75
|
-
def validate_absence_of(
|
|
76
|
-
ValidateAbsenceOfMatcher.new(attr)
|
|
98
|
+
def validate_absence_of(*attrs)
|
|
99
|
+
MatcherCollection.build(attrs) { |attr| ValidateAbsenceOfMatcher.new(attr) }
|
|
77
100
|
end
|
|
78
101
|
|
|
79
102
|
# @private
|
|
@@ -73,10 +73,33 @@ module Shoulda
|
|
|
73
73
|
# with_message('You must accept the terms of service')
|
|
74
74
|
# end
|
|
75
75
|
#
|
|
76
|
+
# #### Multiple attributes
|
|
77
|
+
#
|
|
78
|
+
# You can pass multiple attributes to assert that each one has the
|
|
79
|
+
# validation. Any qualifier chained on the matcher is applied to
|
|
80
|
+
# every attribute uniformly.
|
|
81
|
+
#
|
|
82
|
+
# class User
|
|
83
|
+
# include ActiveModel::Model
|
|
84
|
+
# attr_accessor :terms, :privacy_policy
|
|
85
|
+
#
|
|
86
|
+
# validates_acceptance_of :terms, :privacy_policy
|
|
87
|
+
# end
|
|
88
|
+
#
|
|
89
|
+
# # RSpec
|
|
90
|
+
# RSpec.describe User, type: :model do
|
|
91
|
+
# it { should validate_acceptance_of(:terms, :privacy_policy) }
|
|
92
|
+
# end
|
|
93
|
+
#
|
|
94
|
+
# # Minitest (Shoulda)
|
|
95
|
+
# class UserTest < ActiveSupport::TestCase
|
|
96
|
+
# should validate_acceptance_of(:terms, :privacy_policy)
|
|
97
|
+
# end
|
|
98
|
+
#
|
|
76
99
|
# @return [ValidateAcceptanceOfMatcher]
|
|
77
100
|
#
|
|
78
|
-
def validate_acceptance_of(
|
|
79
|
-
ValidateAcceptanceOfMatcher.new(attr)
|
|
101
|
+
def validate_acceptance_of(*attrs)
|
|
102
|
+
MatcherCollection.build(attrs) { |attr| ValidateAcceptanceOfMatcher.new(attr) }
|
|
80
103
|
end
|
|
81
104
|
|
|
82
105
|
# @private
|
|
@@ -272,10 +272,33 @@ module Shoulda
|
|
|
272
272
|
# should validate_comparison_of(:age).is_greater_than(0).allow_nil
|
|
273
273
|
# end
|
|
274
274
|
#
|
|
275
|
+
# #### Multiple attributes
|
|
276
|
+
#
|
|
277
|
+
# You can pass multiple attributes to assert that each one has the
|
|
278
|
+
# validation. Any qualifier chained on the matcher is applied to
|
|
279
|
+
# every attribute uniformly.
|
|
280
|
+
#
|
|
281
|
+
# class Item
|
|
282
|
+
# include ActiveModel::Model
|
|
283
|
+
# attr_accessor :width, :height
|
|
284
|
+
#
|
|
285
|
+
# validates_comparison_of :width, :height, greater_than: 0
|
|
286
|
+
# end
|
|
287
|
+
#
|
|
288
|
+
# # RSpec
|
|
289
|
+
# RSpec.describe Item, type: :model do
|
|
290
|
+
# it { should validate_comparison_of(:width, :height).is_greater_than(0) }
|
|
291
|
+
# end
|
|
292
|
+
#
|
|
293
|
+
# # Minitest (Shoulda)
|
|
294
|
+
# class ItemTest < ActiveSupport::TestCase
|
|
295
|
+
# should validate_comparison_of(:width, :height).is_greater_than(0)
|
|
296
|
+
# end
|
|
297
|
+
#
|
|
275
298
|
# @return [ValidateComparisonOfMatcher]
|
|
276
299
|
#
|
|
277
|
-
def validate_comparison_of(
|
|
278
|
-
ValidateComparisonOfMatcher.new(attr)
|
|
300
|
+
def validate_comparison_of(*attrs)
|
|
301
|
+
MatcherCollection.build(attrs) { |attr| ValidateComparisonOfMatcher.new(attr) }
|
|
279
302
|
end
|
|
280
303
|
|
|
281
304
|
# @private
|
|
@@ -378,6 +401,13 @@ module Shoulda
|
|
|
378
401
|
end
|
|
379
402
|
end
|
|
380
403
|
|
|
404
|
+
def failure_reason
|
|
405
|
+
raw_submatcher_failure_reason_for(
|
|
406
|
+
first_submatcher_that_fails_to_match,
|
|
407
|
+
:failure_message,
|
|
408
|
+
)
|
|
409
|
+
end
|
|
410
|
+
|
|
381
411
|
def given_numeric_column?
|
|
382
412
|
attribute_is_active_record_column? &&
|
|
383
413
|
[:integer, :float, :decimal].include?(column_type)
|
|
@@ -485,21 +515,26 @@ module Shoulda
|
|
|
485
515
|
submatcher,
|
|
486
516
|
failure_message_method
|
|
487
517
|
)
|
|
518
|
+
Shoulda::Matchers.word_wrap(
|
|
519
|
+
raw_submatcher_failure_reason_for(submatcher, failure_message_method),
|
|
520
|
+
indent: 2,
|
|
521
|
+
)
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
def raw_submatcher_failure_reason_for(submatcher, failure_message_method)
|
|
488
525
|
failure_message = submatcher.public_send(failure_message_method)
|
|
489
526
|
submatcher_description = submatcher.simple_description.
|
|
490
527
|
sub(/\bvalidate that\b/, 'validates').
|
|
491
528
|
sub(/\bdisallow\b/, 'disallows').
|
|
492
529
|
sub(/\ballow\b/, 'allows')
|
|
493
|
-
submatcher_message =
|
|
494
|
-
if number_of_submatchers_for_failure_message > 1
|
|
495
|
-
"In checking that #{model.name} #{submatcher_description}, " +
|
|
496
|
-
failure_message[0].downcase +
|
|
497
|
-
failure_message[1..]
|
|
498
|
-
else
|
|
499
|
-
failure_message
|
|
500
|
-
end
|
|
501
530
|
|
|
502
|
-
|
|
531
|
+
if number_of_submatchers_for_failure_message > 1
|
|
532
|
+
"In checking that #{model.name} #{submatcher_description}, " +
|
|
533
|
+
failure_message[0].downcase +
|
|
534
|
+
failure_message[1..]
|
|
535
|
+
else
|
|
536
|
+
failure_message
|
|
537
|
+
end
|
|
503
538
|
end
|
|
504
539
|
|
|
505
540
|
def comparison_descriptions
|
|
@@ -70,10 +70,33 @@ module Shoulda
|
|
|
70
70
|
# with_message('Please re-enter your password')
|
|
71
71
|
# end
|
|
72
72
|
#
|
|
73
|
+
# #### Multiple attributes
|
|
74
|
+
#
|
|
75
|
+
# You can pass multiple attributes to assert that each one has the
|
|
76
|
+
# validation. Any qualifier chained on the matcher is applied to
|
|
77
|
+
# every attribute uniformly.
|
|
78
|
+
#
|
|
79
|
+
# class User
|
|
80
|
+
# include ActiveModel::Model
|
|
81
|
+
# attr_accessor :password, :password_confirmation, :email, :email_confirmation
|
|
82
|
+
#
|
|
83
|
+
# validates_confirmation_of :password, :email
|
|
84
|
+
# end
|
|
85
|
+
#
|
|
86
|
+
# # RSpec
|
|
87
|
+
# RSpec.describe User, type: :model do
|
|
88
|
+
# it { should validate_confirmation_of(:password, :email) }
|
|
89
|
+
# end
|
|
90
|
+
#
|
|
91
|
+
# # Minitest (Shoulda)
|
|
92
|
+
# class UserTest < ActiveSupport::TestCase
|
|
93
|
+
# should validate_confirmation_of(:password, :email)
|
|
94
|
+
# end
|
|
95
|
+
#
|
|
73
96
|
# @return [ValidateConfirmationOfMatcher]
|
|
74
97
|
#
|
|
75
|
-
def validate_confirmation_of(
|
|
76
|
-
ValidateConfirmationOfMatcher.new(attr)
|
|
98
|
+
def validate_confirmation_of(*attrs)
|
|
99
|
+
MatcherCollection.build(attrs) { |attr| ValidateConfirmationOfMatcher.new(attr) }
|
|
77
100
|
end
|
|
78
101
|
|
|
79
102
|
# @private
|
|
@@ -153,7 +176,7 @@ module Shoulda
|
|
|
153
176
|
matcher.with_message(
|
|
154
177
|
@expected_message,
|
|
155
178
|
against: confirmation_attribute,
|
|
156
|
-
values: { attribute:
|
|
179
|
+
values: { attribute: },
|
|
157
180
|
)
|
|
158
181
|
end
|
|
159
182
|
end
|
|
@@ -112,10 +112,33 @@ module Shoulda
|
|
|
112
112
|
# with_message('You chose a puny weapon')
|
|
113
113
|
# end
|
|
114
114
|
#
|
|
115
|
+
# #### Multiple attributes
|
|
116
|
+
#
|
|
117
|
+
# You can pass multiple attributes to assert that each one has the
|
|
118
|
+
# validation. Any qualifier chained on the matcher is applied to
|
|
119
|
+
# every attribute uniformly.
|
|
120
|
+
#
|
|
121
|
+
# class Article
|
|
122
|
+
# include ActiveModel::Model
|
|
123
|
+
# attr_accessor :slug, :handle
|
|
124
|
+
#
|
|
125
|
+
# validates_exclusion_of :slug, :handle, in: %w[admin root]
|
|
126
|
+
# end
|
|
127
|
+
#
|
|
128
|
+
# # RSpec
|
|
129
|
+
# RSpec.describe Article, type: :model do
|
|
130
|
+
# it { should validate_exclusion_of(:slug, :handle).in_array(%w[admin root]) }
|
|
131
|
+
# end
|
|
132
|
+
#
|
|
133
|
+
# # Minitest (Shoulda)
|
|
134
|
+
# class ArticleTest < ActiveSupport::TestCase
|
|
135
|
+
# should validate_exclusion_of(:slug, :handle).in_array(%w[admin root])
|
|
136
|
+
# end
|
|
137
|
+
#
|
|
115
138
|
# @return [ValidateExclusionOfMatcher]
|
|
116
139
|
#
|
|
117
|
-
def validate_exclusion_of(
|
|
118
|
-
ValidateExclusionOfMatcher.new(attr)
|
|
140
|
+
def validate_exclusion_of(*attrs)
|
|
141
|
+
MatcherCollection.build(attrs) { |attr| ValidateExclusionOfMatcher.new(attr) }
|
|
119
142
|
end
|
|
120
143
|
|
|
121
144
|
# @private
|
|
@@ -263,10 +263,33 @@ module Shoulda
|
|
|
263
263
|
# allow_blank
|
|
264
264
|
# end
|
|
265
265
|
#
|
|
266
|
+
# #### Multiple attributes
|
|
267
|
+
#
|
|
268
|
+
# You can pass multiple attributes to assert that each one has the
|
|
269
|
+
# validation. Any qualifier chained on the matcher is applied to
|
|
270
|
+
# every attribute uniformly.
|
|
271
|
+
#
|
|
272
|
+
# class Article
|
|
273
|
+
# include ActiveModel::Model
|
|
274
|
+
# attr_accessor :status, :category
|
|
275
|
+
#
|
|
276
|
+
# validates_inclusion_of :status, :category, in: %w[draft published]
|
|
277
|
+
# end
|
|
278
|
+
#
|
|
279
|
+
# # RSpec
|
|
280
|
+
# RSpec.describe Article, type: :model do
|
|
281
|
+
# it { should validate_inclusion_of(:status, :category).in_array(%w[draft published]) }
|
|
282
|
+
# end
|
|
283
|
+
#
|
|
284
|
+
# # Minitest (Shoulda)
|
|
285
|
+
# class ArticleTest < ActiveSupport::TestCase
|
|
286
|
+
# should validate_inclusion_of(:status, :category).in_array(%w[draft published])
|
|
287
|
+
# end
|
|
288
|
+
#
|
|
266
289
|
# @return [ValidateInclusionOfMatcher]
|
|
267
290
|
#
|
|
268
|
-
def validate_inclusion_of(
|
|
269
|
-
ValidateInclusionOfMatcher.new(attr)
|
|
291
|
+
def validate_inclusion_of(*attrs)
|
|
292
|
+
MatcherCollection.build(attrs) { |attr| ValidateInclusionOfMatcher.new(attr) }
|
|
270
293
|
end
|
|
271
294
|
|
|
272
295
|
# @private
|
|
@@ -278,18 +301,6 @@ module Shoulda
|
|
|
278
301
|
ARBITRARY_OUTSIDE_DATE = Date.jd(9999999)
|
|
279
302
|
ARBITRARY_OUTSIDE_DATETIME = DateTime.jd(9999999)
|
|
280
303
|
ARBITRARY_OUTSIDE_TIME = Time.at(9999999999)
|
|
281
|
-
BOOLEAN_ALLOWS_BOOLEAN_MESSAGE = <<EOT.freeze
|
|
282
|
-
You are using `validate_inclusion_of` to assert that a boolean column allows
|
|
283
|
-
boolean values and disallows non-boolean ones. Be aware that it is not possible
|
|
284
|
-
to fully test this, as boolean columns will automatically convert non-boolean
|
|
285
|
-
values to boolean ones. Hence, you should consider removing this test.
|
|
286
|
-
EOT
|
|
287
|
-
BOOLEAN_ALLOWS_NIL_MESSAGE = <<EOT.freeze
|
|
288
|
-
You are using `validate_inclusion_of` to assert that a boolean column allows nil.
|
|
289
|
-
Be aware that it is not possible to fully test this, as anything other than
|
|
290
|
-
true, false or nil will be converted to false. Hence, you should consider
|
|
291
|
-
removing this test.
|
|
292
|
-
EOT
|
|
293
304
|
|
|
294
305
|
def initialize(attribute)
|
|
295
306
|
super(attribute)
|
|
@@ -400,6 +411,24 @@ EOT
|
|
|
400
411
|
|
|
401
412
|
private
|
|
402
413
|
|
|
414
|
+
def boolean_allows_boolean_message
|
|
415
|
+
<<-EOT.strip
|
|
416
|
+
You are using `validate_inclusion_of` to assert that the column '#{@subject.class.name}##{attribute}'
|
|
417
|
+
allows boolean values and disallows non-boolean ones. Be aware that it
|
|
418
|
+
is not possible to fully test this, as boolean columns will automatically
|
|
419
|
+
convert non-boolean values to boolean ones. Hence, you should consider
|
|
420
|
+
removing this test.
|
|
421
|
+
EOT
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def boolean_allows_nil_message
|
|
425
|
+
<<-EOT.strip
|
|
426
|
+
You are using `validate_inclusion_of` to assert that the column '#{@subject.class.name}##{attribute}'
|
|
427
|
+
allows nil. Be aware that it is not possible to fully test this, as anything other than
|
|
428
|
+
true, false or nil will be converted to false. Hence, you should consider removing this test.
|
|
429
|
+
EOT
|
|
430
|
+
end
|
|
431
|
+
|
|
403
432
|
def minimum_range_value
|
|
404
433
|
@range.begin
|
|
405
434
|
end
|
|
@@ -492,11 +521,11 @@ EOT
|
|
|
492
521
|
if attribute_type == :boolean
|
|
493
522
|
case @array
|
|
494
523
|
when [false, true], [true, false]
|
|
495
|
-
Shoulda::Matchers.warn
|
|
524
|
+
Shoulda::Matchers.warn boolean_allows_boolean_message
|
|
496
525
|
return true
|
|
497
526
|
when [nil]
|
|
498
527
|
if attribute_column.null
|
|
499
|
-
Shoulda::Matchers.warn
|
|
528
|
+
Shoulda::Matchers.warn boolean_allows_nil_message
|
|
500
529
|
return true
|
|
501
530
|
else
|
|
502
531
|
raise NonNullableBooleanError.create(@attribute)
|
|
@@ -513,11 +542,11 @@ EOT
|
|
|
513
542
|
if attribute_type == :boolean
|
|
514
543
|
case @array
|
|
515
544
|
when [false, true], [true, false]
|
|
516
|
-
Shoulda::Matchers.warn
|
|
545
|
+
Shoulda::Matchers.warn boolean_allows_boolean_message
|
|
517
546
|
return true
|
|
518
547
|
when [nil]
|
|
519
548
|
if attribute_column.null
|
|
520
|
-
Shoulda::Matchers.warn
|
|
549
|
+
Shoulda::Matchers.warn boolean_allows_nil_message
|
|
521
550
|
return true
|
|
522
551
|
else
|
|
523
552
|
raise NonNullableBooleanError.create(@attribute)
|
|
@@ -240,7 +240,6 @@ module Shoulda
|
|
|
240
240
|
# should validate_length_of(:bio).is_at_least(15).allow_nil
|
|
241
241
|
# end
|
|
242
242
|
#
|
|
243
|
-
# @return [ValidateLengthOfMatcher]
|
|
244
243
|
#
|
|
245
244
|
# ##### allow_blank
|
|
246
245
|
#
|
|
@@ -286,8 +285,32 @@ module Shoulda
|
|
|
286
285
|
# should validate_length_of(:arr).as_array.is_at_least(15)
|
|
287
286
|
# end
|
|
288
287
|
#
|
|
289
|
-
|
|
290
|
-
|
|
288
|
+
# #### Multiple attributes
|
|
289
|
+
#
|
|
290
|
+
# You can pass multiple attributes to assert that each one has the
|
|
291
|
+
# validation. Any qualifier chained on the matcher is applied to
|
|
292
|
+
# every attribute uniformly.
|
|
293
|
+
#
|
|
294
|
+
# class User
|
|
295
|
+
# include ActiveModel::Model
|
|
296
|
+
# attr_accessor :first_name, :last_name
|
|
297
|
+
#
|
|
298
|
+
# validates_length_of :first_name, :last_name, minimum: 2
|
|
299
|
+
# end
|
|
300
|
+
#
|
|
301
|
+
# # RSpec
|
|
302
|
+
# RSpec.describe User, type: :model do
|
|
303
|
+
# it { should validate_length_of(:first_name, :last_name).is_at_least(2) }
|
|
304
|
+
# end
|
|
305
|
+
#
|
|
306
|
+
# # Minitest (Shoulda)
|
|
307
|
+
# class UserTest < ActiveSupport::TestCase
|
|
308
|
+
# should validate_length_of(:first_name, :last_name).is_at_least(2)
|
|
309
|
+
# end
|
|
310
|
+
#
|
|
311
|
+
# @return [ValidateLengthOfMatcher]
|
|
312
|
+
def validate_length_of(*attrs)
|
|
313
|
+
MatcherCollection.build(attrs) { |attr| ValidateLengthOfMatcher.new(attr) }
|
|
291
314
|
end
|
|
292
315
|
|
|
293
316
|
# @private
|