callable-mixin 0.1.2 → 0.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: f02bb980d5c43522ab33033a546c9b9418bb02cec29dcc1ef7254413c9beaf7f
4
- data.tar.gz: 796a78f8e6f06b9518742cde7b3fd6101921bad24b8201a0bcd26e0befff1a3d
3
+ metadata.gz: 3c4ab883eaeb5a2633c55642e2ad8aef2c97fc836d8eeb9a037e55d7e93bca84
4
+ data.tar.gz: b5b8203b5653d12f6d40730a734dd29febf78fd8e6db065cc651dced0b6ec248
5
5
  SHA512:
6
- metadata.gz: 7be17d2dc903fe4b37bb4b5a8003296e36c6dfde4cefecfaf5ec5305ae02800b14088bc90443b74fb78796e49191def264b67b7b0aee9c00c0651f62e37673ce
7
- data.tar.gz: 7d6b34a18d7bf1bf4ce34d919d8f0fbc3803bcc1a94cccb78d96f27db63e0a4f3667174d3fa53879aaf899c2705f2c85928e8f4354cdc1b4df5b440b5409bac2
6
+ metadata.gz: 2726c5f636c2ab6502db98a40afce7d237ae811d7a1b3c177c745b3926441ba2dd918b7a9dcb56800271e91b2ea624ade06de9e4da5233a07cdab20092eed547
7
+ data.tar.gz: c46915a2fb9687b707aaea865bd4c002ae197a0ed2f84355a9a924bf380240d8b8a26170c5175d8a338337e25cee66db60f7a87c1fefe1da3c1c1c90546b1a50
data/README.md CHANGED
@@ -1,21 +1,28 @@
1
+ [![Gem Version](https://badge.fury.io/rb/callable-mixin.svg)](https://badge.fury.io/rb/callable-mixin)
2
+ [![Build Status](https://github.com/dbongo/callable-mixin/actions/workflows/ci.yml/badge.svg)](https://github.com/dbongo/callable-mixin/actions)
3
+
1
4
  # Callable
2
5
 
3
- Callable is a minimal Ruby mix-in that provides your classes with a simple `.call` class method. It allows you to instantiate and immediately invoke an instance method (`call`) without boilerplate or additional dependencies. Perfect for clean, readable, and concise service objects.
6
+ Callable is a minimal Ruby mix-in that provides your classes with a simple `.call` class method (plus `to_proc`). It allows you to instantiate and immediately invoke an instance method (`call`) without boilerplate or additional dependencies. Perfect for clean, readable, and concise service objects.
7
+
8
+ Learn more at https://rubydoc.info/gems/callable-mixin
4
9
 
5
10
  ## Features
6
11
 
7
12
  - Provides a `.call` class method to any Ruby class.
8
- - Transparently forwards positional arguments, keyword arguments, and blocks.
9
- - Handles argument errors gracefully, raising clear exceptions.
13
+ - Supports `Class#to_proc`, letting you use `&YourService` in `Enumerable` methods.
14
+ - Transparently forwards positional args, keyword args, and blocks to `#call`.
15
+ - Raises `ConstructionError` (subclass of `ArgumentError`) for constructor arity/kwarg mismatches.
16
+ - Raises `NotImplementedError` when the class does not define an instance `#call`.
10
17
  - Zero external runtime dependencies.
11
18
  - Compatible with MRI Ruby 2.3 through Ruby 3.x.
12
19
 
13
20
  ## Installation
14
21
 
15
- Add this to your application's Gemfile:
22
+ Add this to your application's Gemfile (version 0.2.0 or later):
16
23
 
17
24
  ```ruby
18
- gem 'callable-mixin'
25
+ gem 'callable-mixin', '~> 0.2.0'
19
26
  ```
20
27
 
21
28
  Then execute:
@@ -32,9 +39,7 @@ gem install callable-mixin
32
39
 
33
40
  ## Usage
34
41
 
35
- ### In a Bundler-based app (Rails, Hanami, etc.)
36
-
37
- Simply include `Callable` in your class and implement an instance method named `call`:
42
+ ### Example Service with Multiple Arguments
38
43
 
39
44
  ```ruby
40
45
  class SendNotification
@@ -51,36 +56,66 @@ class SendNotification
51
56
  end
52
57
  ```
53
58
 
54
- ### In a plain Ruby script
59
+ - **Explicit invocation** when you need multiple constructor args:
55
60
 
56
- Require `'callable'`, include `Callable` in your class and implement an instance method named `call`:
61
+ ```ruby
62
+ users.each do |user|
63
+ SendNotification.call(user, "Your message here")
64
+ end
57
65
 
58
- ```ruby
59
- require 'callable'
66
+ # You can also call via the `.()` alias:
67
+ SendNotification.(user, "Your message here")
68
+ ```
60
69
 
61
- class SendNotification
70
+ ### Example Service with Single Argument (to_proc Shorthand)
71
+
72
+ ```ruby
73
+ class WelcomeUser
62
74
  include Callable
63
75
 
64
- def initialize(user, message)
76
+ def initialize(user)
65
77
  @user = user
66
- @message = message
67
78
  end
68
79
 
69
80
  def call
70
- NotificationMailer.notify(@user, @message).deliver_now
81
+ NotificationMailer.welcome(@user).deliver_now
71
82
  end
72
83
  end
73
84
  ```
74
85
 
75
- Then run your class using the `.call` class method:
86
+ - **Proc shorthand** for one-arg services:
87
+
88
+ ```ruby
89
+ users.each(&WelcomeUser)
90
+
91
+ # Or invoke with the `.()` alias:
92
+ WelcomeUser.(current_user)
93
+ ```
94
+
95
+ ### Using in a Plain Ruby Script
76
96
 
77
97
  ```ruby
78
- SendNotification.call(current_user, "Hello from Callable!")
98
+ require 'callable-mixin'
79
99
 
80
- # or use .() (syntactic sugar for .call)
81
- SendNotification.(current_user, "Hello from Callable!")
82
- ```
100
+ # Define your service with Callable
101
+ class MyService
102
+ include Callable
103
+
104
+ def initialize(value)
105
+ @value = value
106
+ end
83
107
 
108
+ def call
109
+ puts "Processing #{@value}"
110
+ end
111
+ end
112
+
113
+ # Invoke the service
114
+ MyService.call("some data")
115
+
116
+ # Or use the `.()` alias:
117
+ MyService.("more data")
118
+ ```
84
119
 
85
120
  ## Development
86
121
 
@@ -1,7 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ ##
4
+ # Callable
5
+ #
6
+ # A lightweight mix‑in that gives any class a convenient class‑level `.call` helper.
7
+ #
8
+ # class SendEmail
9
+ # include Callable
10
+ #
11
+ # def initialize(user)
12
+ # @user = user
13
+ # end
14
+ #
15
+ # def call
16
+ # Mailer.welcome(@user).deliver_now
17
+ # end
18
+ # end
19
+ #
20
+ # SendEmail.call(User.first) # => delivers email
21
+ # users.each(&SendEmail) # => thanks to #to_proc
22
+ #
23
+ # Compatible with Ruby 2.3+ and works the same on 3.x.
3
24
  module Callable
4
- # Raised when .new fails due to arity / keyword mismatch
25
+ # Raised when `.call` cannot build the instance (arity/keyword mismatch).
5
26
  class ConstructionError < ArgumentError; end
6
27
 
7
28
  def self.included(base)
@@ -10,28 +31,32 @@ module Callable
10
31
 
11
32
  module ClassMethods
12
33
  ##
13
- # Instantiate and immediately invoke #call
14
- # @param args [Array] positional arguments for initialize
15
- # @param kwargs [Hash] keyword arguments for initialize
16
- # @yield [*] optional configuration/result block
17
- # @return [Object] whatever the instance #call returns
18
- # @raise [ConstructionError] when instantiation fails
34
+ # Instantiate the class and immediately invoke its instance `#call`.
35
+ #
36
+ # @param args [Array] positional arguments for `initialize`.
37
+ # @param kwargs [Hash] keyword arguments for `initialize`.
38
+ # @yield [optional] block passed directly to the instance method `#call`; ignored if not yielded.
39
+ # @return anything returned by the instance `#call`.
40
+ # @raise [ConstructionError] if construction fails (plain `ArgumentError`).
41
+ # @raise [NotImplementedError] if the instance does not implement `#call`.
19
42
  def call(*args, **kwargs, &block)
20
- inst = begin
21
- # avoids Ruby 2.3–2.6 quirk
22
- kwargs.empty? ? new(*args, &block) : new(*args, **kwargs, &block)
23
- rescue ArgumentError => error
24
- raise if error.class != ArgumentError
25
- raise ConstructionError,
26
- "Failed to construct #{name}.new with the supplied arguments: #{error.message}",
27
- error.backtrace
43
+ # Ruby versions earlier than 2.7 can't reliably handle splatting empty kwargs, so branch for compatibility
44
+ inst = kwargs.empty? ? new(*args) : new(*args, **kwargs)
45
+ rescue ArgumentError => e
46
+ # Bubble up anything that isn't exactly ArgumentError (e.g., subclasses)
47
+ raise unless e.instance_of?(ArgumentError)
48
+ raise ConstructionError, "Cannot instantiate #{name}: #{e.message}", e.backtrace
49
+ else
50
+ # Ensure the instance defines `#call`
51
+ unless inst.respond_to?(:call)
52
+ raise NotImplementedError, "#{name} must implement #call"
28
53
  end
54
+ inst.call(&block)
55
+ end
29
56
 
30
- if block_given? && inst.method(:call).arity.zero?
31
- inst.call(&block)
32
- else
33
- inst.call
34
- end
57
+ # @return [Proc] a proc that delegates to `.call`, enabling `&MyService` shorthand.
58
+ def to_proc
59
+ method(:call).to_proc
35
60
  end
36
61
  end
37
62
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Callable
4
- VERSION = '0.1.2'
4
+ VERSION = '0.2.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: callable-mixin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Crowther