shoulda-matchers 4.5.1 → 5.3.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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +15 -13
  4. data/lib/shoulda/matchers/action_controller/callback_matcher.rb +0 -87
  5. data/lib/shoulda/matchers/action_controller/flash_store.rb +2 -4
  6. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +3 -6
  7. data/lib/shoulda/matchers/active_model/helpers.rb +1 -1
  8. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +13 -34
  9. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +3 -5
  10. data/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +71 -0
  11. data/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +43 -0
  12. data/lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb +26 -0
  13. data/lib/shoulda/matchers/active_model/qualifiers.rb +1 -0
  14. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +14 -2
  15. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +2 -2
  16. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +6 -4
  17. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +1 -1
  18. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +47 -1
  19. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +31 -6
  20. data/lib/shoulda/matchers/active_model/validator.rb +1 -6
  21. data/lib/shoulda/matchers/active_model.rb +2 -1
  22. data/lib/shoulda/matchers/active_record/association_matcher.rb +20 -22
  23. data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +1 -1
  24. data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +1 -1
  25. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +125 -22
  26. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +21 -3
  27. data/lib/shoulda/matchers/rails_shim.rb +14 -39
  28. data/lib/shoulda/matchers/util/word_wrap.rb +2 -2
  29. data/lib/shoulda/matchers/version.rb +1 -1
  30. data/shoulda-matchers.gemspec +3 -3
  31. metadata +10 -8
  32. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +0 -161
@@ -78,6 +78,27 @@ module Shoulda
78
78
  # should validate_presence_of(:nickname).allow_nil
79
79
  # end
80
80
  #
81
+ # #### allow_blank
82
+ #
83
+ # Use `allow_blank` to assert that the attribute allows blank.
84
+ #
85
+ # class Robot
86
+ # include ActiveModel::Model
87
+ # attr_accessor :nickname
88
+ #
89
+ # validates_presence_of :nickname, allow_blank: true
90
+ # end
91
+ #
92
+ # # RSpec
93
+ # RSpec.describe Robot, type: :model do
94
+ # it { should validate_presence_of(:nickname).allow_blank }
95
+ # end
96
+ #
97
+ # # Minitest (Shoulda)
98
+ # class RobotTest < ActiveSupport::TestCase
99
+ # should validate_presence_of(:nickname).allow_blank
100
+ # end
101
+ #
81
102
  # ##### on
82
103
  #
83
104
  # Use `on` if your validation applies only under a certain context.
@@ -133,6 +154,7 @@ module Shoulda
133
154
  # @private
134
155
  class ValidatePresenceOfMatcher < ValidationMatcher
135
156
  include Qualifiers::AllowNil
157
+ include Qualifiers::AllowBlank
136
158
 
137
159
  def initialize(attribute)
138
160
  super
@@ -144,7 +166,8 @@ module Shoulda
144
166
 
145
167
  possibly_ignore_interference_by_writer
146
168
 
147
- if secure_password_being_validated?
169
+ if secure_password_being_validated? &&
170
+ Shoulda::Matchers::RailsShim.active_model_lt_7?
148
171
  ignore_interference_by_writer.default_to(when: :blank?)
149
172
 
150
173
  disallowed_values.all? do |value|
@@ -152,6 +175,7 @@ module Shoulda
152
175
  end
153
176
  else
154
177
  (!expects_to_allow_nil? || allows_value_of(nil)) &&
178
+ (!expects_to_allow_blank? || allows_value_of('')) &&
155
179
  disallowed_values.all? do |value|
156
180
  disallows_original_or_typecast_value?(value)
157
181
  end
@@ -171,6 +195,7 @@ module Shoulda
171
195
  end
172
196
  else
173
197
  (expects_to_allow_nil? && disallows_value_of(nil)) ||
198
+ (expects_to_allow_blank? && disallows_value_of('')) ||
174
199
  disallowed_values.any? do |value|
175
200
  allows_original_or_typecast_value?(value)
176
201
  end
@@ -208,7 +233,7 @@ validation for you? Instead of using `validate_presence_of`, try
208
233
  end
209
234
 
210
235
  def possibly_ignore_interference_by_writer
211
- if secure_password_being_validated?
236
+ if secure_password_being_validated? && RailsShim.active_model_lt_7?
212
237
  ignore_interference_by_writer.default_to(when: :blank?)
213
238
  end
214
239
  end
@@ -241,11 +266,11 @@ validation for you? Instead of using `validate_presence_of`, try
241
266
  else
242
267
  values = []
243
268
 
244
- if attribute_accepts_string_values?
269
+ if attribute_accepts_string_values? && !expects_to_allow_blank?
245
270
  values << ''
246
271
  end
247
272
 
248
- if !expects_to_allow_nil?
273
+ if !expects_to_allow_nil? && !expects_to_allow_blank?
249
274
  values << nil
250
275
  end
251
276
 
@@ -327,8 +352,8 @@ validation for you? Instead of using `validate_presence_of`, try
327
352
  end
328
353
 
329
354
  def collection_association?
330
- association? && association_reflection.macro.in?(
331
- [:has_many, :has_and_belongs_to_many],
355
+ association? && [:has_many, :has_and_belongs_to_many].include?(
356
+ association_reflection.macro,
332
357
  )
333
358
  end
334
359
 
@@ -98,12 +98,7 @@ module Shoulda
98
98
 
99
99
  all_validation_errors = record.errors.dup
100
100
 
101
- validation_error_messages =
102
- if record.errors.respond_to?(:[])
103
- record.errors[attribute]
104
- else
105
- record.errors.on(attribute)
106
- end
101
+ validation_error_messages = record.errors[attribute]
107
102
 
108
103
  {
109
104
  all_validation_errors: all_validation_errors,
@@ -26,7 +26,8 @@ require 'shoulda/matchers/active_model/numericality_matchers/comparison_matcher'
26
26
  require 'shoulda/matchers/active_model/numericality_matchers/odd_number_matcher'
27
27
  require 'shoulda/matchers/active_model/numericality_matchers/even_number_matcher'
28
28
  require 'shoulda/matchers/active_model/numericality_matchers/only_integer_matcher'
29
- require 'shoulda/matchers/active_model/allow_mass_assignment_of_matcher'
29
+ require 'shoulda/matchers/active_model/numericality_matchers/range_matcher'
30
+ require 'shoulda/matchers/active_model/numericality_matchers/submatchers'
30
31
  require 'shoulda/matchers/active_model/errors'
31
32
  require 'shoulda/matchers/active_model/have_secure_password_matcher'
32
33
 
@@ -1004,7 +1004,7 @@ module Shoulda
1004
1004
  @submatchers = []
1005
1005
  @missing = ''
1006
1006
 
1007
- if macro == :belongs_to && RailsShim.active_record_gte_5?
1007
+ if macro == :belongs_to
1008
1008
  required(belongs_to_required_by_default?)
1009
1009
  end
1010
1010
  end
@@ -1394,23 +1394,25 @@ module Shoulda
1394
1394
  end
1395
1395
 
1396
1396
  def class_has_foreign_key?(klass)
1397
+ @missing = validate_foreign_key(klass)
1398
+
1399
+ @missing.nil?
1400
+ end
1401
+
1402
+ def validate_foreign_key(klass)
1397
1403
  if options.key?(:foreign_key) && !foreign_key_correct?
1398
- @missing = foreign_key_failure_message(klass, options[:foreign_key])
1399
- false
1400
- elsif !has_column?(klass, foreign_key)
1401
- @missing = foreign_key_failure_message(klass, foreign_key)
1402
- false
1403
- else
1404
- true
1404
+ foreign_key_failure_message(klass, options[:foreign_key])
1405
+ elsif !has_column?(klass, actual_foreign_key)
1406
+ foreign_key_failure_message(klass, actual_foreign_key)
1405
1407
  end
1406
1408
  end
1407
1409
 
1408
1410
  def has_column?(klass, column)
1409
1411
  case column
1410
1412
  when Array
1411
- column.all? { |c| has_column?(klass, c) }
1413
+ column.all? { |c| has_column?(klass, c.to_s) }
1412
1414
  else
1413
- column_names_for(klass).include?(column)
1415
+ column_names_for(klass).include?(column.to_s)
1414
1416
  end
1415
1417
  end
1416
1418
 
@@ -1442,19 +1444,15 @@ module Shoulda
1442
1444
  end
1443
1445
  end
1444
1446
 
1445
- def foreign_key
1446
- key = if foreign_key_reflection
1447
- if foreign_key_reflection.respond_to?(:foreign_key)
1448
- foreign_key_reflection.foreign_key
1449
- else
1450
- foreign_key_reflection.primary_key_name
1451
- end
1452
- end
1453
-
1454
- if key.is_a?(Array)
1455
- key.map(&:to_s)
1447
+ def actual_foreign_key
1448
+ return unless foreign_key_reflection
1449
+
1450
+ if foreign_key_reflection.options[:foreign_key]
1451
+ foreign_key_reflection.options[:foreign_key]
1452
+ elsif foreign_key_reflection.respond_to?(:foreign_key)
1453
+ foreign_key_reflection.foreign_key
1456
1454
  else
1457
- key.to_s
1455
+ foreign_key_reflection.primary_key_name
1458
1456
  end
1459
1457
  end
1460
1458
 
@@ -42,7 +42,7 @@ module Shoulda
42
42
  end
43
43
 
44
44
  def join_table_exists?
45
- if RailsShim.tables_and_views(connection).
45
+ if connection.data_sources.
46
46
  include?(join_table_name.to_s)
47
47
  true
48
48
  else
@@ -65,7 +65,7 @@ module Shoulda
65
65
  end
66
66
 
67
67
  def validation_message_key
68
- RailsShim.validation_message_key_for_association_required_option
68
+ :required
69
69
  end
70
70
  end
71
71
  end
@@ -6,16 +6,22 @@ module Shoulda
6
6
  #
7
7
  # class Process < ActiveRecord::Base
8
8
  # enum status: [:running, :stopped, :suspended]
9
+ #
10
+ # alias_attribute :kind, :SomeLegacyField
11
+ #
12
+ # enum kind: [:foo, :bar]
9
13
  # end
10
14
  #
11
15
  # # RSpec
12
16
  # RSpec.describe Process, type: :model do
13
17
  # it { should define_enum_for(:status) }
18
+ # it { should define_enum_for(:kind) }
14
19
  # end
15
20
  #
16
21
  # # Minitest (Shoulda)
17
22
  # class ProcessTest < ActiveSupport::TestCase
18
23
  # should define_enum_for(:status)
24
+ # should define_enum_for(:kind)
19
25
  # end
20
26
  #
21
27
  # #### Qualifiers
@@ -110,7 +116,7 @@ module Shoulda
110
116
  ## ##### with_prefix
111
117
  #
112
118
  # Use `with_prefix` to test that the enum is defined with a `_prefix`
113
- # option (Rails 5 only). Can take either a boolean or a symbol:
119
+ # option (Rails 6+ only). Can take either a boolean or a symbol:
114
120
  #
115
121
  # class Issue < ActiveRecord::Base
116
122
  # enum status: [:open, :closed], _prefix: :old
@@ -157,6 +163,30 @@ module Shoulda
157
163
  # with_suffix
158
164
  # end
159
165
  #
166
+ # ##### without_scopes
167
+ #
168
+ # Use `without_scopes` to test that the enum is defined with
169
+ # '_scopes: false' option (Rails 5 only). Can take either a boolean or a
170
+ # symbol:
171
+ #
172
+ # class Issue < ActiveRecord::Base
173
+ # enum status: [:open, :closed], _scopes: false
174
+ # end
175
+ #
176
+ # # RSpec
177
+ # RSpec.describe Issue, type: :model do
178
+ # it do
179
+ # should define_enum_for(:status).
180
+ # without_scopes
181
+ # end
182
+ # end
183
+ #
184
+ # # Minitest (Shoulda)
185
+ # class ProcessTest < ActiveSupport::TestCase
186
+ # should define_enum_for(:status).
187
+ # without_scopes
188
+ # end
189
+ #
160
190
  # @return [DefineEnumForMatcher]
161
191
  #
162
192
  def define_enum_for(attribute_name)
@@ -167,7 +197,7 @@ module Shoulda
167
197
  class DefineEnumForMatcher
168
198
  def initialize(attribute_name)
169
199
  @attribute_name = attribute_name
170
- @options = { expected_enum_values: [] }
200
+ @options = { expected_enum_values: [], scopes: true }
171
201
  end
172
202
 
173
203
  def description
@@ -220,13 +250,19 @@ module Shoulda
220
250
  self
221
251
  end
222
252
 
253
+ def without_scopes
254
+ options[:scopes] = false
255
+ self
256
+ end
257
+
223
258
  def matches?(subject)
224
259
  @record = subject
225
260
 
226
261
  enum_defined? &&
227
262
  enum_values_match? &&
228
263
  column_type_matches? &&
229
- enum_value_methods_exist?
264
+ enum_value_methods_exist? &&
265
+ scope_presence_matches?
230
266
  end
231
267
 
232
268
  def failure_message
@@ -288,6 +324,10 @@ module Shoulda
288
324
  expectation << "_#{expected_suffix}".inspect
289
325
  end
290
326
 
327
+ if exclude_scopes?
328
+ expectation << ' with no scopes'
329
+ end
330
+
291
331
  expectation
292
332
  else
293
333
  simple_description
@@ -370,7 +410,10 @@ module Shoulda
370
410
  end
371
411
 
372
412
  def column
373
- model.columns_hash[attribute_name.to_s]
413
+ key = attribute_name.to_s
414
+ column_name = model.attribute_alias(key) || key
415
+
416
+ model.columns_hash[column_name]
374
417
  end
375
418
 
376
419
  def model
@@ -378,37 +421,81 @@ module Shoulda
378
421
  end
379
422
 
380
423
  def enum_value_methods_exist?
381
- passed = expected_singleton_methods.all? do |method|
382
- model.singleton_methods.include?(method)
424
+ if instance_methods_exist?
425
+ true
426
+ else
427
+ message = missing_methods_message
428
+
429
+ message << " (we can't tell which)"
430
+
431
+ @failure_message_continuation = message
432
+
433
+ false
383
434
  end
435
+ end
384
436
 
385
- if passed
437
+ def scope_presence_matches?
438
+ if exclude_scopes?
439
+ if singleton_methods_exist?
440
+ message = "#{attribute_name.inspect} does map to these values "
441
+ message << 'but class scope methods were present'
442
+
443
+ @failure_message_continuation = message
444
+
445
+ false
446
+ else
447
+ true
448
+ end
449
+ elsif singleton_methods_exist?
386
450
  true
387
451
  else
388
- message = "#{attribute_name.inspect} does map to these "
389
- message << 'values, but the enum is '
452
+ if enum_defined?
453
+ message = 'But the class scope methods are not present'
454
+ else
455
+ message = missing_methods_message
390
456
 
391
- if expected_prefix
392
- if expected_suffix
393
- message << 'configured with either a different prefix or '
394
- message << 'suffix, or no prefix or suffix at all'
395
- else
396
- message << 'configured with either a different prefix or no '
397
- message << 'prefix at all'
398
- end
399
- elsif expected_suffix
400
- message << 'configured with either a different suffix or no '
401
- message << 'suffix at all'
457
+ message << 'or the class scope methods are not present'
458
+ message << " (we can't tell which)"
402
459
  end
403
460
 
404
- message << " (we can't tell which)"
405
-
406
461
  @failure_message_continuation = message
407
462
 
408
463
  false
409
464
  end
410
465
  end
411
466
 
467
+ def missing_methods_message
468
+ message = "#{attribute_name.inspect} does map to these "
469
+ message << 'values, but the enum is '
470
+
471
+ if expected_prefix
472
+ if expected_suffix
473
+ message << 'configured with either a different prefix or '
474
+ message << 'suffix, or no prefix or suffix at all'
475
+ else
476
+ message << 'configured with either a different prefix or no '
477
+ message << 'prefix at all'
478
+ end
479
+ elsif expected_suffix
480
+ message << 'configured with either a different suffix or no '
481
+ message << 'suffix at all'
482
+ else
483
+ ''
484
+ end
485
+ end
486
+
487
+ def singleton_methods_exist?
488
+ expected_singleton_methods.all? do |method|
489
+ model.singleton_methods.include?(method)
490
+ end
491
+ end
492
+
493
+ def instance_methods_exist?
494
+ expected_instance_methods.all? do |method|
495
+ record.methods.include?(method)
496
+ end
497
+ end
498
+
412
499
  def expected_singleton_methods
413
500
  expected_enum_value_names.map do |name|
414
501
  [expected_prefix, name, expected_suffix].
@@ -418,6 +505,18 @@ module Shoulda
418
505
  end
419
506
  end
420
507
 
508
+ def expected_instance_methods
509
+ methods = expected_enum_value_names.map do |name|
510
+ [expected_prefix, name, expected_suffix].
511
+ select(&:present?).
512
+ join('_')
513
+ end
514
+
515
+ methods.flat_map do |m|
516
+ ["#{m}?".to_sym, "#{m}!".to_sym]
517
+ end
518
+ end
519
+
421
520
  def expected_prefix
422
521
  if options.include?(:prefix)
423
522
  if options[:prefix] == true
@@ -438,6 +537,10 @@ module Shoulda
438
537
  end
439
538
  end
440
539
 
540
+ def exclude_scopes?
541
+ !options[:scopes]
542
+ end
543
+
441
544
  def to_hash(value)
442
545
  if value.is_a?(Array)
443
546
  value.each_with_index.inject({}) do |hash, (item, index)|
@@ -52,7 +52,7 @@ module Shoulda
52
52
  #
53
53
  # Use `with_options` to assert that a column has been defined with
54
54
  # certain options (`:precision`, `:limit`, `:default`, `:null`, `:scale`,
55
- # or `:primary`).
55
+ # `:primary` or `:array`).
56
56
  #
57
57
  # class CreatePhones < ActiveRecord::Migration
58
58
  # def change
@@ -84,7 +84,7 @@ module Shoulda
84
84
 
85
85
  # @private
86
86
  class HaveDbColumnMatcher
87
- OPTIONS = %i(precision limit default null scale primary).freeze
87
+ OPTIONS = %i(precision limit default null scale primary array).freeze
88
88
 
89
89
  def initialize(column)
90
90
  @column = column
@@ -115,7 +115,8 @@ module Shoulda
115
115
  correct_default? &&
116
116
  correct_null? &&
117
117
  correct_scale? &&
118
- correct_primary?
118
+ correct_primary? &&
119
+ correct_array?
119
120
  end
120
121
 
121
122
  def failure_message
@@ -258,6 +259,23 @@ module Shoulda
258
259
  end
259
260
  end
260
261
 
262
+ def correct_array?
263
+ return true unless @options.key?(:array)
264
+
265
+ if matched_column.array? == @options[:array]
266
+ true
267
+ else
268
+ @missing = "#{model_class} has a db column named #{@column} "
269
+ @missing <<
270
+ if @options[:primary]
271
+ 'that is not array, but should be'
272
+ else
273
+ 'that is array, but should not be'
274
+ end
275
+ false
276
+ end
277
+ end
278
+
261
279
  def matched_column
262
280
  @_matched_column ||= begin
263
281
  column = model_class.columns.detect do |each|
@@ -3,24 +3,12 @@ module Shoulda
3
3
  # @private
4
4
  module RailsShim # rubocop:disable Metrics/ModuleLength
5
5
  class << self
6
- def action_pack_gte_5?
7
- Gem::Requirement.new('>= 5').satisfied_by?(action_pack_version)
8
- end
9
-
10
- def action_pack_lt_5?
11
- Gem::Requirement.new('< 5').satisfied_by?(action_pack_version)
12
- end
13
-
14
6
  def action_pack_version
15
7
  Gem::Version.new(::ActionPack::VERSION::STRING)
16
8
  rescue NameError
17
9
  Gem::Version.new('0')
18
10
  end
19
11
 
20
- def active_record_gte_5?
21
- Gem::Requirement.new('>= 5').satisfied_by?(active_record_version)
22
- end
23
-
24
12
  def active_record_gte_6?
25
13
  Gem::Requirement.new('>= 6').satisfied_by?(active_record_version)
26
14
  end
@@ -31,6 +19,20 @@ module Shoulda
31
19
  Gem::Version.new('0')
32
20
  end
33
21
 
22
+ def active_model_version
23
+ Gem::Version.new(::ActiveModel::VERSION::STRING)
24
+ rescue NameError
25
+ Gem::Version.new('0')
26
+ end
27
+
28
+ def active_model_st_6_1?
29
+ Gem::Requirement.new('< 6.1').satisfied_by?(active_model_version)
30
+ end
31
+
32
+ def active_model_lt_7?
33
+ Gem::Requirement.new('< 7').satisfied_by?(active_model_version)
34
+ end
35
+
34
36
  def generate_validation_message(
35
37
  record,
36
38
  attribute,
@@ -57,17 +59,6 @@ module Shoulda
57
59
  )
58
60
  end
59
61
 
60
- def make_controller_request(context, verb, action, request_params)
61
- params =
62
- if action_pack_gte_5?
63
- { params: request_params }
64
- else
65
- request_params
66
- end
67
-
68
- context.__send__(verb, action, params)
69
- end
70
-
71
62
  def serialized_attributes_for(model)
72
63
  attribute_types_for(model).
73
64
  inject({}) do |hash, (attribute_name, attribute_type)|
@@ -85,26 +76,10 @@ module Shoulda
85
76
  serialized_attributes_for(model)[attribute_name.to_s]
86
77
  end
87
78
 
88
- def tables_and_views(connection)
89
- if active_record_gte_5?
90
- connection.data_sources
91
- else
92
- connection.tables
93
- end
94
- end
95
-
96
79
  def verb_for_update
97
80
  :patch
98
81
  end
99
82
 
100
- def validation_message_key_for_association_required_option
101
- if active_record_gte_5?
102
- :required
103
- else
104
- :blank
105
- end
106
- end
107
-
108
83
  def parent_of(mod)
109
84
  if mod.respond_to?(:module_parent)
110
85
  mod.module_parent
@@ -92,7 +92,7 @@ module Shoulda
92
92
  if line.list_item?
93
93
  combined_lines << line
94
94
  else
95
- combined_lines.last << (" #{line}").squeeze(' ')
95
+ combined_lines.last << " #{line}".squeeze(' ')
96
96
  end
97
97
 
98
98
  combined_lines
@@ -185,7 +185,7 @@ module Shoulda
185
185
  leftover = ''
186
186
  else
187
187
  fitted_line = line[0..index].rstrip
188
- leftover = line[index + 1..-1]
188
+ leftover = line[index + 1..]
189
189
  end
190
190
 
191
191
  { fitted_line: fitted_line, leftover: leftover }
@@ -1,6 +1,6 @@
1
1
  module Shoulda
2
2
  module Matchers
3
3
  # @private
4
- VERSION = '4.5.1'.freeze
4
+ VERSION = '5.3.0'.freeze
5
5
  end
6
6
  end
@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
26
26
 
27
27
  s.metadata = {
28
28
  'bug_tracker_uri' => 'https://github.com/thoughtbot/shoulda-matchers/issues',
29
- 'changelog_uri' => 'https://github.com/thoughtbot/shoulda-matchers/blob/master/CHANGELOG.md',
29
+ 'changelog_uri' => 'https://github.com/thoughtbot/shoulda-matchers/blob/main/CHANGELOG.md',
30
30
  'documentation_uri' => 'https://matchers.shoulda.io/docs',
31
31
  'homepage_uri' => 'https://matchers.shoulda.io',
32
32
  'source_code_uri' => 'https://github.com/thoughtbot/shoulda-matchers',
@@ -36,6 +36,6 @@ Gem::Specification.new do |s|
36
36
  'shoulda-matchers.gemspec']
37
37
  s.require_paths = ['lib']
38
38
 
39
- s.required_ruby_version = '>= 2.4.0'
40
- s.add_dependency('activesupport', '>= 4.2.0')
39
+ s.required_ruby_version = '>= 2.6.0'
40
+ s.add_dependency('activesupport', '>= 5.2.0')
41
41
  end