duck_typer 0.6.0 → 0.6.1

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: e7c4910b3357c23523997ed6cbb76cc1add4824af460fe8845390f1f69cfe2e6
4
- data.tar.gz: 4aea61000506666b3ce649e08398247c9ab72c75826cc9e4819e91471547f8dd
3
+ metadata.gz: 9d83b2db8bd9f4f9286edacaeecaf978aed251b15c0295d697a529c542f4e5c8
4
+ data.tar.gz: 176b5e8a8b555749b3e6f56f7ef36225e32db086ca54f9085b0a0db2ef5f09c6
5
5
  SHA512:
6
- metadata.gz: 48bcd0be970572a490e373064d3894bd174aaffc29b52a0c7b8beea030530b0661bde8221b01a0b681d3511478259cb7edfe858383def4a1e8f22f1afec36577
7
- data.tar.gz: 923d8722ae7ac8fb7f86aa12292056014fb00f5328c2034d63ed8b3c87984a0cce8890a0983d48fb2148b7d9ff1eb68888079cbaac90d4a7a7ebf33c8b5d6156
6
+ metadata.gz: aef37fad60ebeb09be165d46ff2fb0cdcb2e0ec330d4a773c71b412d265fe0e6ed3de2a2ec2daec5f0a19fbae8b32fb66541c29f6c1705e9e4528199fe12063a
7
+ data.tar.gz: d9f172fd026f220574582025803bdd887eead27d97d8b37a71f11cb6ee046508b1dc7e1efda88a1727ba60161a80fa47d0199f046a055263ee6fcd1cadc2cd7e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.6.1] - 2026-05-20
4
+
5
+ ### Added
6
+ - `assert_canonical_duck_type_match` Minitest alias for
7
+ `assert_canonical_interface_match`
8
+ - `implement_canonical_duck_type` RSpec alias for
9
+ `implement_canonical_interface`
10
+ - `canonical:` keyword support in the shared example
11
+ (`it_behaves_like "an interface"`)
12
+
3
13
  ## [0.6.0] - 2026-05-15
4
14
 
5
15
  ### Added
data/README.md CHANGED
@@ -240,6 +240,9 @@ assert_canonical_interface_match PaymentGateway, [StripeProcessor, PaypalProcess
240
240
  name: "PaymentProcessor"
241
241
  ```
242
242
 
243
+ > If you prefer duck typing terminology,
244
+ > `assert_canonical_duck_type_match` is available as an alias.
245
+
243
246
  ### RSpec
244
247
 
245
248
  Require the RSpec integration in your `spec_helper.rb`:
@@ -354,6 +357,9 @@ expect([StripeProcessor, PaypalProcessor])
354
357
  When multiple classes fail, the failure message reports all of them
355
358
  at once.
356
359
 
360
+ > If you prefer duck typing terminology,
361
+ > `implement_canonical_duck_type` is available as an alias.
362
+
357
363
  #### Shared example
358
364
 
359
365
  If you prefer shared examples, register one in `spec_helper.rb`
@@ -398,6 +404,25 @@ To check all classes in a module, pass it with `namespace:`:
398
404
  it_behaves_like "an interface", namespace: Payments
399
405
  ```
400
406
 
407
+ To check against a canonical reference class, pass
408
+ `canonical:`:
409
+
410
+ ```ruby
411
+ it_behaves_like "an interface", [
412
+ StripeProcessor,
413
+ PaypalProcessor,
414
+ BraintreeProcessor
415
+ ], canonical: PaymentGateway
416
+ ```
417
+
418
+ To combine `canonical:` with `namespace:`:
419
+
420
+ ```ruby
421
+ it_behaves_like "an interface",
422
+ canonical: PaymentGateway,
423
+ namespace: Payments
424
+ ```
425
+
401
426
  ## Limitations
402
427
 
403
428
  By default, Duck Typer checks the **structure** of public method
@@ -22,5 +22,8 @@ module DuckTyper
22
22
  result = checker.call
23
23
  assert result.match?, result.failure_message
24
24
  end
25
+
26
+ alias_method :assert_canonical_duck_type_match,
27
+ :assert_canonical_interface_match
25
28
  end
26
29
  end
@@ -19,7 +19,8 @@ RSpec::Matchers.define :have_matching_interfaces do |name: nil, type: :instance_
19
19
  end
20
20
  end
21
21
 
22
- RSpec::Matchers.alias_matcher :have_matching_duck_types, :have_matching_interfaces
22
+ RSpec::Matchers.alias_matcher :have_matching_duck_types,
23
+ :have_matching_interfaces
23
24
 
24
25
  RSpec::Matchers.define :implement_canonical_interface do |canonical, name: nil, type: :instance_methods, methods: nil, strict: false|
25
26
  match do |actual|
@@ -38,25 +39,41 @@ RSpec::Matchers.define :implement_canonical_interface do |canonical, name: nil,
38
39
  end
39
40
  end
40
41
 
42
+ RSpec::Matchers.alias_matcher :implement_canonical_duck_type,
43
+ :implement_canonical_interface
44
+
41
45
  module DuckTyper
42
46
  module RSpec
43
47
  def self.define_shared_example(name = "an interface")
44
- ::RSpec.shared_examples name do |*args, namespace: nil, name: nil, type: :instance_methods, methods: nil, strict: false|
48
+ ::RSpec.shared_examples name do |*args, canonical: nil, namespace: nil, name: nil, type: :instance_methods, methods: nil, strict: false|
45
49
  objects = namespace ? nil : args.first
46
50
 
47
- # We intentionally avoid reusing the have_matching_interfaces matcher
48
- # here. Since this shared example is defined in gem code, RSpec filters
49
- # it from its backtrace, causing the Failure/Error: line to show an
50
- # internal RSpec constant instead of useful context.
51
- it "has compatible interfaces" do
52
- checker = DuckTyper::BulkInterfaceChecker
53
- .new(objects, namespace:, type:, methods:, strict:, name:)
51
+ # We intentionally avoid reusing the matchers here. Since this shared
52
+ # example is defined in gem code, RSpec filters it from its backtrace,
53
+ # causing the Failure/Error: line to show an internal RSpec constant
54
+ # instead of useful context.
55
+ if canonical
56
+ it "implements the canonical interface" do
57
+ checker = DuckTyper::CanonicalInterfaceChecker
58
+ .new(objects, canonical:, namespace:, type:, methods:, strict:, name:)
59
+
60
+ result = checker.call
61
+
62
+ if !result.match?
63
+ raise ::RSpec::Expectations::ExpectationNotMetError, result.failure_message
64
+ end
65
+ end
66
+ else
67
+ it "has compatible interfaces" do
68
+ checker = DuckTyper::BulkInterfaceChecker
69
+ .new(objects, namespace:, type:, methods:, strict:, name:)
54
70
 
55
- failures = checker.call.reject(&:match?)
71
+ failures = checker.call.reject(&:match?)
56
72
 
57
- if failures.any?
58
- message = failures.map(&:failure_message).join("\n")
59
- raise ::RSpec::Expectations::ExpectationNotMetError, message
73
+ if failures.any?
74
+ message = failures.map(&:failure_message).join("\n")
75
+ raise ::RSpec::Expectations::ExpectationNotMetError, message
76
+ end
60
77
  end
61
78
  end
62
79
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DuckTyper
4
- VERSION = "0.6.0"
4
+ VERSION = "0.6.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duck_typer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thiago A. Silva
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-15 00:00:00.000000000 Z
11
+ date: 2026-05-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: