legionio 1.4.58 → 1.4.59

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: b941e35cffdc0cee05b505548896ec05866dc24c517f42ecc4d9a3f16a77c53f
4
- data.tar.gz: f0e5f2a91dc3627a3124bd9cbe74fb02ed235fad0fee966b221e48acc935bee7
3
+ metadata.gz: bb8c466919694ff3ee71e207099e6cdbc7d93ec3a641c7213a9efa5f624fceec
4
+ data.tar.gz: 714ca199c88b04b1f6b84680dbffe2c6f5d953d09d6114d115878cc98a10e3fe
5
5
  SHA512:
6
- metadata.gz: 67561fe5e20de1e404b63218fa971989d44767ff9df893ddc922598e94086e14b5134884c434264bb47449065018c8fe99dad88274c3ed629be0f98345ac0036
7
- data.tar.gz: fcb1fbe1f834f856410ce41d319dabc115bbbccbd23562783d26cc0efc8672c09eae205a5e7c376533744008cd3675f8b2208b393ba2e0dbc6335d4cb8e27ed8
6
+ metadata.gz: '08cce649982a2b0fabb240cf1ce373bf8b98c7807d5608d4e05caea9f26458b56c98f7b62584950d15376112d3f6396dbd3bd5c91e2779ca61867e1debaab2c5'
7
+ data.tar.gz: a7d37673e26e87ef65a04100884720da7c762f5fbd8c15f116febd7dc02314df77e0f1243acd2c383bc3e55e4ac940f3ca70ae49e0dfa76ece907871f39e7892
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Legion Changelog
2
2
 
3
+ ## [1.4.59] - 2026-03-17
4
+
5
+ ### Added
6
+ - `remote_invocable?` flag for LEX extensions: when `false`, the auto-generated Subscription actor is skipped (no RabbitMQ queue, no thread pool, no AMQP binding)
7
+ - 5-level resolution order: per-runner settings, extension settings, runner class method, extension module method, default `true`
8
+ - `@local_tasks` list tracks subscription actors skipped due to `remote_invocable? false` for introspection
9
+ - `remote_invocable?` default method added to `Legion::Extensions::Core` and `Legion::Extensions::Actors::Base`
10
+ - Fully backward compatible — all existing extensions unaffected
11
+
3
12
  ## [1.4.58] - 2026-03-17
4
13
 
5
14
  ### Added
@@ -35,6 +44,7 @@
35
44
  ### Changed
36
45
  - `build_default_exchange` now sets `exchange_name` on dynamically created exchange classes to return `amqp_prefix` (dot-joined segments with `legion.` prefix) instead of defaulting to the parent class behavior
37
46
  - `auto_create_exchange` now derives `exchange_name` from `amqp_prefix` + the exchange's own downcased class name, replacing the index-based `split('::')[5].downcase` extraction that broke for nested extension namespaces
47
+
38
48
  ### Fixed
39
49
  - `legion config scaffold` now writes to `~/.legionio/settings/` by default instead of `./settings/`
40
50
  - Removed Thor `default: './settings'` that shadowed the Ruby fallback in `ConfigScaffold.run`
@@ -43,6 +43,10 @@ module Legion
43
43
  def enabled?
44
44
  true
45
45
  end
46
+
47
+ def remote_invocable?
48
+ true
49
+ end
46
50
  end
47
51
  end
48
52
  end
@@ -83,6 +83,10 @@ module Legion
83
83
  false
84
84
  end
85
85
 
86
+ def remote_invocable?
87
+ true
88
+ end
89
+
86
90
  def build_data
87
91
  auto_generate_data
88
92
  lex_class::Data.build
@@ -16,12 +16,15 @@ module Legion
16
16
  @once_tasks = []
17
17
  @poll_tasks = []
18
18
  @subscription_tasks = []
19
+ @local_tasks = []
19
20
  @actors = []
20
21
 
21
22
  find_extensions
22
23
  load_extensions
23
24
  end
24
25
 
26
+ attr_reader :local_tasks
27
+
25
28
  def shutdown
26
29
  return nil if @loaded_extensions.nil?
27
30
 
@@ -192,23 +195,69 @@ module Legion
192
195
  elsif actor_class.ancestors.include? Legion::Extensions::Actors::Poll
193
196
  @poll_tasks.push(extension_hash)
194
197
  elsif actor_class.ancestors.include? Legion::Extensions::Actors::Subscription
195
- extension_hash[:threadpool] = Concurrent::FixedThreadPool.new(size)
196
- size.times do
197
- extension_hash[:threadpool].post do
198
- klass = actor_class.new
199
- if klass.respond_to?(:async)
200
- klass.async.subscribe
201
- else
202
- klass.subscribe
203
- end
204
- end
205
- end
206
- @subscription_tasks.push(extension_hash)
198
+ hook_subscription_actor(extension_hash, size, opts)
207
199
  else
208
200
  Legion::Logging.fatal 'did not match any actor classes'
209
201
  end
210
202
  end
211
203
 
204
+ private
205
+
206
+ def hook_subscription_actor(extension_hash, size, opts)
207
+ ext_name = extension_hash[:extension_name]
208
+ extension = extension_hash[:extension]
209
+ actor_class = extension_hash[:actor_class]
210
+
211
+ unless resolve_remote_invocable(ext_name, opts.merge(actor_class: actor_class, extension: extension))
212
+ Legion::Logging.debug { "#{ext_name}/#{extension_hash[:actor_name]} is not remote_invocable, skipping AMQP subscription" }
213
+ @local_tasks.push(extension_hash)
214
+ return
215
+ end
216
+
217
+ extension_hash[:threadpool] = Concurrent::FixedThreadPool.new(size)
218
+ size.times do
219
+ extension_hash[:threadpool].post do
220
+ klass = actor_class.new
221
+ if klass.respond_to?(:async)
222
+ klass.async.subscribe
223
+ else
224
+ klass.subscribe
225
+ end
226
+ end
227
+ end
228
+ @subscription_tasks.push(extension_hash)
229
+ end
230
+
231
+ def resolve_remote_invocable(extension_name, opts = {})
232
+ ext_key = extension_name.to_sym
233
+ ext_settings = Legion::Settings.dig(:extensions, ext_key)
234
+ runner_name = opts[:actor_name]&.to_sym
235
+
236
+ # 1. Per-runner settings override
237
+ runner_setting = ext_settings&.dig(:runners, runner_name, :remote_invocable)
238
+ return runner_setting unless runner_setting.nil?
239
+
240
+ # 2. Extension settings override
241
+ ext_setting = ext_settings&.dig(:remote_invocable)
242
+ return ext_setting unless ext_setting.nil?
243
+
244
+ # 3. Runner class method (only if defined directly on the runner, not inherited)
245
+ runner_class = opts[:runner_class]
246
+ if runner_class.respond_to?(:remote_invocable?)
247
+ owner = runner_class.method(:remote_invocable?).owner
248
+ return runner_class.remote_invocable? if owner == runner_class.singleton_class || !owner.singleton_class?
249
+ end
250
+
251
+ # 4. Extension module method
252
+ extension = opts[:extension]
253
+ return extension.remote_invocable? if extension.respond_to?(:remote_invocable?)
254
+
255
+ # 5. Default
256
+ true
257
+ end
258
+
259
+ public
260
+
212
261
  def gem_load(entry)
213
262
  gem_name = entry[:gem_name]
214
263
  require_path = entry[:require_path]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Legion
4
- VERSION = '1.4.58'
4
+ VERSION = '1.4.59'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legionio
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.58
4
+ version: 1.4.59
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity