callback-collection 0.1.0 → 0.2.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 +4 -4
- data/README.md +104 -32
- data/lib/callback_collection/version.rb +1 -1
- data/lib/callback_collection.rb +30 -4
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7b15736a4210e0b8985c8978498bfcde00f3d7c574626e2b51a61d65cf5f85ef
|
|
4
|
+
data.tar.gz: 51e8e8b26a322261f77fb27e0a5c23bd35cced8347da6ac6b0bc806079b40e6b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 52cacd005d07848b64da529f35c3dfbcaef61c0851e2384e453e9e6f09e9de22eacc1a478f528941f8a0a8abd66f1df7792d22449e18c66beed3e24f9fe7bc49
|
|
7
|
+
data.tar.gz: 2fba1f3f416d764d261dc0fff5f516521898b9ee8af4242079ffecad87057d8ac8ab9d67a44770f7fd84c98de3a941264de02eaffda7028adbcaddb6971a6bf5
|
data/README.md
CHANGED
|
@@ -6,74 +6,146 @@
|
|
|
6
6
|
[](https://www.rubydoc.info/gems/callback-collection)
|
|
7
7
|
[](https://rubygems.org/gems/callback-collection)
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
A small Ruby gem for defining an immutable collection of named callbacks.
|
|
10
|
+
|
|
11
|
+
## Why Callback Collection?
|
|
12
|
+
|
|
13
|
+
`CallbackCollection` groups callback definitions during initialization and
|
|
14
|
+
exposes a single `respond_with` interface for invoking them. The registry
|
|
15
|
+
becomes immutable once constructed, preventing late additions and making its
|
|
16
|
+
behavior easier to test and reason about.
|
|
17
|
+
|
|
18
|
+
The gem does not replace framework callbacks or start threads or Ractors
|
|
19
|
+
itself. It provides an independent container that applications can use within
|
|
20
|
+
their own execution model.
|
|
21
|
+
|
|
22
|
+
## Architectural highlights
|
|
23
|
+
|
|
24
|
+
- **Immutable after initialization:** the collection and its internal registry
|
|
25
|
+
are frozen when the configuration block completes. Callback definitions can
|
|
26
|
+
then be read concurrently by multiple threads without mutating shared
|
|
27
|
+
registry state.
|
|
28
|
+
- **Optional Ractor compatibility:** on Ruby 3.0 and later, `register` stores a
|
|
29
|
+
receiver and method name instead of a closure. The collection is shareable
|
|
30
|
+
when the receiver and all the state it uses are also Ractor-shareable.
|
|
31
|
+
- **Ruby 2.7 and later:** the standard API and `register` do not depend on
|
|
32
|
+
`Ractor`. Applications running Ruby 2.7 retain the immutable registry and
|
|
33
|
+
concurrent thread-read behavior.
|
|
34
|
+
- **Direct lookup:** `respond_with` uses `Hash#fetch` to find a callback in one
|
|
35
|
+
operation and raises an explicit error when it does not exist.
|
|
36
|
+
|
|
37
|
+
Immutability protects the registry, not automatically the code executed by its
|
|
38
|
+
callbacks. A callback that accesses mutable shared state remains responsible
|
|
39
|
+
for its own synchronization.
|
|
11
40
|
|
|
12
41
|
## Installation
|
|
13
42
|
|
|
14
|
-
|
|
43
|
+
Install the gem from RubyGems:
|
|
15
44
|
|
|
16
45
|
```sh
|
|
17
46
|
gem install callback-collection
|
|
18
47
|
```
|
|
19
48
|
|
|
20
|
-
|
|
49
|
+
With Bundler, add it to your `Gemfile`:
|
|
21
50
|
|
|
22
51
|
```ruby
|
|
23
52
|
gem "callback-collection"
|
|
24
53
|
```
|
|
25
54
|
|
|
26
|
-
##
|
|
55
|
+
## Usage
|
|
27
56
|
|
|
28
57
|
```ruby
|
|
29
58
|
require "callback_collection"
|
|
30
59
|
|
|
31
60
|
callbacks = CallbackCollection.new do |collection|
|
|
32
|
-
collection.success { |name| "
|
|
33
|
-
collection.failure { |error| "
|
|
61
|
+
collection.success { |name| "Welcome, #{name}!" }
|
|
62
|
+
collection.failure { |error| "Error: #{error.message}" }
|
|
34
63
|
end
|
|
35
64
|
|
|
36
65
|
callbacks.respond_with(:success, "Ruby")
|
|
37
|
-
# => "
|
|
66
|
+
# => "Welcome, Ruby!"
|
|
38
67
|
```
|
|
39
68
|
|
|
40
|
-
|
|
41
|
-
|
|
69
|
+
The collection is frozen at the end of initialization. Attempting to add a
|
|
70
|
+
callback afterward raises `FrozenError`.
|
|
42
71
|
|
|
43
|
-
|
|
72
|
+
### Ractors (Ruby 3.0 and later)
|
|
73
|
+
|
|
74
|
+
Blocks retain their lexical context and therefore cannot be shared between
|
|
75
|
+
Ractors. To create a shareable collection, register a shareable receiver and
|
|
76
|
+
one of its methods instead:
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
module Handlers
|
|
80
|
+
def self.sum(left, right)
|
|
81
|
+
left + right
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
callbacks = CallbackCollection.new do |collection|
|
|
86
|
+
collection.register(:sum, Handlers)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
Ractor.shareable?(callbacks)
|
|
90
|
+
# => true
|
|
91
|
+
|
|
92
|
+
worker = Ractor.new(callbacks) do |collection|
|
|
93
|
+
collection.respond_with(:sum, 20, 22)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
result = worker.respond_to?(:value) ? worker.value : worker.take
|
|
97
|
+
result
|
|
98
|
+
# => 42
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The third argument to `register` lets you use a method name that differs from
|
|
102
|
+
the callback name:
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
collection.register(:total, Handlers, :sum)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The registered receiver and the data it uses must follow Ractor shareability
|
|
109
|
+
rules. Calls to `respond_with` remain unchanged. On Ruby 2.7, the gem and
|
|
110
|
+
`register` remain fully usable; only Ractor execution is unavailable. The gem
|
|
111
|
+
does not create workers: the application retains control over their lifecycle
|
|
112
|
+
and exchanged messages.
|
|
113
|
+
|
|
114
|
+
## Development
|
|
44
115
|
|
|
45
116
|
```sh
|
|
46
117
|
bundle install
|
|
47
118
|
bundle exec rake
|
|
48
119
|
```
|
|
49
120
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
121
|
+
The default task runs the Minitest suite and builds the gem in `pkg/`. GitHub
|
|
122
|
+
Actions checks the project with Ruby 2.7 through 3.4 and the latest stable
|
|
123
|
+
release, Ruby 4.0.
|
|
53
124
|
|
|
54
|
-
##
|
|
125
|
+
## Publishing
|
|
55
126
|
|
|
56
|
-
|
|
57
|
-
[Trusted Publishing](https://guides.rubygems.org/trusted-publishing/)
|
|
58
|
-
|
|
127
|
+
RubyGems releases use
|
|
128
|
+
[Trusted Publishing](https://guides.rubygems.org/trusted-publishing/) and do
|
|
129
|
+
not require an API key in GitHub secrets.
|
|
59
130
|
|
|
60
|
-
|
|
61
|
-
|
|
131
|
+
Before the first release, create a *Pending Trusted Publisher* in your RubyGems
|
|
132
|
+
profile with these settings:
|
|
62
133
|
|
|
63
|
-
- gem
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
- workflow
|
|
67
|
-
-
|
|
134
|
+
- gem: `callback-collection`
|
|
135
|
+
- repository owner: `nicolasva`
|
|
136
|
+
- repository: `callback-collection`
|
|
137
|
+
- workflow: `release.yml`
|
|
138
|
+
- GitHub environment: `release`
|
|
68
139
|
|
|
69
|
-
|
|
70
|
-
`CallbackCollection::VERSION
|
|
140
|
+
Release a version by pushing the tag that matches
|
|
141
|
+
`CallbackCollection::VERSION`:
|
|
71
142
|
|
|
72
143
|
```sh
|
|
73
|
-
|
|
74
|
-
git
|
|
144
|
+
VERSION=$(ruby -Ilib -rcallback_collection/version -e 'print CallbackCollection::VERSION')
|
|
145
|
+
git tag "v${VERSION}"
|
|
146
|
+
git push origin "v${VERSION}"
|
|
75
147
|
```
|
|
76
148
|
|
|
77
|
-
GitHub Actions
|
|
78
|
-
|
|
79
|
-
|
|
149
|
+
GitHub Actions then builds and publishes the gem. RubyDoc generates its
|
|
150
|
+
documentation automatically, and the version and download badges update after
|
|
151
|
+
publication.
|
data/lib/callback_collection.rb
CHANGED
|
@@ -12,10 +12,30 @@ require_relative "callback_collection/version"
|
|
|
12
12
|
# callbacks.respond_with(:success, "data")
|
|
13
13
|
# # => "Received data"
|
|
14
14
|
class CallbackCollection
|
|
15
|
+
class RegisteredCallback
|
|
16
|
+
def initialize(receiver, method_name)
|
|
17
|
+
@receiver = receiver
|
|
18
|
+
@method_name = method_name
|
|
19
|
+
freeze
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call(*args, **kwargs, &block)
|
|
23
|
+
return @receiver.public_send(@method_name, *args, &block) if kwargs.empty?
|
|
24
|
+
|
|
25
|
+
@receiver.public_send(@method_name, *args, **kwargs, &block)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
private_constant :RegisteredCallback
|
|
29
|
+
|
|
15
30
|
def initialize
|
|
16
31
|
callbacks
|
|
17
32
|
yield(self) if block_given?
|
|
18
33
|
callbacks.freeze
|
|
34
|
+
freeze
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def register(callback, receiver, method_name = callback)
|
|
38
|
+
store_callback(callback, RegisteredCallback.new(receiver, method_name))
|
|
19
39
|
end
|
|
20
40
|
|
|
21
41
|
def respond_with(callback, *args, **kwargs, &block)
|
|
@@ -30,10 +50,7 @@ class CallbackCollection
|
|
|
30
50
|
|
|
31
51
|
def method_missing(method_name, *args, &block)
|
|
32
52
|
if block
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
callbacks[method_name] = block
|
|
36
|
-
self
|
|
53
|
+
store_callback(method_name, block)
|
|
37
54
|
else
|
|
38
55
|
super
|
|
39
56
|
end
|
|
@@ -48,4 +65,13 @@ class CallbackCollection
|
|
|
48
65
|
def callbacks
|
|
49
66
|
@callbacks ||= {}
|
|
50
67
|
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def store_callback(callback, handler)
|
|
72
|
+
raise FrozenError, "Cannot define a callback after initialization." if callbacks.frozen?
|
|
73
|
+
|
|
74
|
+
callbacks[callback] = handler
|
|
75
|
+
self
|
|
76
|
+
end
|
|
51
77
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: callback-collection
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nicolas Vandenbogaerde
|
|
@@ -56,7 +56,7 @@ homepage: https://github.com/nicolasva/callback-collection
|
|
|
56
56
|
licenses: []
|
|
57
57
|
metadata:
|
|
58
58
|
rubygems_mfa_required: 'true'
|
|
59
|
-
documentation_uri: https://www.rubydoc.info/gems/callback-collection/0.1
|
|
59
|
+
documentation_uri: https://www.rubydoc.info/gems/callback-collection/0.2.1
|
|
60
60
|
source_code_uri: https://github.com/nicolasva/callback-collection
|
|
61
61
|
rdoc_options: []
|
|
62
62
|
require_paths:
|