shoulda-matchers 6.2.0 → 6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d549ac8f3629ad37bc56d0d09daf37416ec23d78374a8ed6a630ea23e5c9e487
4
- data.tar.gz: f09bc94b6be181564cdde56729c2055192a4725adee4ba8b6b7f8e84429fe9a0
3
+ metadata.gz: 87a298f618f08949dbe1c165b17dfbfc940a6675b2f790e655c9a43568956c31
4
+ data.tar.gz: ea5eaad001d24f89a698a4a944f2596da029c019a4bcc2f893c0881146dea058
5
5
  SHA512:
6
- metadata.gz: 1f3ba08a6d15ad56cb583c0a8289e180854f5d89020b3182b2d6140289800d71bca467f187c9a4718ba26078cc2562f6377d77b00a0e84f2e683079d62ef60d3
7
- data.tar.gz: 45d0141c79d9642439c9c03ffcd8e41d1aee993eafaea11fc72812456ead1dcf17d1698e9eb7f311c40945c54c77fc4e573a820ffa115fc344e9b0049ed69479
6
+ metadata.gz: bcaf6fc8e3e59cf1936c2a346d1a02c1269ab8e231b7820c273873ed81cf6f97501bf15ea44503f357008064f34eaec66bc9a81f2938fd3876078d707979daea
7
+ data.tar.gz: 94a53d9576ea0df451d2ff0ff34528f8c035699e9d216e877ced2cdff156ae0a5d18bc106dc0b5fc487aebfd56807bd836d2cb9aa51ababfc232be5e3b94c2cf
data/README.md CHANGED
@@ -527,7 +527,6 @@ The names and logos for thoughtbot are trademarks of thoughtbot, inc.
527
527
 
528
528
  We love open source software!
529
529
  See [our other projects][community].
530
-
531
530
  We are [available for hire][hire].
532
531
 
533
532
  [community]: https://thoughtbot.com/community?utm_source=github
@@ -133,7 +133,7 @@ module Shoulda
133
133
  when :missing then 404
134
134
  when :error then 500..599
135
135
  when Symbol
136
- ::Rack::Utils::SYMBOL_TO_STATUS_CODE[potential_symbol]
136
+ ::Rack::Utils.status_code(potential_symbol)
137
137
  else
138
138
  potential_symbol
139
139
  end
@@ -187,8 +187,95 @@ module Shoulda
187
187
  # without_scopes
188
188
  # end
189
189
  #
190
- # @return [DefineEnumForMatcher]
190
+ # ##### with_default
191
+ #
192
+ # Use `with_default` to test that the enum is defined with a
193
+ # default value. A proc can also be passed, and will be called once each
194
+ # time a new value is needed. (If using Time or Date, it's recommended to
195
+ # freeze time or date to avoid flaky tests):
196
+ #
197
+ # class Issue < ActiveRecord::Base
198
+ # enum status: [:open, :closed], default: :closed
199
+ # end
200
+ #
201
+ # # RSpec
202
+ # RSpec.describe Issue, type: :model do
203
+ # it do
204
+ # should define_enum_for(:status).
205
+ # with_default(:closed)
206
+ # end
207
+ # end
208
+ #
209
+ # # Minitest (Shoulda)
210
+ # class ProcessTest < ActiveSupport::TestCase
211
+ # should define_enum_for(:status).
212
+ # with_default(:closed)
213
+ # end
191
214
  #
215
+ # ##### validating
216
+ #
217
+ # Use `validating` to test that the enum is being validated.
218
+ # Can take a boolean value and an allowing_nil keyword argument:
219
+ #
220
+ # class Issue < ActiveRecord::Base
221
+ # enum status: [:open, :closed], validate: true
222
+ # end
223
+ #
224
+ # # RSpec
225
+ # RSpec.describe Issue, type: :model do
226
+ # it do
227
+ # should define_enum_for(:status).
228
+ # validating
229
+ # end
230
+ # end
231
+ #
232
+ # # Minitest (Shoulda)
233
+ # class ProcessTest < ActiveSupport::TestCase
234
+ # should define_enum_for(:status).
235
+ # validating
236
+ # end
237
+ #
238
+ # class Issue < ActiveRecord::Base
239
+ # enum status: [:open, :closed], validate: { allow_nil: true }
240
+ # end
241
+ #
242
+ # # RSpec
243
+ # RSpec.describe Issue, type: :model do
244
+ # it do
245
+ # should define_enum_for(:status).
246
+ # validating(allowing_nil: true)
247
+ # end
248
+ # end
249
+ #
250
+ # # Minitest (Shoulda)
251
+ # class ProcessTest < ActiveSupport::TestCase
252
+ # should define_enum_for(:status).
253
+ # validating(allowing_nil: true)
254
+ # end
255
+ #
256
+ # ##### without_instance_methods
257
+ #
258
+ # Use `without_instance_methods` to exclude the check for instance methods.
259
+ #
260
+ # class Issue < ActiveRecord::Base
261
+ # enum status: [:open, :closed], instance_methods: false
262
+ # end
263
+ #
264
+ # # RSpec
265
+ # RSpec.describe Issue, type: :model do
266
+ # it do
267
+ # should define_enum_for(:status).
268
+ # without_instance_methods
269
+ # end
270
+ # end
271
+ #
272
+ # # Minitest (Shoulda)
273
+ # class ProcessTest < ActiveSupport::TestCase
274
+ # should define_enum_for(:status).
275
+ # without_instance_methods
276
+ # end
277
+ #
278
+ # @return [DefineEnumForMatcher]
192
279
  def define_enum_for(attribute_name)
193
280
  DefineEnumForMatcher.new(attribute_name)
194
281
  end
@@ -197,7 +284,7 @@ module Shoulda
197
284
  class DefineEnumForMatcher
198
285
  def initialize(attribute_name)
199
286
  @attribute_name = attribute_name
200
- @options = { expected_enum_values: [], scopes: true }
287
+ @options = { expected_enum_values: [], scopes: true, instance_methods: true }
201
288
  end
202
289
 
203
290
  def description
@@ -222,6 +309,12 @@ module Shoulda
222
309
  description
223
310
  end
224
311
 
312
+ def validating(value = true, allowing_nil: false)
313
+ options[:validating] = value
314
+ options[:allowing_nil] = allowing_nil
315
+ self
316
+ end
317
+
225
318
  def with_values(expected_enum_values)
226
319
  options[:expected_enum_values] = expected_enum_values
227
320
  self
@@ -247,6 +340,16 @@ module Shoulda
247
340
  self
248
341
  end
249
342
 
343
+ def without_instance_methods
344
+ options[:instance_methods] = false
345
+ self
346
+ end
347
+
348
+ def with_default(default_value)
349
+ options[:default] = default_value
350
+ self
351
+ end
352
+
250
353
  def matches?(subject)
251
354
  @record = subject
252
355
 
@@ -254,7 +357,9 @@ module Shoulda
254
357
  enum_values_match? &&
255
358
  column_type_matches? &&
256
359
  enum_value_methods_exist? &&
257
- scope_presence_matches?
360
+ scope_presence_matches? &&
361
+ default_value_matches? &&
362
+ validating_matches?
258
363
  end
259
364
 
260
365
  def failure_message
@@ -277,6 +382,30 @@ module Shoulda
277
382
 
278
383
  private
279
384
 
385
+ def validating_matches?
386
+ return true if options[:validating].nil?
387
+
388
+ validator = find_enum_validator
389
+
390
+ if expected_validating? == !!validator
391
+ if validator&.options&.dig(:allow_nil).present? == expected_allowing_nil?
392
+ true
393
+ else
394
+ @failure_message_continuation =
395
+ "However, #{attribute_name.inspect} is allowing nil values"
396
+ false
397
+ end
398
+ else
399
+ @failure_message_continuation =
400
+ if expected_validating?
401
+ "However, #{attribute_name.inspect} is not being validated"
402
+ else
403
+ "However, #{attribute_name.inspect} is being validated"
404
+ end
405
+ false
406
+ end
407
+ end
408
+
280
409
  attr_reader :attribute_name, :options, :record,
281
410
  :failure_message_continuation
282
411
 
@@ -292,6 +421,21 @@ module Shoulda
292
421
  )
293
422
  end
294
423
 
424
+ if options[:default].present?
425
+ expectation << ', with a default value of '
426
+ expectation << Shoulda::Matchers::Util.inspect_value(expected_default_value)
427
+ end
428
+
429
+ if expected_validating?
430
+ expectation << ', and being validated '
431
+ expectation <<
432
+ if expected_allowing_nil?
433
+ 'allowing nil values'
434
+ else
435
+ 'not allowing nil values'
436
+ end
437
+ end
438
+
295
439
  if expected_prefix
296
440
  expectation <<
297
441
  if expected_suffix
@@ -413,16 +557,24 @@ module Shoulda
413
557
  end
414
558
 
415
559
  def enum_value_methods_exist?
416
- if instance_methods_exist?
417
- true
418
- else
560
+ if options[:instance_methods]
561
+ return true if instance_methods_exist?
562
+
419
563
  message = missing_methods_message
564
+ message << " (we can't tell which)" if [expected_prefix, expected_suffix].any?
565
+
566
+ @failure_message_continuation = message
420
567
 
421
- message << " (we can't tell which)"
568
+ false
569
+ elsif instance_methods_exist?
570
+ message = "#{attribute_name.inspect} does map to these values"
571
+ message << ' with instance methods, but expected no instance methods'
422
572
 
423
573
  @failure_message_continuation = message
424
574
 
425
575
  false
576
+ else
577
+ true
426
578
  end
427
579
  end
428
580
 
@@ -471,11 +623,50 @@ module Shoulda
471
623
  elsif expected_suffix
472
624
  message << 'configured with either a different suffix or no '
473
625
  message << 'suffix at all'
626
+ elsif expected_instance_methods?
627
+ message << 'configured with no instance methods'
474
628
  else
475
629
  ''
476
630
  end
477
631
  end
478
632
 
633
+ def default_value_matches?
634
+ return true if options[:default].blank?
635
+
636
+ if actual_default_value.nil?
637
+ @failure_message_continuation = 'However, no default value was set'
638
+ return false
639
+ end
640
+
641
+ if actual_default_value == expected_default_value
642
+ true
643
+ else
644
+ String.new.tap do |message|
645
+ message << 'However, the default value is '
646
+ message << Shoulda::Matchers::Util.inspect_value(actual_default_value)
647
+ @failure_message_continuation = message
648
+ end
649
+ false
650
+ end
651
+ end
652
+
653
+ def expected_default_value
654
+ options[:default].respond_to?(:call) ? options[:default].call : options[:default]
655
+ end
656
+
657
+ def actual_default_value
658
+ attribute_schema = model.attributes_to_define_after_schema_loads[attribute_name.to_s]
659
+
660
+ value = case attribute_schema
661
+ in [_, { default: default_value } ]
662
+ default_value
663
+ in [_, default_value]
664
+ default_value
665
+ end
666
+
667
+ value.respond_to?(:call) ? value.call : value
668
+ end
669
+
479
670
  def singleton_methods_exist?
480
671
  expected_singleton_methods.all? do |method|
481
672
  model.singleton_methods.include?(method)
@@ -509,6 +700,10 @@ module Shoulda
509
700
  end
510
701
  end
511
702
 
703
+ def expected_instance_methods?
704
+ options[:instance_methods]
705
+ end
706
+
512
707
  def expected_prefix
513
708
  if options.include?(:prefix)
514
709
  if options[:prefix] == true
@@ -529,6 +724,22 @@ module Shoulda
529
724
  end
530
725
  end
531
726
 
727
+ def expected_validating?
728
+ options[:validating].present?
729
+ end
730
+
731
+ def expected_allowing_nil?
732
+ options[:allowing_nil].present?
733
+ end
734
+
735
+ def find_enum_validator
736
+ record.class.validators.detect do |validator|
737
+ validator.kind == :inclusion &&
738
+ validator.attributes.include?(attribute_name.to_s) &&
739
+ validator.options[:in] == expected_enum_values
740
+ end
741
+ end
742
+
532
743
  def exclude_scopes?
533
744
  !options[:scopes]
534
745
  end
@@ -29,7 +29,19 @@ module Shoulda
29
29
  end
30
30
 
31
31
  def symlink_to(parent)
32
- namespace.set(name, Class.new(parent))
32
+ table_name = parent.table_name
33
+
34
+ new_class = Class.new(parent) do
35
+ define_singleton_method :table_name do
36
+ table_name
37
+ end
38
+
39
+ define_singleton_method :base_class do
40
+ self
41
+ end
42
+ end
43
+
44
+ namespace.set(name, new_class)
33
45
  end
34
46
 
35
47
  def to_s
@@ -1,5 +1,4 @@
1
1
  require 'forwardable'
2
- require 'logger'
3
2
 
4
3
  module Shoulda
5
4
  module Matchers
@@ -1,6 +1,6 @@
1
1
  module Shoulda
2
2
  module Matchers
3
3
  # @private
4
- VERSION = '6.2.0'.freeze
4
+ VERSION = '6.3.0'.freeze
5
5
  end
6
6
  end
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: 6.2.0
4
+ version: 6.3.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: 2024-03-15 00:00:00.000000000 Z
17
+ date: 2024-08-09 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: activesupport
@@ -198,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
198
  - !ruby/object:Gem::Version
199
199
  version: '0'
200
200
  requirements: []
201
- rubygems_version: 3.5.6
201
+ rubygems_version: 3.5.3
202
202
  signing_key:
203
203
  specification_version: 4
204
204
  summary: Simple one-liner tests for common Rails functionality