shoulda-matchers 7.0.1 → 8.0.1
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 -4
- data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +1 -5
- data/lib/shoulda/matchers/active_model/allow_value_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 +25 -2
- 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 +4 -8
- data/lib/shoulda/matchers/active_model/validator.rb +4 -0
- 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 +27 -2
- data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +26 -2
- data/lib/shoulda/matchers/active_record/uniqueness/namespace.rb +4 -2
- data/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +32 -0
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +4 -14
- data/lib/shoulda/matchers/matcher_collection.rb +99 -0
- data/lib/shoulda/matchers/rails_shim.rb +4 -4
- data/lib/shoulda/matchers/version.rb +1 -1
- data/lib/shoulda/matchers.rb +1 -0
- data/shoulda-matchers.gemspec +4 -3
- metadata +8 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0cad2b70febc8044b3a8a61bc3ce7aa00b780708d3e0d2baccedaa3922d6f42f
|
|
4
|
+
data.tar.gz: 4a3fca99d9bccfac2bc74035f1e1a8608ef24775a19dd7bc30b0a979e6f87ed1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 29dcf4008451127b46e668f2399fe58d851ec54d2057595e59c7a57937b2f0e72d4a2aa4bdacd90f78d6a12a6de79f253675ab15ff4339c4222813bcaa449c04
|
|
7
|
+
data.tar.gz: 3f5b06b148848185c566f381f8cf3871dc56e454ea9ff59cb6106f1e969b854de53d07d84d6fa6d9554090440efcb568f30f33ac3dcfaada6fd4304fcbc284b5
|
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
|
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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
|
|
@@ -350,10 +350,33 @@ module Shoulda
|
|
|
350
350
|
# should validate_numericality_of(:age).allow_nil
|
|
351
351
|
# end
|
|
352
352
|
#
|
|
353
|
+
# #### Multiple attributes
|
|
354
|
+
#
|
|
355
|
+
# You can pass multiple attributes to assert that each one has the
|
|
356
|
+
# validation. Any qualifier chained on the matcher is applied to
|
|
357
|
+
# every attribute uniformly.
|
|
358
|
+
#
|
|
359
|
+
# class Item
|
|
360
|
+
# include ActiveModel::Model
|
|
361
|
+
# attr_accessor :price, :quantity
|
|
362
|
+
#
|
|
363
|
+
# validates_numericality_of :price, :quantity
|
|
364
|
+
# end
|
|
365
|
+
#
|
|
366
|
+
# # RSpec
|
|
367
|
+
# RSpec.describe Item, type: :model do
|
|
368
|
+
# it { should validate_numericality_of(:price, :quantity) }
|
|
369
|
+
# end
|
|
370
|
+
#
|
|
371
|
+
# # Minitest (Shoulda)
|
|
372
|
+
# class ItemTest < ActiveSupport::TestCase
|
|
373
|
+
# should validate_numericality_of(:price, :quantity)
|
|
374
|
+
# end
|
|
375
|
+
#
|
|
353
376
|
# @return [ValidateNumericalityOfMatcher]
|
|
354
377
|
#
|
|
355
|
-
def validate_numericality_of(
|
|
356
|
-
ValidateNumericalityOfMatcher.new(attr)
|
|
378
|
+
def validate_numericality_of(*attrs)
|
|
379
|
+
MatcherCollection.build(attrs) { |attr| ValidateNumericalityOfMatcher.new(attr) }
|
|
357
380
|
end
|
|
358
381
|
|
|
359
382
|
# @private
|
|
@@ -482,6 +505,13 @@ module Shoulda
|
|
|
482
505
|
end
|
|
483
506
|
end
|
|
484
507
|
|
|
508
|
+
def failure_reason
|
|
509
|
+
raw_submatcher_failure_reason_for(
|
|
510
|
+
first_submatcher_that_fails_to_match,
|
|
511
|
+
:failure_message,
|
|
512
|
+
)
|
|
513
|
+
end
|
|
514
|
+
|
|
485
515
|
def given_numeric_column?
|
|
486
516
|
attribute_is_active_record_column? &&
|
|
487
517
|
[:integer, :float, :decimal].include?(column_type)
|
|
@@ -620,21 +650,26 @@ module Shoulda
|
|
|
620
650
|
submatcher,
|
|
621
651
|
failure_message_method
|
|
622
652
|
)
|
|
653
|
+
Shoulda::Matchers.word_wrap(
|
|
654
|
+
raw_submatcher_failure_reason_for(submatcher, failure_message_method),
|
|
655
|
+
indent: 2,
|
|
656
|
+
)
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
def raw_submatcher_failure_reason_for(submatcher, failure_message_method)
|
|
623
660
|
failure_message = submatcher.public_send(failure_message_method)
|
|
624
661
|
submatcher_description = submatcher.simple_description.
|
|
625
662
|
sub(/\bvalidate that\b/, 'validates').
|
|
626
663
|
sub(/\bdisallow\b/, 'disallows').
|
|
627
664
|
sub(/\ballow\b/, 'allows')
|
|
628
|
-
submatcher_message =
|
|
629
|
-
if number_of_submatchers_for_failure_message > 1
|
|
630
|
-
"In checking that #{model.name} #{submatcher_description}, " +
|
|
631
|
-
failure_message[0].downcase +
|
|
632
|
-
failure_message[1..]
|
|
633
|
-
else
|
|
634
|
-
failure_message
|
|
635
|
-
end
|
|
636
665
|
|
|
637
|
-
|
|
666
|
+
if number_of_submatchers_for_failure_message > 1
|
|
667
|
+
"In checking that #{model.name} #{submatcher_description}, " +
|
|
668
|
+
failure_message[0].downcase +
|
|
669
|
+
failure_message[1..]
|
|
670
|
+
else
|
|
671
|
+
failure_message
|
|
672
|
+
end
|
|
638
673
|
end
|
|
639
674
|
|
|
640
675
|
def full_allowed_type
|
|
@@ -145,10 +145,34 @@ module Shoulda
|
|
|
145
145
|
# with_message('Robot has no legs')
|
|
146
146
|
# end
|
|
147
147
|
#
|
|
148
|
+
# #### Multiple attributes
|
|
149
|
+
#
|
|
150
|
+
# You can pass multiple attributes to assert that each one has the
|
|
151
|
+
# validation. Any qualifier chained on the matcher is applied to
|
|
152
|
+
# every attribute uniformly.
|
|
153
|
+
#
|
|
154
|
+
# class Robot
|
|
155
|
+
# include ActiveModel::Model
|
|
156
|
+
# attr_accessor :arms, :legs
|
|
157
|
+
#
|
|
158
|
+
# validates_presence_of :arms, :legs
|
|
159
|
+
# end
|
|
160
|
+
#
|
|
161
|
+
# # RSpec
|
|
162
|
+
# RSpec.describe Robot, type: :model do
|
|
163
|
+
# it { should validate_presence_of(:arms, :legs) }
|
|
164
|
+
# end
|
|
165
|
+
#
|
|
166
|
+
# # Minitest (Shoulda)
|
|
167
|
+
# class RobotTest < ActiveSupport::TestCase
|
|
168
|
+
# should validate_presence_of(:arms, :legs)
|
|
169
|
+
# end
|
|
170
|
+
#
|
|
148
171
|
# @return [ValidatePresenceOfMatcher]
|
|
149
172
|
#
|
|
150
|
-
|
|
151
|
-
|
|
173
|
+
|
|
174
|
+
def validate_presence_of(*attrs)
|
|
175
|
+
MatcherCollection.build(attrs) { |attr| ValidatePresenceOfMatcher.new(attr) }
|
|
152
176
|
end
|
|
153
177
|
|
|
154
178
|
# @private
|
|
@@ -206,27 +230,29 @@ module Shoulda
|
|
|
206
230
|
"validate that :#{@attribute} cannot be empty/falsy"
|
|
207
231
|
end
|
|
208
232
|
|
|
209
|
-
def
|
|
210
|
-
|
|
211
|
-
|
|
233
|
+
def failure_reason
|
|
234
|
+
reason = super
|
|
212
235
|
if should_add_footnote_about_belongs_to?
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
*This* presence validation doesn't use "can't be blank", the usual validation
|
|
217
|
-
message, but "must exist" instead.
|
|
218
|
-
|
|
219
|
-
With that said, did you know that the `belong_to` matcher can test this
|
|
220
|
-
validation for you? Instead of using `validate_presence_of`, try
|
|
221
|
-
#{suggestions_for_belongs_to}
|
|
222
|
-
MESSAGE
|
|
236
|
+
"#{reason}\n\n#{belongs_to_footnote}"
|
|
237
|
+
else
|
|
238
|
+
reason
|
|
223
239
|
end
|
|
224
|
-
|
|
225
|
-
message
|
|
226
240
|
end
|
|
227
241
|
|
|
228
242
|
private
|
|
229
243
|
|
|
244
|
+
def belongs_to_footnote
|
|
245
|
+
<<~MESSAGE.strip
|
|
246
|
+
You're getting this error because #{reason_for_existing_presence_validation}.
|
|
247
|
+
*This* presence validation doesn't use "can't be blank", the usual validation
|
|
248
|
+
message, but "must exist" instead.
|
|
249
|
+
|
|
250
|
+
With that said, did you know that the `belong_to` matcher can test this
|
|
251
|
+
validation for you? Instead of using `validate_presence_of`, try
|
|
252
|
+
#{suggestions_for_belongs_to}
|
|
253
|
+
MESSAGE
|
|
254
|
+
end
|
|
255
|
+
|
|
230
256
|
def secure_password_being_validated?
|
|
231
257
|
Shoulda::Matchers::RailsShim.digestible_attributes_in(@subject).
|
|
232
258
|
include?(@attribute)
|
|
@@ -86,6 +86,10 @@ module Shoulda
|
|
|
86
86
|
end
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
+
def failure_reason
|
|
90
|
+
last_submatcher_run.try(:failure_message)
|
|
91
|
+
end
|
|
92
|
+
|
|
89
93
|
protected
|
|
90
94
|
|
|
91
95
|
attr_reader :attribute, :context, :subject, :last_submatcher_run
|
|
@@ -150,14 +154,6 @@ module Shoulda
|
|
|
150
154
|
)
|
|
151
155
|
end
|
|
152
156
|
|
|
153
|
-
def failure_reason
|
|
154
|
-
last_submatcher_run.try(:failure_message)
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def failure_reason_when_negated
|
|
158
|
-
last_submatcher_run.try(:failure_message_when_negated)
|
|
159
|
-
end
|
|
160
|
-
|
|
161
157
|
def build_allow_or_disallow_value_matcher(args)
|
|
162
158
|
matcher_class = args.fetch(:matcher_class)
|
|
163
159
|
value = args.fetch(:value)
|