shoulda-matchers 5.3.0 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +22 -7
  4. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +1 -1
  5. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +9 -0
  6. data/lib/shoulda/matchers/active_model/comparison_matcher.rb +162 -0
  7. data/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +1 -1
  8. data/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +21 -6
  9. data/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +534 -0
  10. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +3 -3
  11. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +4 -3
  12. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +64 -9
  13. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +32 -86
  14. data/lib/shoulda/matchers/active_model/validation_matcher.rb +6 -0
  15. data/lib/shoulda/matchers/active_model/validator.rb +4 -0
  16. data/lib/shoulda/matchers/active_model.rb +2 -1
  17. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +0 -8
  18. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +46 -6
  19. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +14 -3
  20. data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +3 -5
  21. data/lib/shoulda/matchers/active_record/normalize_matcher.rb +151 -0
  22. data/lib/shoulda/matchers/active_record.rb +1 -0
  23. data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +1 -1
  24. data/lib/shoulda/matchers/rails_shim.rb +8 -6
  25. data/lib/shoulda/matchers/util/word_wrap.rb +1 -1
  26. data/lib/shoulda/matchers/util.rb +1 -1
  27. data/lib/shoulda/matchers/version.rb +1 -1
  28. data/lib/shoulda/matchers.rb +2 -2
  29. data/shoulda-matchers.gemspec +1 -1
  30. metadata +10 -8
  31. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +0 -136
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d083d22f49f99bf60d6c6bdfed7ea08ea1f838a90b4aed439d84108ebc814af9
4
- data.tar.gz: 90cc8440d8c63da1c338306b3d9f129cc9af8dc1a80277d9e7f5b233f2bbe115
3
+ metadata.gz: '00039d1c47436bb88b431784a2b956713382face294b6bbe5c7599378ce03a55'
4
+ data.tar.gz: 27640030d1ada19757e5da4d9c10465f422234f8dde9242a54bcb002585c71d5
5
5
  SHA512:
6
- metadata.gz: '09b1dcf7e19179b50992d34cf73a86e72727cf27a1db911f2adb98fe291dc4d3d711da962d3efdbec60d2b1f280dcecd441e725a7e6a701a152ff60fdd8e340c'
7
- data.tar.gz: 8ba54626246e133cfd86b84aba3d3b05487166d6359b2ba97256af242a057b5ff6cd40d5eae35bd35f619c9a9b0f39cc2c8b6508e299b44e2605e3cdf4f64078
6
+ metadata.gz: 51aff6ac4ab386f8df5dec81dfca4f197604d206263b29b711146820dda54ef5f9a9aa723aa6ce14084364eccdcbfbb8c3b1c38f2a4cdcbfc5d3ae8e8c965806
7
+ data.tar.gz: 476b055e38257cc8d6841a96779a012ef68cd01239c26c15b170c251a4f7655bab6344a7c163361e9ef283142994bc04ed69f8c6bbccf1bae3acfafc5fd62176
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2006-2022 Tammer Saleh and thoughtbot, inc.
1
+ Copyright (c) Tammer Saleh and thoughtbot, inc.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person
4
4
  obtaining a copy of this software and associated documentation
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [version-badge]: https://img.shields.io/gem/v/shoulda-matchers.svg
4
4
  [rubygems]: https://rubygems.org/gems/shoulda-matchers
5
- [github-actions-badge]: https://img.shields.io/github/workflow/status/thoughtbot/shoulda-matchers/Test
5
+ [github-actions-badge]: https://img.shields.io/github/actions/workflow/status/thoughtbot/shoulda-matchers/ci.yml?branch=main
6
6
  [github-actions]: https://github.com/thoughtbot/shoulda-matchers/actions
7
7
  [downloads-total]: https://img.shields.io/gem/dt/shoulda-matchers.svg
8
8
  [downloads-badge]: https://img.shields.io/gem/dtv/shoulda-matchers.svg
@@ -374,6 +374,8 @@ about any of them, make sure to [consult the documentation][rubydocs]!
374
374
  tests usage of `validates_numericality_of`.
375
375
  * **[validate_presence_of](lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb)**
376
376
  tests usage of `validates_presence_of`.
377
+ * **[validate_comparison_of](lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb)**
378
+ tests usage of `validates_comparison_of`.
377
379
 
378
380
  ### ActiveRecord matchers
379
381
 
@@ -407,6 +409,8 @@ about any of them, make sure to [consult the documentation][rubydocs]!
407
409
  usage of the `serialize` macro.
408
410
  * **[validate_uniqueness_of](lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb)**
409
411
  tests usage of `validates_uniqueness_of`.
412
+ * **[normalize](lib/shoulda/matchers/active_record/normalize_matcher.rb)** tests
413
+ usage of the `normalize` macro
410
414
 
411
415
  ### ActionController matchers
412
416
 
@@ -468,8 +472,8 @@ machine, understanding the codebase, and creating a good pull request.
468
472
 
469
473
  ## Compatibility
470
474
 
471
- Shoulda Matchers is tested and supported against Ruby 2.6+, Rails
472
- 5.2+, RSpec 3.x, and Minitest 5.x.
475
+ Shoulda Matchers is tested and supported against Ruby 3.0+, Rails
476
+ 6.1+, RSpec 3.x, and Minitest 5.x.
473
477
 
474
478
  - For Ruby < 2.4 and Rails < 4.1 compatibility, please use [v3.1.3][v3.1.3].
475
479
  - For Ruby < 3.0 and Rails < 6.1 compatibility, please use [v4.5.1][v4.5.1].
@@ -484,15 +488,26 @@ Shoulda Matchers follows Semantic Versioning 2.0 as defined at
484
488
 
485
489
  ## Team
486
490
 
487
- Shoulda Matchers is maintained by [Elliot Winkler][mcmire] and [Gui
488
- Albuk][guialbuk].
491
+ Shoulda Matchers is currently maintained by [Pedro Paiva][VSPPedro] and [Matheus
492
+ Sales][matsales28]. Previous maintainers include [Elliot Winkler][mcmire],
493
+ [Gui Albuk][guialbuk], [Jason Draper][drapergeek], [Melissa Xie][mxie],
494
+ [Gabe Berke-Williams][gabebw], [Ryan McGeary][rmm5t], [Joe Ferris][jferris], and
495
+ [Tammer Saleh][tammersaleh].
489
496
 
497
+ [VSPPedro]: https://github.com/VSPPedro
498
+ [matsales28]: https://github.com/matsales28
490
499
  [mcmire]: https://github.com/mcmire
491
500
  [guialbuk]: https://github.com/guialbuk
501
+ [drapergeek]: https://github.com/drapergeek
502
+ [mxie]: https://github.com/mxie
503
+ [gabebw]: https://github.com/gabebw
504
+ [rmm5t]: https://github.com/rmm5t
505
+ [jferris]: https://github.com/jferris
506
+ [tammersaleh]: https://github.com/tammersaleh
492
507
 
493
508
  ## Copyright/License
494
509
 
495
- Shoulda Matchers is copyright © 2006-2022 Tammer Saleh and [thoughtbot,
510
+ Shoulda Matchers is copyright © Tammer Saleh and [thoughtbot,
496
511
  inc][thoughtbot-website]. It is free and opensource software and may be
497
512
  redistributed under the terms specified in the [LICENSE](LICENSE) file.
498
513
 
@@ -502,7 +517,7 @@ redistributed under the terms specified in the [LICENSE](LICENSE) file.
502
517
 
503
518
  ![thoughtbot][thoughtbot-logo]
504
519
 
505
- [thoughtbot-logo]: https://presskit.thoughtbot.com/images/thoughtbot-logo-for-readmes.svg
520
+ [thoughtbot-logo]: https://thoughtbot.com/brand_assets/93:44.svg
506
521
 
507
522
  The names and logos for thoughtbot are trademarks of thoughtbot, inc.
508
523
 
@@ -11,7 +11,7 @@ module Shoulda
11
11
  module Matchers
12
12
  module ActionController
13
13
  # The `permit` matcher tests that an action in your controller receives a
14
- # whitelist of parameters using Rails' Strong Parameters feature
14
+ # allowlist of parameters using Rails' Strong Parameters feature
15
15
  # (specifically that `permit` was called with the correct arguments).
16
16
  #
17
17
  # Here's an example:
@@ -402,6 +402,10 @@ module Shoulda
402
402
  @result.nil?
403
403
  end
404
404
 
405
+ def has_any_errors?
406
+ validator.record.errors.any?
407
+ end
408
+
405
409
  def failure_message
406
410
  attribute_setter = result.attribute_setter
407
411
 
@@ -480,6 +484,11 @@ module Shoulda
480
484
  message << " it produced these validation errors instead:\n\n"
481
485
  message << validator.all_formatted_validation_error_messages
482
486
  end
487
+ elsif validator.has_any_errors?
488
+ message << ", placing a validation error on :#{attribute_setter.attribute_name}"
489
+ message << '. The Example was invalid,'
490
+ message << " but it had errors involving other attributes:\n\n"
491
+ message << validator.all_formatted_validation_error_messages
483
492
  else
484
493
  message << ', but it was valid instead.'
485
494
  end
@@ -0,0 +1,162 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+
3
+ module Shoulda
4
+ module Matchers
5
+ module ActiveModel
6
+ # @private
7
+ class ComparisonMatcher < ValidationMatcher
8
+ ERROR_MESSAGES = {
9
+ :> => {
10
+ label: :greater_than,
11
+ assertions: [false, false, true],
12
+ },
13
+ :>= => {
14
+ label: :greater_than_or_equal_to,
15
+ assertions: [false, true, true],
16
+ },
17
+ :< => {
18
+ label: :less_than,
19
+ assertions: [true, false, false],
20
+ },
21
+ :<= => {
22
+ label: :less_than_or_equal_to,
23
+ assertions: [true, true, false],
24
+ },
25
+ :== => {
26
+ label: :equal_to,
27
+ assertions: [false, true, false],
28
+ },
29
+ :!= => {
30
+ label: :other_than,
31
+ assertions: [true, false, true],
32
+ },
33
+ }.freeze
34
+
35
+ delegate :failure_message, :failure_message_when_negated, to: :comparison_submatchers
36
+
37
+ def initialize(matcher, value, operator)
38
+ super(nil)
39
+ unless matcher.respond_to? :diff_to_compare
40
+ raise ArgumentError, 'matcher is invalid'
41
+ end
42
+
43
+ @matcher = matcher
44
+ @value = value
45
+ @operator = operator
46
+ @message = ERROR_MESSAGES[operator][:label]
47
+ end
48
+
49
+ def simple_description
50
+ description = ''
51
+
52
+ if expects_strict?
53
+ description << ' strictly'
54
+ end
55
+
56
+ description +
57
+ "disallow :#{attribute} from being a number that is not " +
58
+ "#{comparison_expectation} #{@value}"
59
+ end
60
+
61
+ def for(attribute)
62
+ @attribute = attribute
63
+ self
64
+ end
65
+
66
+ def with_message(message)
67
+ @expects_custom_validation_message = true
68
+ @message = message
69
+ self
70
+ end
71
+
72
+ def expects_custom_validation_message?
73
+ @expects_custom_validation_message
74
+ end
75
+
76
+ def matches?(subject)
77
+ @subject = subject
78
+ comparison_submatchers.matches?(subject)
79
+ end
80
+
81
+ def does_not_match?(subject)
82
+ @subject = subject
83
+ comparison_submatchers.does_not_match?(subject)
84
+ end
85
+
86
+ def comparison_description
87
+ "#{comparison_expectation} #{@value}"
88
+ end
89
+
90
+ def comparison_submatchers
91
+ @_comparison_submatchers ||=
92
+ NumericalityMatchers::Submatchers.new(build_comparison_submatchers)
93
+ end
94
+
95
+ private
96
+
97
+ def build_comparison_submatchers
98
+ comparison_combos.map do |diff, submatcher_method_name|
99
+ matcher = __send__(submatcher_method_name, diff, nil)
100
+ matcher.with_message(@message, values: { count: option_value })
101
+ matcher
102
+ end
103
+ end
104
+
105
+ def comparison_combos
106
+ diffs_to_compare.zip(submatcher_method_names)
107
+ end
108
+
109
+ def submatcher_method_names
110
+ assertions.map do |value|
111
+ if value
112
+ :allow_value_matcher
113
+ else
114
+ :disallow_value_matcher
115
+ end
116
+ end
117
+ end
118
+
119
+ def assertions
120
+ ERROR_MESSAGES[@operator][:assertions]
121
+ end
122
+
123
+ def option_value
124
+ if defined?(@_option_value)
125
+ @_option_value
126
+ else
127
+ @_option_value =
128
+ case @value
129
+ when Proc then @value.call(@subject)
130
+ when Symbol then @subject.send(@value)
131
+ else @value
132
+ end
133
+ end
134
+ end
135
+
136
+ def diffs_to_compare
137
+ diff_to_compare = @matcher.diff_to_compare
138
+ values = case option_value
139
+ when String then diffs_when_string(diff_to_compare)
140
+ else [-1, 0, 1].map { |sign| option_value + (diff_to_compare * sign) }
141
+ end
142
+
143
+ if @matcher.given_numeric_column?
144
+ values
145
+ else
146
+ values.map(&:to_s)
147
+ end
148
+ end
149
+
150
+ def diffs_when_string(diff_to_compare)
151
+ [-1, 0, 1].map do |sign|
152
+ option_value[0..-2] + (option_value[-1].ord + diff_to_compare * sign).chr
153
+ end
154
+ end
155
+
156
+ def comparison_expectation
157
+ ERROR_MESSAGES[@operator][:label].to_s.tr('_', ' ')
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -59,7 +59,7 @@ module Shoulda
59
59
  end
60
60
 
61
61
  def build_comparison_submatcher(value, operator)
62
- NumericalityMatchers::ComparisonMatcher.new(@numericality_matcher, value, operator).
62
+ ComparisonMatcher.new(@numericality_matcher, value, operator).
63
63
  for(@attribute).
64
64
  with_message(@message).
65
65
  on(@context)
@@ -13,22 +13,23 @@ module Shoulda
13
13
  failing_submatchers.empty?
14
14
  end
15
15
 
16
+ def does_not_match?(subject)
17
+ @subject = subject
18
+ non_failing_submatchers.empty?
19
+ end
20
+
16
21
  def failure_message
17
- last_failing_submatcher.failure_message
22
+ failing_submatcher.failure_message
18
23
  end
19
24
 
20
25
  def failure_message_when_negated
21
- last_failing_submatcher.failure_message_when_negated
26
+ non_failing_submatcher.failure_message_when_negated
22
27
  end
23
28
 
24
29
  def add(submatcher)
25
30
  @submatchers << submatcher
26
31
  end
27
32
 
28
- def last_failing_submatcher
29
- failing_submatchers.last
30
- end
31
-
32
33
  private
33
34
 
34
35
  def failing_submatchers
@@ -36,6 +37,20 @@ module Shoulda
36
37
  submatcher.matches?(@subject)
37
38
  end
38
39
  end
40
+
41
+ def non_failing_submatchers
42
+ @_non_failing_submatchers ||= @submatchers.reject do |submatcher|
43
+ submatcher.does_not_match?(@subject)
44
+ end
45
+ end
46
+
47
+ def failing_submatcher
48
+ failing_submatchers.last
49
+ end
50
+
51
+ def non_failing_submatcher
52
+ non_failing_submatchers.last
53
+ end
39
54
  end
40
55
  end
41
56
  end