shoulda-matchers 4.5.1 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
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