cmdx 1.17.0 → 1.18.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: 6f8ab8732cd1ac0a15ac46872e78dc5d9549905c52bfda74d24948cae216b0d5
4
- data.tar.gz: 7735fc7d16163a8265adc59ad35f17990887c853aded67a92d5039c44c2df888
3
+ metadata.gz: 80c55fa1f758dc69cebff57fa5dd770b4bd7692f4a37768c320b6813d28cc9fb
4
+ data.tar.gz: babf14b98faa436de370d039cc2b6c796f2d0c43d9e408137ccdd1f666f4abe3
5
5
  SHA512:
6
- metadata.gz: 294c1f8cf3eab880c4e2f5d12c8655b246534cb61c357531171e90102f73146c37efc185cc6e877e4680c5a2809f0d3cba4fb64bf59fe1b6fea410da65454c9e
7
- data.tar.gz: d5e8e31425d658cfe151eeab44c938352d92d4f9e96326691d0c63b2688f0eeb64db26f7297d38264cbc6485c7355ae1ea3ad9a486a4ea0ccea8c02e4cd07a13
6
+ metadata.gz: cbcb58370126bc18aec53218930b11a7c6a3194b4b1e1c6247ae2d6e693ef16f84cc4dbf90a7dbd816e900f421e625cac3ca227aca7ce4898fa711c1451230e0
7
+ data.tar.gz: de88ce41eacbf4f843803f84055d46dcfb7774aa5532396c820112a5ca3d80d68deb9dbc296f8b782d990122ed6ccc082bbc582f93d3714373c2705c55a936fe
data/CHANGELOG.md CHANGED
@@ -4,7 +4,14 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [TODO]
7
+ ## [UNRELEASED]
8
+
9
+ ## [1.18.0] - 2025-03-09
10
+
11
+ ### Changed
12
+ - Use `Fiber.storage` instead of `Thread.current` for `Chain` and `Correlate` storage, with fallback to `Thread.current` for Ruby < 3.2, making them thread and fiber safe
13
+ - Clone shared logger in `Task#logger` when `log_level` or `log_formatter` is customized to prevent mutation of the shared instance
14
+ - Derive attribute values from source objects that respond to the attribute name (via `send`) as fallback when the source is not callable
8
15
 
9
16
  ## [1.17.0] - 2025-02-23
10
17
 
@@ -169,7 +169,12 @@ module CMDx
169
169
  when Hash then source_value[name.to_s] || source_value[name.to_sym]
170
170
  when Symbol then source_value.send(name)
171
171
  when Proc then task.instance_exec(name, &source_value)
172
- else source_value.call(task, name) if source_value.respond_to?(:call)
172
+ else
173
+ if source_value.respond_to?(:call)
174
+ source_value.call(task, name)
175
+ elsif source_value.respond_to?(name, true)
176
+ source_value.send(name)
177
+ end
173
178
  end
174
179
 
175
180
  derived_value.nil? ? default_value : derived_value
data/lib/cmdx/chain.rb CHANGED
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Manages a collection of task execution results in a thread-safe manner.
4
+ # Manages a collection of task execution results in a thread and fiber safe manner.
5
5
  # Chains provide a way to track related task executions and their outcomes
6
6
  # within the same execution context.
7
7
  class Chain
8
8
 
9
9
  extend Forwardable
10
10
 
11
- # @rbs THREAD_KEY: Symbol
12
- THREAD_KEY = :cmdx_chain
11
+ # @rbs CONCURRENCY_KEY: Symbol
12
+ CONCURRENCY_KEY = :cmdx_chain
13
13
 
14
14
  # Returns the unique identifier for this chain.
15
15
  #
@@ -47,7 +47,7 @@ module CMDx
47
47
 
48
48
  class << self
49
49
 
50
- # Retrieves the current chain for the current thread.
50
+ # Retrieves the current chain for the current execution context.
51
51
  #
52
52
  # @return [Chain, nil] The current chain or nil if none exists
53
53
  #
@@ -59,10 +59,10 @@ module CMDx
59
59
  #
60
60
  # @rbs () -> Chain?
61
61
  def current
62
- Thread.current[THREAD_KEY]
62
+ thread_or_fiber[CONCURRENCY_KEY]
63
63
  end
64
64
 
65
- # Sets the current chain for the current thread.
65
+ # Sets the current chain for the current execution context.
66
66
  #
67
67
  # @param chain [Chain] The chain to set as current
68
68
  #
@@ -73,10 +73,10 @@ module CMDx
73
73
  #
74
74
  # @rbs (Chain chain) -> Chain
75
75
  def current=(chain)
76
- Thread.current[THREAD_KEY] = chain
76
+ thread_or_fiber[CONCURRENCY_KEY] = chain
77
77
  end
78
78
 
79
- # Clears the current chain for the current thread.
79
+ # Clears the current chain for the current execution context.
80
80
  #
81
81
  # @return [nil] Always returns nil
82
82
  #
@@ -85,7 +85,7 @@ module CMDx
85
85
  #
86
86
  # @rbs () -> nil
87
87
  def clear
88
- Thread.current[THREAD_KEY] = nil
88
+ thread_or_fiber[CONCURRENCY_KEY] = nil
89
89
  end
90
90
 
91
91
  # Builds or extends the current chain by adding a result.
@@ -111,6 +111,17 @@ module CMDx
111
111
  current
112
112
  end
113
113
 
114
+ private
115
+
116
+ # Returns the thread or fiber storage for the current execution context.
117
+ #
118
+ # @return [Hash] The thread or fiber storage
119
+ #
120
+ # @rbs () -> Hash
121
+ def thread_or_fiber
122
+ Fiber.respond_to?(:storage) ? Fiber.storage : Thread.current
123
+ end
124
+
114
125
  end
115
126
 
116
127
  # Returns whether the chain is running in dry-run mode.
@@ -4,18 +4,18 @@ module CMDx
4
4
  module Middlewares
5
5
  # Middleware for correlating task executions with unique identifiers.
6
6
  #
7
- # The Correlate middleware provides thread-safe correlation ID management
8
- # for tracking task execution flows across different operations.
9
- # It automatically generates correlation IDs when none are provided and
10
- # stores them in task result metadata for traceability.
7
+ # The Correlate middleware provides thread and fiber safe correlation ID management
8
+ # for tracking task execution flows across different operations. It automatically
9
+ # generates correlation IDs when none are provided and stores them in task result
10
+ # metadata for traceability.
11
11
  module Correlate
12
12
 
13
13
  extend self
14
14
 
15
- # @rbs THREAD_KEY: Symbol
16
- THREAD_KEY = :cmdx_correlate
15
+ # @rbs CONCURRENCY_KEY: Symbol
16
+ CONCURRENCY_KEY = :cmdx_correlate
17
17
 
18
- # Retrieves the current correlation ID from thread-local storage.
18
+ # Retrieves the current correlation ID from local storage.
19
19
  #
20
20
  # @return [String, nil] The current correlation ID or nil if not set
21
21
  #
@@ -24,10 +24,10 @@ module CMDx
24
24
  #
25
25
  # @rbs () -> String?
26
26
  def id
27
- Thread.current[THREAD_KEY]
27
+ thread_or_fiber[CONCURRENCY_KEY]
28
28
  end
29
29
 
30
- # Sets the correlation ID in thread-local storage.
30
+ # Sets the correlation ID in local storage.
31
31
  #
32
32
  # @param id [String] The correlation ID to set
33
33
  # @return [String] The set correlation ID
@@ -37,10 +37,10 @@ module CMDx
37
37
  #
38
38
  # @rbs (String id) -> String
39
39
  def id=(id)
40
- Thread.current[THREAD_KEY] = id
40
+ thread_or_fiber[CONCURRENCY_KEY] = id
41
41
  end
42
42
 
43
- # Clears the current correlation ID from thread-local storage.
43
+ # Clears the current correlation ID from local storage.
44
44
  #
45
45
  # @return [nil] Always returns nil
46
46
  #
@@ -49,7 +49,7 @@ module CMDx
49
49
  #
50
50
  # @rbs () -> nil
51
51
  def clear
52
- Thread.current[THREAD_KEY] = nil
52
+ thread_or_fiber[CONCURRENCY_KEY] = nil
53
53
  end
54
54
 
55
55
  # Temporarily uses a new correlation ID for the duration of a block.
@@ -122,6 +122,17 @@ module CMDx
122
122
  use(correlation_id, &)
123
123
  end
124
124
 
125
+ private
126
+
127
+ # Returns the thread or fiber storage for the current execution context.
128
+ #
129
+ # @return [Hash] The thread or fiber storage
130
+ #
131
+ # @rbs () -> Hash
132
+ def thread_or_fiber
133
+ Fiber.respond_to?(:storage) ? Fiber.storage : Thread.current
134
+ end
135
+
125
136
  end
126
137
  end
127
138
  end
data/lib/cmdx/task.rb CHANGED
@@ -259,8 +259,8 @@ module CMDx
259
259
  #
260
260
  # @rbs () -> Hash[Symbol, Hash[Symbol, untyped]]
261
261
  def attributes_schema
262
- Array(settings[:attributes]).each_with_object({}) do |attr, schema|
263
- schema[attr.method_name] = attr.to_h
262
+ Array(settings[:attributes]).to_h do |attr|
263
+ [attr.method_name, attr.to_h]
264
264
  end
265
265
  end
266
266
 
@@ -351,6 +351,10 @@ module CMDx
351
351
  raise UndefinedMethodError, "undefined method #{self.class.name}#work"
352
352
  end
353
353
 
354
+ # Returns a logger for this task. When a custom log_level or
355
+ # log_formatter is configured, the shared logger is duplicated
356
+ # so the original instance is never mutated.
357
+ #
354
358
  # @return [Logger] The logger instance for this task
355
359
  #
356
360
  # @example
@@ -360,10 +364,17 @@ module CMDx
360
364
  # @rbs () -> Logger
361
365
  def logger
362
366
  @logger ||= begin
363
- logger = self.class.settings[:logger] || CMDx.configuration.logger
364
- logger.level = self.class.settings[:log_level] || logger.level
365
- logger.formatter = self.class.settings[:log_formatter] || logger.formatter
366
- logger
367
+ log_instance = self.class.settings[:logger] || CMDx.configuration.logger
368
+ log_level = self.class.settings[:log_level]
369
+ log_formatter = self.class.settings[:log_formatter]
370
+
371
+ if log_level || log_formatter
372
+ log_instance = log_instance.dup
373
+ log_instance.level = log_level if log_level
374
+ log_instance.formatter = log_formatter if log_formatter
375
+ end
376
+
377
+ log_instance
367
378
  end
368
379
  end
369
380
 
data/lib/cmdx/version.rb CHANGED
@@ -5,6 +5,6 @@ module CMDx
5
5
  # @return [String] the version of the CMDx gem
6
6
  #
7
7
  # @rbs return: String
8
- VERSION = "1.17.0"
8
+ VERSION = "1.18.0"
9
9
 
10
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cmdx
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.17.0
4
+ version: 1.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Juan Gomez
@@ -420,7 +420,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
420
420
  - !ruby/object:Gem::Version
421
421
  version: '0'
422
422
  requirements: []
423
- rubygems_version: 4.0.4
423
+ rubygems_version: 4.0.6
424
424
  specification_version: 4
425
425
  summary: CMDx is a framework for building maintainable business processes.
426
426
  test_files: []