cmdx 1.1.2 → 1.5.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 +4 -4
- data/.DS_Store +0 -0
- data/.cursor/prompts/docs.md +4 -1
- data/.cursor/prompts/llms.md +20 -0
- data/.cursor/prompts/rspec.md +4 -1
- data/.cursor/prompts/yardoc.md +3 -2
- data/.cursor/rules/cursor-instructions.mdc +56 -1
- data/.irbrc +6 -0
- data/.rubocop.yml +29 -18
- data/CHANGELOG.md +5 -133
- data/LLM.md +3317 -0
- data/README.md +68 -44
- data/docs/attributes/coercions.md +162 -0
- data/docs/attributes/defaults.md +90 -0
- data/docs/attributes/definitions.md +281 -0
- data/docs/attributes/naming.md +78 -0
- data/docs/attributes/validations.md +309 -0
- data/docs/basics/chain.md +56 -249
- data/docs/basics/context.md +56 -289
- data/docs/basics/execution.md +114 -0
- data/docs/basics/setup.md +37 -334
- data/docs/callbacks.md +89 -467
- data/docs/deprecation.md +91 -174
- data/docs/getting_started.md +212 -202
- data/docs/internationalization.md +11 -647
- data/docs/interruptions/exceptions.md +23 -198
- data/docs/interruptions/faults.md +71 -151
- data/docs/interruptions/halt.md +109 -186
- data/docs/logging.md +44 -256
- data/docs/middlewares.md +113 -426
- data/docs/outcomes/result.md +81 -228
- data/docs/outcomes/states.md +33 -221
- data/docs/outcomes/statuses.md +21 -311
- data/docs/tips_and_tricks.md +120 -70
- data/docs/workflows.md +99 -283
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/attribute.rb +229 -0
- data/lib/cmdx/attribute_registry.rb +94 -0
- data/lib/cmdx/attribute_value.rb +193 -0
- data/lib/cmdx/callback_registry.rb +69 -77
- data/lib/cmdx/chain.rb +56 -73
- data/lib/cmdx/coercion_registry.rb +52 -68
- data/lib/cmdx/coercions/array.rb +19 -18
- data/lib/cmdx/coercions/big_decimal.rb +20 -24
- data/lib/cmdx/coercions/boolean.rb +26 -25
- data/lib/cmdx/coercions/complex.rb +21 -22
- data/lib/cmdx/coercions/date.rb +25 -23
- data/lib/cmdx/coercions/date_time.rb +24 -25
- data/lib/cmdx/coercions/float.rb +25 -22
- data/lib/cmdx/coercions/hash.rb +31 -32
- data/lib/cmdx/coercions/integer.rb +30 -24
- data/lib/cmdx/coercions/rational.rb +29 -24
- data/lib/cmdx/coercions/string.rb +19 -22
- data/lib/cmdx/coercions/symbol.rb +37 -0
- data/lib/cmdx/coercions/time.rb +26 -25
- data/lib/cmdx/configuration.rb +49 -108
- data/lib/cmdx/context.rb +222 -44
- data/lib/cmdx/deprecator.rb +61 -0
- data/lib/cmdx/errors.rb +42 -252
- data/lib/cmdx/exceptions.rb +39 -0
- data/lib/cmdx/faults.rb +78 -39
- data/lib/cmdx/freezer.rb +51 -0
- data/lib/cmdx/identifier.rb +30 -0
- data/lib/cmdx/locale.rb +52 -0
- data/lib/cmdx/log_formatters/json.rb +21 -22
- data/lib/cmdx/log_formatters/key_value.rb +20 -22
- data/lib/cmdx/log_formatters/line.rb +15 -22
- data/lib/cmdx/log_formatters/logstash.rb +22 -23
- data/lib/cmdx/log_formatters/raw.rb +16 -22
- data/lib/cmdx/middleware_registry.rb +70 -74
- data/lib/cmdx/middlewares/correlate.rb +90 -54
- data/lib/cmdx/middlewares/runtime.rb +58 -0
- data/lib/cmdx/middlewares/timeout.rb +48 -68
- data/lib/cmdx/railtie.rb +12 -45
- data/lib/cmdx/result.rb +229 -314
- data/lib/cmdx/task.rb +194 -366
- data/lib/cmdx/utils/call.rb +49 -0
- data/lib/cmdx/utils/condition.rb +71 -0
- data/lib/cmdx/utils/format.rb +61 -0
- data/lib/cmdx/validator_registry.rb +63 -72
- data/lib/cmdx/validators/exclusion.rb +38 -67
- data/lib/cmdx/validators/format.rb +48 -49
- data/lib/cmdx/validators/inclusion.rb +43 -74
- data/lib/cmdx/validators/length.rb +91 -154
- data/lib/cmdx/validators/numeric.rb +87 -162
- data/lib/cmdx/validators/presence.rb +37 -50
- data/lib/cmdx/version.rb +1 -1
- data/lib/cmdx/worker.rb +178 -0
- data/lib/cmdx/workflow.rb +85 -81
- data/lib/cmdx.rb +19 -13
- data/lib/generators/cmdx/install_generator.rb +14 -13
- data/lib/generators/cmdx/task_generator.rb +25 -50
- data/lib/generators/cmdx/templates/install.rb +11 -46
- data/lib/generators/cmdx/templates/task.rb.tt +3 -2
- data/lib/locales/en.yml +18 -4
- data/src/cmdx-logo.png +0 -0
- metadata +32 -116
- data/docs/ai_prompts.md +0 -393
- data/docs/basics/call.md +0 -317
- data/docs/configuration.md +0 -344
- data/docs/parameters/coercions.md +0 -396
- data/docs/parameters/defaults.md +0 -335
- data/docs/parameters/definitions.md +0 -446
- data/docs/parameters/namespacing.md +0 -378
- data/docs/parameters/validations.md +0 -405
- data/docs/testing.md +0 -553
- data/lib/cmdx/callback.rb +0 -53
- data/lib/cmdx/chain_inspector.rb +0 -56
- data/lib/cmdx/chain_serializer.rb +0 -63
- data/lib/cmdx/coercion.rb +0 -57
- data/lib/cmdx/coercions/virtual.rb +0 -29
- data/lib/cmdx/core_ext/hash.rb +0 -83
- data/lib/cmdx/core_ext/module.rb +0 -98
- data/lib/cmdx/core_ext/object.rb +0 -125
- data/lib/cmdx/correlator.rb +0 -122
- data/lib/cmdx/error.rb +0 -67
- data/lib/cmdx/fault.rb +0 -140
- data/lib/cmdx/immutator.rb +0 -52
- data/lib/cmdx/lazy_struct.rb +0 -246
- data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
- data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
- data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
- data/lib/cmdx/logger.rb +0 -49
- data/lib/cmdx/logger_ansi.rb +0 -68
- data/lib/cmdx/logger_serializer.rb +0 -116
- data/lib/cmdx/middleware.rb +0 -70
- data/lib/cmdx/parameter.rb +0 -312
- data/lib/cmdx/parameter_evaluator.rb +0 -231
- data/lib/cmdx/parameter_inspector.rb +0 -66
- data/lib/cmdx/parameter_registry.rb +0 -106
- data/lib/cmdx/parameter_serializer.rb +0 -59
- data/lib/cmdx/result_ansi.rb +0 -71
- data/lib/cmdx/result_inspector.rb +0 -71
- data/lib/cmdx/result_logger.rb +0 -59
- data/lib/cmdx/result_serializer.rb +0 -104
- data/lib/cmdx/rspec/matchers.rb +0 -28
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
- data/lib/cmdx/task_deprecator.rb +0 -58
- data/lib/cmdx/task_processor.rb +0 -246
- data/lib/cmdx/task_serializer.rb +0 -57
- data/lib/cmdx/utils/ansi_color.rb +0 -73
- data/lib/cmdx/utils/log_timestamp.rb +0 -36
- data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
- data/lib/cmdx/utils/name_affix.rb +0 -52
- data/lib/cmdx/validator.rb +0 -57
- data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
- data/lib/generators/cmdx/workflow_generator.rb +0 -84
- data/lib/locales/ar.yml +0 -35
- data/lib/locales/cs.yml +0 -35
- data/lib/locales/da.yml +0 -35
- data/lib/locales/de.yml +0 -35
- data/lib/locales/el.yml +0 -35
- data/lib/locales/es.yml +0 -35
- data/lib/locales/fi.yml +0 -35
- data/lib/locales/fr.yml +0 -35
- data/lib/locales/he.yml +0 -35
- data/lib/locales/hi.yml +0 -35
- data/lib/locales/it.yml +0 -35
- data/lib/locales/ja.yml +0 -35
- data/lib/locales/ko.yml +0 -35
- data/lib/locales/nl.yml +0 -35
- data/lib/locales/no.yml +0 -35
- data/lib/locales/pl.yml +0 -35
- data/lib/locales/pt.yml +0 -35
- data/lib/locales/ru.yml +0 -35
- data/lib/locales/sv.yml +0 -35
- data/lib/locales/th.yml +0 -35
- data/lib/locales/tr.yml +0 -35
- data/lib/locales/vi.yml +0 -35
- data/lib/locales/zh.yml +0 -35
@@ -2,36 +2,35 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
module LogFormatters
|
5
|
-
#
|
5
|
+
# Formats log messages as JSON for structured logging
|
6
6
|
#
|
7
|
-
# This formatter converts log entries into JSON format
|
8
|
-
#
|
9
|
-
#
|
10
|
-
class
|
7
|
+
# This formatter converts log entries into JSON format with standardized fields
|
8
|
+
# including severity, timestamp, program name, process ID, and formatted message.
|
9
|
+
# The output is suitable for log aggregation systems and structured analysis.
|
10
|
+
class JSON
|
11
11
|
|
12
|
-
# Formats a log entry as a JSON string
|
12
|
+
# Formats a log entry as a JSON string
|
13
13
|
#
|
14
|
-
# @param severity [String]
|
15
|
-
# @param time [Time]
|
16
|
-
# @param
|
17
|
-
# @param message [
|
14
|
+
# @param severity [String] The log level (e.g., "INFO", "ERROR", "DEBUG")
|
15
|
+
# @param time [Time] The timestamp when the log entry was created
|
16
|
+
# @param progname [String, nil] The program name or identifier
|
17
|
+
# @param message [Object] The log message content
|
18
18
|
#
|
19
|
-
# @return [String]
|
19
|
+
# @return [String] A JSON-formatted log entry with a trailing newline
|
20
20
|
#
|
21
|
-
# @
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
# #=> "{\"severity\":\"INFO\",\"pid\":12345,\"timestamp\":\"2024-01-01T12:00:00Z\",\"message\":\"Task completed\"}\n"
|
27
|
-
def call(severity, time, task, message)
|
28
|
-
m = LoggerSerializer.call(severity, time, task, message).merge!(
|
21
|
+
# @example Basic usage
|
22
|
+
# logger_formatter.call("INFO", Time.now, "MyApp", "User logged in")
|
23
|
+
# # => '{"severity":"INFO","timestamp":"2024-01-15T10:30:45.123456Z","progname":"MyApp","pid":12345,"message":"User logged in"}\n'
|
24
|
+
def call(severity, time, progname, message)
|
25
|
+
hash = {
|
29
26
|
severity:,
|
27
|
+
timestamp: time.utc.iso8601(6),
|
28
|
+
progname:,
|
30
29
|
pid: Process.pid,
|
31
|
-
|
32
|
-
|
30
|
+
message: Utils::Format.to_log(message)
|
31
|
+
}
|
33
32
|
|
34
|
-
JSON.dump(
|
33
|
+
::JSON.dump(hash) << "\n"
|
35
34
|
end
|
36
35
|
|
37
36
|
end
|
@@ -2,37 +2,35 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
module LogFormatters
|
5
|
-
#
|
5
|
+
# Formats log messages as key-value pairs for structured logging
|
6
6
|
#
|
7
|
-
# This formatter converts log entries into key-value format
|
8
|
-
#
|
9
|
-
#
|
7
|
+
# This formatter converts log entries into key-value format with standardized fields
|
8
|
+
# including severity, timestamp, program name, process ID, and formatted message.
|
9
|
+
# The output is suitable for log parsing tools and human-readable structured logs.
|
10
10
|
class KeyValue
|
11
11
|
|
12
|
-
# Formats a log entry as a key
|
12
|
+
# Formats a log entry as a key-value string
|
13
13
|
#
|
14
|
-
# @param severity [String]
|
15
|
-
# @param time [Time]
|
16
|
-
# @param
|
17
|
-
# @param message [
|
14
|
+
# @param severity [String] The log level (e.g., "INFO", "ERROR", "DEBUG")
|
15
|
+
# @param time [Time] The timestamp when the log entry was created
|
16
|
+
# @param progname [String, nil] The program name or identifier
|
17
|
+
# @param message [Object] The log message content
|
18
18
|
#
|
19
|
-
# @return [String]
|
19
|
+
# @return [String] A key-value formatted log entry with a trailing newline
|
20
20
|
#
|
21
|
-
# @
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
# #=> "severity=INFO pid=12345 timestamp=2024-01-01T12:00:00Z message=Task completed\n"
|
27
|
-
def call(severity, time, task, message)
|
28
|
-
m = LoggerSerializer.call(severity, time, task, message).merge!(
|
21
|
+
# @example Basic usage
|
22
|
+
# logger_formatter.call("INFO", Time.now, "MyApp", "User logged in")
|
23
|
+
# # => "severity=INFO timestamp=2024-01-15T10:30:45.123456Z progname=MyApp pid=12345 message=User logged in\n"
|
24
|
+
def call(severity, time, progname, message)
|
25
|
+
hash = {
|
29
26
|
severity:,
|
27
|
+
timestamp: time.utc.iso8601(6),
|
28
|
+
progname:,
|
30
29
|
pid: Process.pid,
|
31
|
-
|
32
|
-
|
30
|
+
message: Utils::Format.to_log(message)
|
31
|
+
}
|
33
32
|
|
34
|
-
|
35
|
-
m << "\n"
|
33
|
+
Utils::Format.to_str(hash) << "\n"
|
36
34
|
end
|
37
35
|
|
38
36
|
end
|
@@ -2,34 +2,27 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
module LogFormatters
|
5
|
-
#
|
5
|
+
# Formats log messages as single-line text for human-readable logging
|
6
6
|
#
|
7
|
-
# This formatter converts log entries into a
|
8
|
-
#
|
9
|
-
#
|
7
|
+
# This formatter converts log entries into a compact single-line format with
|
8
|
+
# severity abbreviation, ISO8601 timestamp, process ID, and formatted message.
|
9
|
+
# The output is optimized for human readability and traditional log file formats.
|
10
10
|
class Line
|
11
11
|
|
12
|
-
# Formats a log entry as a line string
|
12
|
+
# Formats a log entry as a single-line string
|
13
13
|
#
|
14
|
-
# @param severity [String]
|
15
|
-
# @param time [Time]
|
16
|
-
# @param
|
17
|
-
# @param message [
|
14
|
+
# @param severity [String] The log level (e.g., "INFO", "ERROR", "DEBUG")
|
15
|
+
# @param time [Time] The timestamp when the log entry was created
|
16
|
+
# @param progname [String, nil] The program name or identifier
|
17
|
+
# @param message [Object] The log message content
|
18
18
|
#
|
19
|
-
# @return [String]
|
19
|
+
# @return [String] A single-line formatted log entry with a trailing newline
|
20
20
|
#
|
21
|
-
# @
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
# #=> "I, [2024-01-01T12:00:00.000Z #12345] INFO -- TaskClass: Task completed\n"
|
27
|
-
def call(severity, time, task, message)
|
28
|
-
t = Utils::LogTimestamp.call(time.utc)
|
29
|
-
m = LoggerSerializer.call(severity, time, task, message)
|
30
|
-
m = m.map { |k, v| "#{k}=#{v}" }.join(" ") if m.is_a?(Hash)
|
31
|
-
|
32
|
-
"#{severity[0]}, [#{t} ##{Process.pid}] #{severity} -- #{task.class.name}: #{m}\n"
|
21
|
+
# @example Basic usage
|
22
|
+
# logger_formatter.call("INFO", Time.now, "MyApp", "User logged in")
|
23
|
+
# # => "I, [2024-01-15T10:30:45.123456Z #12345] INFO -- MyApp: User logged in\n"
|
24
|
+
def call(severity, time, progname, message)
|
25
|
+
"#{severity[0]}, [#{time.utc.iso8601(6)} ##{Process.pid}] #{severity} -- #{progname}: #{message}\n"
|
33
26
|
end
|
34
27
|
|
35
28
|
end
|
@@ -2,38 +2,37 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
module LogFormatters
|
5
|
-
#
|
5
|
+
# Formats log messages as Logstash-compatible JSON for structured logging
|
6
6
|
#
|
7
|
-
# This formatter converts log entries into Logstash-compatible JSON format
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
7
|
+
# This formatter converts log entries into Logstash-compatible JSON format with
|
8
|
+
# standardized fields including @version, @timestamp, severity, program name,
|
9
|
+
# process ID, and formatted message. The output follows Logstash event format
|
10
|
+
# specifications for seamless integration with ELK stack and similar systems.
|
11
11
|
class Logstash
|
12
12
|
|
13
|
-
# Formats a log entry as a Logstash-compatible JSON string
|
13
|
+
# Formats a log entry as a Logstash-compatible JSON string
|
14
14
|
#
|
15
|
-
# @param severity [String]
|
16
|
-
# @param time [Time]
|
17
|
-
# @param
|
18
|
-
# @param message [
|
15
|
+
# @param severity [String] The log level (e.g., "INFO", "ERROR", "DEBUG")
|
16
|
+
# @param time [Time] The timestamp when the log entry was created
|
17
|
+
# @param progname [String, nil] The program name or identifier
|
18
|
+
# @param message [Object] The log message content
|
19
19
|
#
|
20
|
-
# @return [String]
|
20
|
+
# @return [String] A Logstash-compatible JSON-formatted log entry with a trailing newline
|
21
21
|
#
|
22
|
-
# @
|
23
|
-
#
|
24
|
-
# @
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
m = LoggerSerializer.call(severity, time, task, message).merge!(
|
22
|
+
# @example Basic usage
|
23
|
+
# logger_formatter.call("INFO", Time.now, "MyApp", "User logged in")
|
24
|
+
# # => '{"@version":"1","@timestamp":"2024-01-15T10:30:45.123456Z","severity":"INFO","progname":"MyApp","pid":12345,"message":"User logged in"}\n'
|
25
|
+
def call(severity, time, progname, message)
|
26
|
+
hash = {
|
27
|
+
"@version" => "1",
|
28
|
+
"@timestamp" => time.utc.iso8601(6),
|
30
29
|
severity:,
|
30
|
+
progname:,
|
31
31
|
pid: Process.pid,
|
32
|
-
|
33
|
-
|
34
|
-
)
|
32
|
+
message: Utils::Format.to_log(message)
|
33
|
+
}
|
35
34
|
|
36
|
-
JSON.dump(
|
35
|
+
::JSON.dump(hash) << "\n"
|
37
36
|
end
|
38
37
|
|
39
38
|
end
|
@@ -2,34 +2,28 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
module LogFormatters
|
5
|
-
#
|
5
|
+
# Formats log messages as raw text without additional formatting
|
6
6
|
#
|
7
|
-
# This formatter
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
7
|
+
# This formatter outputs log messages in their original form with minimal
|
8
|
+
# processing, adding only a trailing newline. It's useful for scenarios
|
9
|
+
# where you want to preserve the exact message content without metadata
|
10
|
+
# or structured formatting.
|
11
11
|
class Raw
|
12
12
|
|
13
|
-
# Formats a log entry as
|
13
|
+
# Formats a log entry as raw text
|
14
14
|
#
|
15
|
-
# @param
|
16
|
-
# @param
|
17
|
-
# @param
|
18
|
-
# @param message [Object]
|
15
|
+
# @param severity [String] The log level (e.g., "INFO", "ERROR", "DEBUG")
|
16
|
+
# @param time [Time] The timestamp when the log entry was created
|
17
|
+
# @param progname [String, nil] The program name or identifier
|
18
|
+
# @param message [Object] The log message content
|
19
19
|
#
|
20
|
-
# @return [String]
|
20
|
+
# @return [String] The raw message with a trailing newline
|
21
21
|
#
|
22
|
-
# @example
|
23
|
-
#
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
-
# @example Formatting a complex object
|
28
|
-
# formatter = CMDx::LogFormatters::Raw.new
|
29
|
-
# result = formatter.call("DEBUG", Time.now, task_object, { status: :success, count: 42 })
|
30
|
-
# #=> "{:status=>:success, :count=>42}\n"
|
31
|
-
def call(_severity, _time, _task, message)
|
32
|
-
message.inspect + "\n" # rubocop:disable Style/StringConcatenation
|
22
|
+
# @example Basic usage
|
23
|
+
# logger_formatter.call("INFO", Time.now, "MyApp", "User logged in")
|
24
|
+
# # => "User logged in\n"
|
25
|
+
def call(severity, time, progname, message)
|
26
|
+
"#{message}\n"
|
33
27
|
end
|
34
28
|
|
35
29
|
end
|
@@ -1,110 +1,106 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
# Registry for managing middleware
|
4
|
+
# Registry for managing middleware components in a task execution chain.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
6
|
+
# The MiddlewareRegistry maintains an ordered list of middleware components
|
7
|
+
# that can be inserted, removed, and executed in sequence. Each middleware
|
8
|
+
# can be configured with specific options and is executed in the order
|
9
|
+
# they were registered.
|
9
10
|
class MiddlewareRegistry
|
10
11
|
|
11
|
-
# @return [Hash] hash containing middleware classes/objects and their configurations
|
12
12
|
attr_reader :registry
|
13
|
+
alias to_a registry
|
13
14
|
|
14
|
-
#
|
15
|
+
# Initialize a new middleware registry.
|
15
16
|
#
|
16
|
-
# @param registry [
|
17
|
+
# @param registry [Array] Initial array of middleware entries
|
17
18
|
#
|
18
|
-
# @
|
19
|
+
# @example
|
20
|
+
# registry = MiddlewareRegistry.new
|
21
|
+
# registry = MiddlewareRegistry.new([[MyMiddleware, {option: 'value'}]])
|
22
|
+
def initialize(registry = [])
|
23
|
+
@registry = registry
|
24
|
+
end
|
25
|
+
|
26
|
+
# Create a duplicate of the registry with duplicated middleware entries.
|
19
27
|
#
|
20
|
-
# @
|
21
|
-
# MiddlewareRegistry.new
|
28
|
+
# @return [MiddlewareRegistry] A new registry instance with duplicated entries
|
22
29
|
#
|
23
|
-
# @example
|
24
|
-
#
|
25
|
-
def
|
26
|
-
|
30
|
+
# @example
|
31
|
+
# new_registry = registry.dup
|
32
|
+
def dup
|
33
|
+
self.class.new(registry.map(&:dup))
|
27
34
|
end
|
28
35
|
|
29
|
-
#
|
36
|
+
# Register a middleware component in the registry.
|
30
37
|
#
|
31
|
-
# @param middleware [
|
32
|
-
# @param
|
33
|
-
# @param
|
34
|
-
# @
|
38
|
+
# @param middleware [Object] The middleware object to register
|
39
|
+
# @param at [Integer] Position to insert the middleware (default: -1, end of list)
|
40
|
+
# @param options [Hash] Configuration options for the middleware
|
41
|
+
# @option options [Symbol] :key Configuration key for the middleware
|
42
|
+
# @option options [Object] :value Configuration value for the middleware
|
35
43
|
#
|
36
|
-
# @return [MiddlewareRegistry] self for method chaining
|
44
|
+
# @return [MiddlewareRegistry] Returns self for method chaining
|
37
45
|
#
|
38
|
-
# @example
|
39
|
-
# registry.register(
|
46
|
+
# @example
|
47
|
+
# registry.register(LoggingMiddleware, at: 0, log_level: :debug)
|
48
|
+
# registry.register(AuthMiddleware, at: -1, timeout: 30)
|
49
|
+
def register(middleware, at: -1, **options)
|
50
|
+
registry.insert(at, [middleware, options])
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
# Remove a middleware component from the registry.
|
55
|
+
#
|
56
|
+
# @param middleware [Object] The middleware object to remove
|
40
57
|
#
|
41
|
-
# @
|
42
|
-
# registry.register(LoggingMiddleware, level: :info)
|
58
|
+
# @return [MiddlewareRegistry] Returns self for method chaining
|
43
59
|
#
|
44
|
-
# @example
|
45
|
-
# registry.
|
46
|
-
def
|
47
|
-
registry
|
60
|
+
# @example
|
61
|
+
# registry.deregister(LoggingMiddleware)
|
62
|
+
def deregister(middleware)
|
63
|
+
registry.reject! { |mw, _opts| mw == middleware }
|
48
64
|
self
|
49
65
|
end
|
50
66
|
|
51
|
-
#
|
67
|
+
# Execute the middleware chain for a given task.
|
52
68
|
#
|
53
|
-
# @param task [
|
54
|
-
# @param block [Proc] the block to execute after all middleware processing
|
69
|
+
# @param task [Object] The task object to process through middleware
|
55
70
|
#
|
56
|
-
# @
|
71
|
+
# @yield [task] Block to execute after all middleware processing
|
72
|
+
# @yieldparam task [Object] The processed task object
|
57
73
|
#
|
58
|
-
# @
|
74
|
+
# @return [Object] Result of the block execution
|
59
75
|
#
|
60
|
-
# @
|
61
|
-
# registry.call(task) { |task| task.process }
|
76
|
+
# @raise [ArgumentError] When no block is provided
|
62
77
|
#
|
63
|
-
# @example
|
64
|
-
# registry.call(
|
65
|
-
|
78
|
+
# @example
|
79
|
+
# result = registry.call!(my_task) do |processed_task|
|
80
|
+
# processed_task.execute
|
81
|
+
# end
|
82
|
+
def call!(task, &)
|
66
83
|
raise ArgumentError, "block required" unless block_given?
|
67
84
|
|
68
|
-
|
69
|
-
|
70
|
-
build_chain(&).call(task)
|
71
|
-
end
|
72
|
-
|
73
|
-
# Returns a hash representation of the registry.
|
74
|
-
#
|
75
|
-
# @return [Hash] deep copy of registry with duplicated configuration arrays
|
76
|
-
# @option return [Array] args duplicated positional arguments array
|
77
|
-
# @option return [Hash] kwargs duplicated keyword arguments hash
|
78
|
-
# @option return [Proc, nil] block the original block reference (not duplicated)
|
79
|
-
#
|
80
|
-
# @example Getting registry hash
|
81
|
-
# registry.to_h
|
82
|
-
# #=> { TimeoutMiddleware => [[30], {}, nil] }
|
83
|
-
def to_h
|
84
|
-
registry.transform_values do |config|
|
85
|
-
args, kwargs, block = config
|
86
|
-
[args.dup, kwargs.dup, block]
|
87
|
-
end
|
85
|
+
recursively_call_middleware(0, task, &)
|
88
86
|
end
|
89
87
|
|
90
88
|
private
|
91
89
|
|
92
|
-
#
|
93
|
-
#
|
94
|
-
# @param
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
# @
|
99
|
-
#
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end
|
107
|
-
end
|
90
|
+
# Recursively execute middleware in the chain.
|
91
|
+
#
|
92
|
+
# @param index [Integer] Current middleware index in the chain
|
93
|
+
# @param task [Object] The task object being processed
|
94
|
+
#
|
95
|
+
# @yield [task] Block to execute after middleware processing
|
96
|
+
# @yieldparam task [Object] The processed task object
|
97
|
+
#
|
98
|
+
# @return [Object] Result of the block execution or next middleware call
|
99
|
+
def recursively_call_middleware(index, task, &block)
|
100
|
+
return yield(task) if index >= registry.size
|
101
|
+
|
102
|
+
middleware, options = registry[index]
|
103
|
+
middleware.call(task, **options) { recursively_call_middleware(index + 1, task, &block) }
|
108
104
|
end
|
109
105
|
|
110
106
|
end
|