shoulda-matchers 5.1.0 → 5.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 188abbfbc6cac3e26997733b3d88b34ea74e9747e25f137738190bf15d033751
4
- data.tar.gz: 73136b35573cffac784cadd7e3193e8e9a0934f0e69fa751f536ed589db01024
3
+ metadata.gz: 97aebe50b9fb389687c99bc5dc1229a27abc5255f15e2330c9d1409d957fb689
4
+ data.tar.gz: 7cd8151aae87feb29121f4b4c152f963e5cc1c7a136e0a2115344af225fb1dcd
5
5
  SHA512:
6
- metadata.gz: 55f3ccad9ce3d4affcb6ccc38020872bc000710f887765d7b78962bcdd339d79a2a1316e3a9d2cb33c62887e66bab9e54801d79c872f6cab7a2459a32ddce4bf
7
- data.tar.gz: e5a862e62f25dfe284f0325f20600bbc4a3567ef78a0b7742685d1a9af9d03147ab650a259be287bd4566719444c7d998c0106874b7c3ac43efbd79996191838
6
+ metadata.gz: 351c2bc9565abf151db5eeef3d17280989cdc8ec1841b0a099de8be8533767324494b04bfcc4464290852f5b022f6595fb5759e79c4534f50b6e464c94e44679
7
+ data.tar.gz: 529b6d0815299c10c0f52664371e37b948ec4b8403c0096065f11649fb1849851454634f736e747820f06e27fc4d48a0616d9adfbc34b560f14a8dad364e4268
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2006-2021 Tammer Saleh and thoughtbot, inc.
1
+ Copyright (c) 2006-2022 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
@@ -19,7 +19,7 @@ complex, and error-prone.
19
19
 
20
20
  ## Quick links
21
21
 
22
- 📖 **[Read the documentation for the latest version][rubydocs].**
22
+ 📖 **[Read the documentation for the latest version][rubydocs].**
23
23
  📢 **[See what's changed in recent versions][changelog].**
24
24
 
25
25
  [rubydocs]: https://matchers.shoulda.io/docs
@@ -117,7 +117,7 @@ Otherwise, add `shoulda-matchers` to your Gemfile:
117
117
 
118
118
  ```ruby
119
119
  group :test do
120
- gem 'shoulda-matchers', '~> 4.0'
120
+ gem 'shoulda-matchers', '~> 5.0'
121
121
  end
122
122
  ```
123
123
 
@@ -492,7 +492,7 @@ Albuk][guialbuk].
492
492
 
493
493
  ## Copyright/License
494
494
 
495
- Shoulda Matchers is copyright © 2006-2021 Tammer Saleh and [thoughtbot,
495
+ Shoulda Matchers is copyright © 2006-2022 Tammer Saleh and [thoughtbot,
496
496
  inc][thoughtbot-website]. It is free and opensource software and may be
497
497
  redistributed under the terms specified in the [LICENSE](LICENSE) file.
498
498
 
@@ -0,0 +1,26 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ module Qualifiers
5
+ # @private
6
+ module AllowBlank
7
+ def initialize(*args)
8
+ super
9
+ @expects_to_allow_blank = false
10
+ end
11
+
12
+ def allow_blank
13
+ @expects_to_allow_blank = true
14
+ self
15
+ end
16
+
17
+ protected
18
+
19
+ def expects_to_allow_blank?
20
+ @expects_to_allow_blank
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -9,5 +9,6 @@ module Shoulda
9
9
  end
10
10
 
11
11
  require_relative 'qualifiers/allow_nil'
12
+ require_relative 'qualifiers/allow_blank'
12
13
  require_relative 'qualifiers/ignore_interference_by_writer'
13
14
  require_relative 'qualifiers/ignoring_interference_by_writer'
@@ -239,7 +239,7 @@ module Shoulda
239
239
  #
240
240
  # @return [ValidateLengthOfMatcher]
241
241
  #
242
- # # ##### allow_blank
242
+ # ##### allow_blank
243
243
  #
244
244
  # Use `allow_blank` to assert that the attribute allows blank.
245
245
  #
@@ -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
 
@@ -116,7 +116,7 @@ module Shoulda
116
116
  ## ##### with_prefix
117
117
  #
118
118
  # Use `with_prefix` to test that the enum is defined with a `_prefix`
119
- # 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:
120
120
  #
121
121
  # class Issue < ActiveRecord::Base
122
122
  # enum status: [:open, :closed], _prefix: :old
@@ -163,6 +163,30 @@ module Shoulda
163
163
  # with_suffix
164
164
  # end
165
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
+ #
166
190
  # @return [DefineEnumForMatcher]
167
191
  #
168
192
  def define_enum_for(attribute_name)
@@ -173,7 +197,7 @@ module Shoulda
173
197
  class DefineEnumForMatcher
174
198
  def initialize(attribute_name)
175
199
  @attribute_name = attribute_name
176
- @options = { expected_enum_values: [] }
200
+ @options = { expected_enum_values: [], scopes: true }
177
201
  end
178
202
 
179
203
  def description
@@ -226,13 +250,19 @@ module Shoulda
226
250
  self
227
251
  end
228
252
 
253
+ def without_scopes
254
+ options[:scopes] = false
255
+ self
256
+ end
257
+
229
258
  def matches?(subject)
230
259
  @record = subject
231
260
 
232
261
  enum_defined? &&
233
262
  enum_values_match? &&
234
263
  column_type_matches? &&
235
- enum_value_methods_exist?
264
+ enum_value_methods_exist? &&
265
+ scope_presence_matches?
236
266
  end
237
267
 
238
268
  def failure_message
@@ -294,6 +324,10 @@ module Shoulda
294
324
  expectation << "_#{expected_suffix}".inspect
295
325
  end
296
326
 
327
+ if exclude_scopes?
328
+ expectation << ' with no scopes'
329
+ end
330
+
297
331
  expectation
298
332
  else
299
333
  simple_description
@@ -387,37 +421,81 @@ module Shoulda
387
421
  end
388
422
 
389
423
  def enum_value_methods_exist?
390
- passed = expected_singleton_methods.all? do |method|
391
- 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
392
434
  end
435
+ end
393
436
 
394
- 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?
395
450
  true
396
451
  else
397
- message = "#{attribute_name.inspect} does map to these "
398
- 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
399
456
 
400
- if expected_prefix
401
- if expected_suffix
402
- message << 'configured with either a different prefix or '
403
- message << 'suffix, or no prefix or suffix at all'
404
- else
405
- message << 'configured with either a different prefix or no '
406
- message << 'prefix at all'
407
- end
408
- elsif expected_suffix
409
- message << 'configured with either a different suffix or no '
410
- message << 'suffix at all'
457
+ message << 'or the class scope methods are not present'
458
+ message << " (we can't tell which)"
411
459
  end
412
460
 
413
- message << " (we can't tell which)"
414
-
415
461
  @failure_message_continuation = message
416
462
 
417
463
  false
418
464
  end
419
465
  end
420
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
+
421
499
  def expected_singleton_methods
422
500
  expected_enum_value_names.map do |name|
423
501
  [expected_prefix, name, expected_suffix].
@@ -427,6 +505,18 @@ module Shoulda
427
505
  end
428
506
  end
429
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
+
430
520
  def expected_prefix
431
521
  if options.include?(:prefix)
432
522
  if options[:prefix] == true
@@ -447,6 +537,10 @@ module Shoulda
447
537
  end
448
538
  end
449
539
 
540
+ def exclude_scopes?
541
+ !options[:scopes]
542
+ end
543
+
450
544
  def to_hash(value)
451
545
  if value.is_a?(Array)
452
546
  value.each_with_index.inject({}) do |hash, (item, index)|
@@ -19,6 +19,20 @@ module Shoulda
19
19
  Gem::Version.new('0')
20
20
  end
21
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_gte_7?
29
+ Gem::Requirement.new('>= 7').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
+
22
36
  def generate_validation_message(
23
37
  record,
24
38
  attribute,
@@ -1,6 +1,6 @@
1
1
  module Shoulda
2
2
  module Matchers
3
3
  # @private
4
- VERSION = '5.1.0'.freeze
4
+ VERSION = '5.2.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',
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shoulda-matchers
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tammer Saleh
@@ -14,7 +14,7 @@ authors:
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
- date: 2021-12-22 00:00:00.000000000 Z
17
+ date: 2022-09-17 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: activesupport
@@ -80,6 +80,7 @@ files:
80
80
  - lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb
81
81
  - lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb
82
82
  - lib/shoulda/matchers/active_model/qualifiers.rb
83
+ - lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb
83
84
  - lib/shoulda/matchers/active_model/qualifiers/allow_nil.rb
84
85
  - lib/shoulda/matchers/active_model/qualifiers/ignore_interference_by_writer.rb
85
86
  - lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb
@@ -173,7 +174,7 @@ licenses:
173
174
  - MIT
174
175
  metadata:
175
176
  bug_tracker_uri: https://github.com/thoughtbot/shoulda-matchers/issues
176
- changelog_uri: https://github.com/thoughtbot/shoulda-matchers/blob/master/CHANGELOG.md
177
+ changelog_uri: https://github.com/thoughtbot/shoulda-matchers/blob/main/CHANGELOG.md
177
178
  documentation_uri: https://matchers.shoulda.io/docs
178
179
  homepage_uri: https://matchers.shoulda.io
179
180
  source_code_uri: https://github.com/thoughtbot/shoulda-matchers