shoulda-matchers 5.3.0 → 6.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.
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