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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 78150ace9b1f03ae373409b70582aab42fded3b8ca7cf7268c9e15fcc8eba8a6
4
- data.tar.gz: 98fa5ae6f33bc0116f4510d58f698c5e493fd17582843f6b067888d9cf5d82db
3
+ metadata.gz: 7b15736a4210e0b8985c8978498bfcde00f3d7c574626e2b51a61d65cf5f85ef
4
+ data.tar.gz: 51e8e8b26a322261f77fb27e0a5c23bd35cced8347da6ac6b0bc806079b40e6b
5
5
  SHA512:
6
- metadata.gz: 9473879ba7b4f83ce62116855eff9b7dac6ea4923ed4cd43610490be669792bea01ed6ec273c8de58351b127a33efd36ecf0670da0a681d6116d6ebbc6fa9db4
7
- data.tar.gz: f4decc447ab5a9b79a6531fdaf40d0cadec9ecfca4f0f460d85ac8c629df1ae7f4d39aaa83b8bf23f822a7d7558133a4ac99cfb278bbe8364e4a6b2aa348bf4d
6
+ metadata.gz: 52cacd005d07848b64da529f35c3dfbcaef61c0851e2384e453e9e6f09e9de22eacc1a478f528941f8a0a8abd66f1df7792d22449e18c66beed3e24f9fe7bc49
7
+ data.tar.gz: 2fba1f3f416d764d261dc0fff5f516521898b9ee8af4242079ffecad87057d8ac8ab9d67a44770f7fd84c98de3a941264de02eaffda7028adbcaddb6971a6bf5
data/README.md CHANGED
@@ -6,74 +6,146 @@
6
6
  [![Documentation Status](https://img.shields.io/badge/docs-RubyDoc.info-blue.svg)](https://www.rubydoc.info/gems/callback-collection)
7
7
  [![Downloads](https://img.shields.io/gem/dt/callback-collection.svg?style=flat)](https://rubygems.org/gems/callback-collection)
8
8
 
9
- Une petite gem Ruby permettant de définir une collection immuable de callbacks
10
- nommés.
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
- Installez la gem depuis RubyGems :
43
+ Install the gem from RubyGems:
15
44
 
16
45
  ```sh
17
46
  gem install callback-collection
18
47
  ```
19
48
 
20
- Avec Bundler, ajoutez-la au `Gemfile` :
49
+ With Bundler, add it to your `Gemfile`:
21
50
 
22
51
  ```ruby
23
52
  gem "callback-collection"
24
53
  ```
25
54
 
26
- ## Utilisation
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| "Bienvenue, #{name} !" }
33
- collection.failure { |error| "Erreur : #{error.message}" }
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
- # => "Bienvenue, Ruby !"
66
+ # => "Welcome, Ruby!"
38
67
  ```
39
68
 
40
- La collection est figée à la fin de son initialisation. Toute tentative
41
- d'ajouter ensuite un callback lève une `FrozenError`.
69
+ The collection is frozen at the end of initialization. Attempting to add a
70
+ callback afterward raises `FrozenError`.
42
71
 
43
- ## Développement
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
- La tâche par défaut exécute la suite Minitest et construit la gem dans `pkg/`.
51
- L'intégration continue GitHub Actions vérifie le projet avec Ruby 2.7 à 3.4,
52
- ainsi qu'avec la dernière version stable, Ruby 4.0.
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
- ## Publication
125
+ ## Publishing
55
126
 
56
- La publication sur RubyGems utilise
57
- [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/) et ne
58
- nécessite aucune clé API dans les secrets GitHub.
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
- Avant la première publication, créez un *Pending Trusted Publisher* dans votre
61
- profil RubyGems avec les paramètres suivants :
131
+ Before the first release, create a *Pending Trusted Publisher* in your RubyGems
132
+ profile with these settings:
62
133
 
63
- - gem : `callback-collection`
64
- - propriétaire du dépôt : `nicolasva`
65
- - dépôt : `callback-collection`
66
- - workflow : `release.yml`
67
- - environnement GitHub : `release`
134
+ - gem: `callback-collection`
135
+ - repository owner: `nicolasva`
136
+ - repository: `callback-collection`
137
+ - workflow: `release.yml`
138
+ - GitHub environment: `release`
68
139
 
69
- Publiez ensuite une version en poussant le tag correspondant à
70
- `CallbackCollection::VERSION` :
140
+ Release a version by pushing the tag that matches
141
+ `CallbackCollection::VERSION`:
71
142
 
72
143
  ```sh
73
- git tag v0.1.0
74
- git push origin v0.1.0
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 construit et publie alors la gem. RubyDoc génère
78
- automatiquement sa documentation, et les badges de version et de
79
- téléchargements deviennent actifs après la première publication.
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.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class CallbackCollection
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.1"
5
5
  end
@@ -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
- raise FrozenError, "Cannot define a callback after initialization." if callbacks.frozen?
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.0
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.0
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: