cmdx 0.1.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 +7 -0
- data/.DS_Store +0 -0
- data/.rspec +4 -0
- data/.rubocop.yml +64 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +76 -0
- data/Rakefile +12 -0
- data/docs/basics/call.md +31 -0
- data/docs/basics/context.md +67 -0
- data/docs/basics/run.md +34 -0
- data/docs/basics/setup.md +32 -0
- data/docs/batch.md +53 -0
- data/docs/configuration.md +57 -0
- data/docs/example.md +82 -0
- data/docs/getting_started.md +47 -0
- data/docs/hooks.md +59 -0
- data/docs/interruptions/exceptions.md +29 -0
- data/docs/interruptions/faults.md +89 -0
- data/docs/interruptions/halt.md +80 -0
- data/docs/logging.md +102 -0
- data/docs/outcomes/result.md +17 -0
- data/docs/outcomes/states.md +31 -0
- data/docs/outcomes/statuses.md +33 -0
- data/docs/parameters/coercions.md +52 -0
- data/docs/parameters/defaults.md +47 -0
- data/docs/parameters/definitions.md +123 -0
- data/docs/parameters/namespacing.md +57 -0
- data/docs/parameters/validations.md +312 -0
- data/docs/tips_and_tricks.md +79 -0
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/batch.rb +43 -0
- data/lib/cmdx/coercions/array.rb +15 -0
- data/lib/cmdx/coercions/big_decimal.rb +23 -0
- data/lib/cmdx/coercions/boolean.rb +27 -0
- data/lib/cmdx/coercions/complex.rb +21 -0
- data/lib/cmdx/coercions/date.rb +26 -0
- data/lib/cmdx/coercions/date_time.rb +26 -0
- data/lib/cmdx/coercions/float.rb +21 -0
- data/lib/cmdx/coercions/hash.rb +31 -0
- data/lib/cmdx/coercions/integer.rb +21 -0
- data/lib/cmdx/coercions/rational.rb +21 -0
- data/lib/cmdx/coercions/string.rb +15 -0
- data/lib/cmdx/coercions/time.rb +26 -0
- data/lib/cmdx/coercions/virtual.rb +15 -0
- data/lib/cmdx/configuration.rb +25 -0
- data/lib/cmdx/context.rb +15 -0
- data/lib/cmdx/core_ext/hash.rb +36 -0
- data/lib/cmdx/core_ext/module.rb +48 -0
- data/lib/cmdx/core_ext/object.rb +55 -0
- data/lib/cmdx/error.rb +23 -0
- data/lib/cmdx/errors.rb +92 -0
- data/lib/cmdx/fault.rb +47 -0
- data/lib/cmdx/faults.rb +11 -0
- data/lib/cmdx/immutator.rb +21 -0
- data/lib/cmdx/lazy_struct.rb +79 -0
- data/lib/cmdx/log_formatters/json.rb +13 -0
- data/lib/cmdx/log_formatters/key_value.rb +13 -0
- data/lib/cmdx/log_formatters/line.rb +14 -0
- data/lib/cmdx/log_formatters/logstash.rb +18 -0
- data/lib/cmdx/log_formatters/raw.rb +13 -0
- data/lib/cmdx/logger.rb +16 -0
- data/lib/cmdx/parameter.rb +101 -0
- data/lib/cmdx/parameter_inspector.rb +23 -0
- data/lib/cmdx/parameter_serializer.rb +20 -0
- data/lib/cmdx/parameter_validator.rb +19 -0
- data/lib/cmdx/parameter_value.rb +136 -0
- data/lib/cmdx/parameters.rb +34 -0
- data/lib/cmdx/parameters_inspector.rb +13 -0
- data/lib/cmdx/parameters_serializer.rb +13 -0
- data/lib/cmdx/railtie.rb +32 -0
- data/lib/cmdx/result.rb +170 -0
- data/lib/cmdx/result_inspector.rb +31 -0
- data/lib/cmdx/result_logger.rb +22 -0
- data/lib/cmdx/result_serializer.rb +38 -0
- data/lib/cmdx/run.rb +33 -0
- data/lib/cmdx/run_inspector.rb +21 -0
- data/lib/cmdx/run_serializer.rb +16 -0
- data/lib/cmdx/task.rb +151 -0
- data/lib/cmdx/task_hook.rb +18 -0
- data/lib/cmdx/utils/datetime_formatter.rb +17 -0
- data/lib/cmdx/utils/method_name.rb +24 -0
- data/lib/cmdx/utils/runtime.rb +19 -0
- data/lib/cmdx/validators/custom.rb +20 -0
- data/lib/cmdx/validators/exclusion.rb +51 -0
- data/lib/cmdx/validators/format.rb +27 -0
- data/lib/cmdx/validators/inclusion.rb +51 -0
- data/lib/cmdx/validators/length.rb +114 -0
- data/lib/cmdx/validators/numeric.rb +114 -0
- data/lib/cmdx/validators/presence.rb +27 -0
- data/lib/cmdx/version.rb +7 -0
- data/lib/cmdx.rb +80 -0
- data/lib/generators/cmdx/batch_generator.rb +30 -0
- data/lib/generators/cmdx/install_generator.rb +15 -0
- data/lib/generators/cmdx/task_generator.rb +30 -0
- data/lib/generators/cmdx/templates/batch.rb.tt +7 -0
- data/lib/generators/cmdx/templates/install.rb +23 -0
- data/lib/generators/cmdx/templates/task.rb.tt +9 -0
- data/lib/locales/en.yml +36 -0
- data/lib/locales/es.yml +36 -0
- metadata +288 -0
data/docs/example.md
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# Example
|
2
|
+
|
3
|
+
The following is a full example of task that is commonly implemented.
|
4
|
+
|
5
|
+
## Setup
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
class ProcessOrderTask < CMDx::Task
|
9
|
+
|
10
|
+
required :order
|
11
|
+
required :billing_details, :shipping_details, from: :order
|
12
|
+
|
13
|
+
optional :package do
|
14
|
+
required :delivery_company, default: -> { Current.account.premium? ? "UPS" : "DHL" }
|
15
|
+
optional :weight, :volume, type: :float
|
16
|
+
end
|
17
|
+
|
18
|
+
after_execution :track_metrics
|
19
|
+
|
20
|
+
def call
|
21
|
+
if cart_abandoned?
|
22
|
+
skip!(reason: "Cart was abandoned due to 30 days of inactivity")
|
23
|
+
elsif cart_items_out_of_stock?
|
24
|
+
fail!(reason: "Items in the cart are out of stock", item: [123, 987])
|
25
|
+
else
|
26
|
+
charge_payment_method
|
27
|
+
ship_order_packages
|
28
|
+
send_confirmation_email
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def charge_payment_method
|
35
|
+
@charge_payment_method ||= ChargePaymentMethodTask.call(details: billing_details)
|
36
|
+
end
|
37
|
+
|
38
|
+
def ship_order_packages
|
39
|
+
@ship_order_packages ||= ShipOrderPackagesTask.call(details: shipping_details)
|
40
|
+
end
|
41
|
+
|
42
|
+
def send_confirmation_email
|
43
|
+
return if charge_payment_method.failed? || ship_order_packages.bad?
|
44
|
+
|
45
|
+
BatchSendConfirmationNotifications.call(context)
|
46
|
+
end
|
47
|
+
|
48
|
+
def track_metrics
|
49
|
+
if Rails.env.development?
|
50
|
+
logger.debug { "Sending metrics to collector" }
|
51
|
+
else
|
52
|
+
TrackMetricsTask.call(metric: :process_order, status: order.status)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
## Execution
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
class OrdersController < ApplicationController
|
63
|
+
|
64
|
+
def create
|
65
|
+
task = ProcessOrderTask.call(order_params)
|
66
|
+
|
67
|
+
if task.success?
|
68
|
+
flash[:success] = "Order is on its way!"
|
69
|
+
redirect_to(my_orders_path)
|
70
|
+
else
|
71
|
+
flash[:error] = "Whoops something is wrong: #{task.metadata[:reason]}"
|
72
|
+
render(:new)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
---
|
80
|
+
|
81
|
+
- **Prev:** [Tips & Tricks](https://github.com/drexed/cmdx/blob/main/docs/tips_and_tricks.md)
|
82
|
+
- **Next:** [Getting Started](https://github.com/drexed/cmdx/blob/main/docs/getting_started.md)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Getting Start
|
2
|
+
|
3
|
+
`CMDx` is a framework for expressive processing of business logic.
|
4
|
+
|
5
|
+
Goals:
|
6
|
+
- Provide easy branching, nesting, and composition of complex tasks
|
7
|
+
- Supply intent, severity, and reasoning to halting execution of tasks
|
8
|
+
- Demystify root causes of complex multi-level tasks with exhaustive tracing
|
9
|
+
|
10
|
+
## Setup
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
class ProcessOrderTask < CMDx::Task
|
14
|
+
|
15
|
+
def call
|
16
|
+
fail!(reason: "Order was canceled") if context.order.canceled?
|
17
|
+
skip!(reason: "Order is processing") if context.order.processing?
|
18
|
+
|
19
|
+
inform_partner_warehouses
|
20
|
+
send_confirmation_email
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
## Execution
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
result = ProcessOrderTask.call(order: order)
|
30
|
+
```
|
31
|
+
|
32
|
+
## Result
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
if result.failed?
|
36
|
+
flash[:error] = "Failed! #{result.metadata[:reason]}"
|
37
|
+
elsif result.skipped?
|
38
|
+
flash[:notice] = "FYI: #{result.metadata[:reason]}"
|
39
|
+
else
|
40
|
+
flash[:success] = "Order successfully processed"
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
---
|
45
|
+
|
46
|
+
- **Prev:** [Example](https://github.com/drexed/cmdx/blob/main/docs/example.md)
|
47
|
+
- **Next:** [Configuration](https://github.com/drexed/cmdx/blob/main/docs/configuration.md)
|
data/docs/hooks.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Hooks
|
2
|
+
|
3
|
+
Hooks (callbacks) run logic at task transition points. Callable hooks have access
|
4
|
+
to all the same information as the `call` method.
|
5
|
+
|
6
|
+
> [!TIP]
|
7
|
+
> Hooks are inheritable which is handy for setting up global logic execution,
|
8
|
+
> eg: setting tracking markers, account plan checks, etc.
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
class ProcessOrderTask < CMDx::Task
|
12
|
+
|
13
|
+
# Symbol or string declaration:
|
14
|
+
after_validation :verify_message_starting_chars
|
15
|
+
|
16
|
+
# Proc or lambda declaration:
|
17
|
+
on_complete -> { send_telemetry_data }
|
18
|
+
|
19
|
+
# Multiple declarations:
|
20
|
+
on_success :increment_success_task_counter, :scrub_secret_message_data
|
21
|
+
|
22
|
+
# With options (applies to all declared in that group):
|
23
|
+
on_failure :increment_failure_task_counter, if: :worth_keep_track?
|
24
|
+
|
25
|
+
def call
|
26
|
+
# Do work...
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
```
|
31
|
+
|
32
|
+
The hook methods support the following options:
|
33
|
+
|
34
|
+
| Option | Description |
|
35
|
+
| ------------- | ----------- |
|
36
|
+
| `:if` | Specifies a callable method, proc or string to determine if hook processing should occur. |
|
37
|
+
| `:unless` | Specifies a callable method, proc, or string to determine if hook processing should not occur. |
|
38
|
+
|
39
|
+
## Order
|
40
|
+
|
41
|
+
Hook types are executed in the following order:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
1. before_execution
|
45
|
+
2. on_executing
|
46
|
+
3. before_validation
|
47
|
+
4. after_validation
|
48
|
+
5. on_[success, skipped, failed]
|
49
|
+
6. on_[complete, interrupted]
|
50
|
+
7. after_execution
|
51
|
+
```
|
52
|
+
|
53
|
+
> [!IMPORTANT]
|
54
|
+
> Callable hooks are executed in the order they are declared (FIFO: first in, first out).
|
55
|
+
|
56
|
+
---
|
57
|
+
|
58
|
+
- **Prev:** [Outcomes - States](https://github.com/drexed/cmdx/blob/main/docs/outcomes/states.md)
|
59
|
+
- **Next:** [Batch](https://github.com/drexed/cmdx/blob/main/docs/batch.md)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Interruptions - Exceptions
|
2
|
+
|
3
|
+
## Non-bang call
|
4
|
+
|
5
|
+
Any unhandled exception will be caught and halted using `fail!`.
|
6
|
+
The original exception will be passed as metadata of the result object.
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
result = ProcessOrderTask.call
|
10
|
+
result.state #=> "interrupted"
|
11
|
+
result.status #=> "failed"
|
12
|
+
result.metadata #=> {
|
13
|
+
#=> reason: "[RuntimeError] method xyz is not defined",
|
14
|
+
#=> original_exception: <RuntimeError message="method xyz is not defined">
|
15
|
+
#=> }
|
16
|
+
```
|
17
|
+
|
18
|
+
## Bang call
|
19
|
+
|
20
|
+
Any unhandled exception from a `call!` method will be raised as is.
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
ProcessOrderTask.call! #=> raises NoMethodError, "method xyz is not defined"
|
24
|
+
```
|
25
|
+
|
26
|
+
---
|
27
|
+
|
28
|
+
- **Prev:** [Interruptions - Faults](https://github.com/drexed/cmdx/blob/main/docs/interruptions/faults.md)
|
29
|
+
- **Next:** [Outcomes - Result](https://github.com/drexed/cmdx/blob/main/docs/outcomes/result.md)
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# Interruptions - Faults
|
2
|
+
|
3
|
+
Faults are the mechanisms by which `CMDx` goes about halting execution of tasks
|
4
|
+
via the `skip!` and `fail!` methods. When tasks are executed with bang `call!` method,
|
5
|
+
a fault exception that matches the current task status will be raised.
|
6
|
+
|
7
|
+
## Rescue
|
8
|
+
|
9
|
+
Use the standard Ruby `rescue` method to handle any faults with custom logic.
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
begin
|
13
|
+
ProcessOrderTask.call!
|
14
|
+
rescue CMDx::Skipped
|
15
|
+
# Do work on any skipped tasks
|
16
|
+
rescue CMDx::Failed
|
17
|
+
# Do work on any failed tasks
|
18
|
+
rescue CMDx::Fault
|
19
|
+
# Do work on any skipped or failed tasks
|
20
|
+
end
|
21
|
+
```
|
22
|
+
|
23
|
+
## For
|
24
|
+
|
25
|
+
Faults can be matched for the task that caused it.
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
begin
|
29
|
+
ProcessOrderTask.call!
|
30
|
+
rescue CMDx::Skipped.for?(ProcessOrderTask, DeliverOrderTask)
|
31
|
+
# Do work on just skipped ProcessOrderTask or DeliverOrderTask tasks
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
## Matches
|
36
|
+
|
37
|
+
Faults allow advance rescue matching with access to the underlying task internals.
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
begin
|
41
|
+
ProcessOrderTask.call!
|
42
|
+
rescue CMDx::Fault.matches? { |f| f.result.metadata[:reason].includes?("out of stock") }
|
43
|
+
# Do work on any skipped or failed tasks that have `:reason` metadata equals "out of stock"
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
> [!IMPORTANT]
|
48
|
+
> All fault exceptions have access to the `for?` and `matches?` methods.
|
49
|
+
|
50
|
+
## Throw
|
51
|
+
|
52
|
+
Throw the result of subtasks to bubble up fault as its own. Throwing will use the
|
53
|
+
subtask results' status and metadata to create a matching halt on the parent task.
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
class ProcessOrderTask < CMDx::Task
|
57
|
+
|
58
|
+
def call
|
59
|
+
throw!(SendConfirmationNotificationsTask.call)
|
60
|
+
|
61
|
+
# Do other work...
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
result = ProcessOrderTask.call
|
67
|
+
result.state #=> "interrupted"
|
68
|
+
result.status #=> "skipped"
|
69
|
+
result.metadata #=> { reason: "Order confirmation could not be sent due to invalid email." }
|
70
|
+
```
|
71
|
+
|
72
|
+
> [!NOTE]
|
73
|
+
> `throw!` will bubble any skipped and failed results. To only throw skipped results, just add
|
74
|
+
> a conditional for the specific status.
|
75
|
+
|
76
|
+
## Results
|
77
|
+
|
78
|
+
The following represents a result output example of a thrown fault.
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
result = ProcessOrderTask.call
|
82
|
+
result.threw_failure #=> <CMDx::Result[SendConfirmationNotificationsTask] ...>
|
83
|
+
result.caused_failure #=> <CMDx::Result[DeliverEmailTask] ...>
|
84
|
+
```
|
85
|
+
|
86
|
+
---
|
87
|
+
|
88
|
+
- **Prev:** [Interruptions - Halt](https://github.com/drexed/cmdx/blob/main/docs/interruptions/halt.md)
|
89
|
+
- **Next:** [Interruptions - Exceptions](https://github.com/drexed/cmdx/blob/main/docs/interruptions/exceptions.md)
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# Interruptions - Halt
|
2
|
+
|
3
|
+
Halting stops execution of a task. Halt methods signal the intent as to why a task
|
4
|
+
is stopped executing.
|
5
|
+
|
6
|
+
## Skip
|
7
|
+
|
8
|
+
The `skip!` method indicates that a task did not meet the criteria to continue execution.
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
class ProcessOrderTask < CMDx::Task
|
12
|
+
|
13
|
+
def call
|
14
|
+
skip! if cart_abandoned?
|
15
|
+
|
16
|
+
# Do work...
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
```
|
21
|
+
|
22
|
+
## Fail
|
23
|
+
|
24
|
+
The `fail!` method indicates that a task met with incomplete, broken, or failed logic.
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
class ProcessOrderTask < CMDx::Task
|
28
|
+
|
29
|
+
def call
|
30
|
+
fail! if cart_items_out_of_stock?
|
31
|
+
|
32
|
+
# Do work...
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
## Metadata
|
39
|
+
|
40
|
+
Pass metadata to enrich faults with additional contextual information. Metadata requires
|
41
|
+
that it be passed as a hash object. Internal failures will hydrate metadata into its result,
|
42
|
+
eg: failed validations and unrescued exceptions.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
class ProcessOrderTask < CMDx::Task
|
46
|
+
|
47
|
+
def call
|
48
|
+
if cart_abandoned?
|
49
|
+
skip!(reason: "Cart was abandoned due to 30 days of inactivity")
|
50
|
+
elsif cart_items_out_of_stock?
|
51
|
+
fail!(reason: "Items in the cart are out of stock", item: [123, 987])
|
52
|
+
else
|
53
|
+
# Do work...
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
result = ProcessOrderTask.call
|
60
|
+
result.metadata #=> { reason: "Items in the cart are out of stock", item: [123, 987] }
|
61
|
+
```
|
62
|
+
|
63
|
+
> [!Important]
|
64
|
+
> The `:reason` key is used to define the fault exception message. While not
|
65
|
+
> required, it is strongly recommended that it is used on every halt method.
|
66
|
+
|
67
|
+
## Results
|
68
|
+
|
69
|
+
The following represents a result output example of a halted task.
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
result = ProcessOrderTask.call
|
73
|
+
result.status #=> "failed"
|
74
|
+
result.metadata #=> { reason: "Cart was abandoned due to 30 days of inactivity" }
|
75
|
+
```
|
76
|
+
|
77
|
+
---
|
78
|
+
|
79
|
+
- **Prev:** [Basics - Run](https://github.com/drexed/cmdx/blob/main/docs/basics/run.md)
|
80
|
+
- **Next:** [Interruptions - Faults](https://github.com/drexed/cmdx/blob/main/docs/interruptions/faults.md)
|
data/docs/logging.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# Logging
|
2
|
+
|
3
|
+
Tasks log the result object after execution. Multi-threaded systems will have many
|
4
|
+
tasks executing concurrently so `CMDx` uses a custom logger to make debugging easier.
|
5
|
+
|
6
|
+
## Output
|
7
|
+
|
8
|
+
Built-in log formatters are: `Line` (default), `Json`, `KeyValue`, `Logstash`, `Raw`
|
9
|
+
|
10
|
+
#### Success:
|
11
|
+
```txt
|
12
|
+
I, [2022-07-17T18:43:15.000000 #3784] INFO -- CMDx: index=0 run_id=018c2b95-b764-7615-a924-cc5b910ed1e5 type=Task class=SimulationTask id=018c2b95-b764-7615-a924-cc5b910ed1e5 state=complete status=success outcome=success metadata={} runtime=0 tags=[] pid=3784
|
13
|
+
```
|
14
|
+
|
15
|
+
#### Skipped:
|
16
|
+
```txt
|
17
|
+
W, [2022-07-17T18:43:15.000000 #3784] WARN -- CMDx: index=0 run_id=018c2b95-b764-7615-a924-cc5b910ed1e5 type=Task class=SimulationTask id=018c2b95-b764-7615-a924-cc5b910ed1e5 state=interrupted status=skipped outcome=skipped metadata={} runtime=0 tags=[] pid=3784
|
18
|
+
```
|
19
|
+
|
20
|
+
#### Failed:
|
21
|
+
```txt
|
22
|
+
E, [2022-07-17T18:43:15.000000 #3784] ERROR -- CMDx: index=0 run_id=018c2b95-b764-7615-a924-cc5b910ed1e5 type=Task class=SimulationTask id=018c2b95-b764-7615-a924-cc5b910ed1e5 state=interrupted status=failed outcome=failed metadata={} runtime=0 tags=[] pid=3784
|
23
|
+
```
|
24
|
+
|
25
|
+
#### Level 1 subtask failure:
|
26
|
+
```txt
|
27
|
+
E, [2022-07-17T18:43:15.000000 #3784] ERROR -- CMDx: index=0 run_id=018c2b95-b764-7615-a924-cc5b910ed1e5 type=Task class=SimulationTask id=018c2b95-b764-7615-a924-cc5b910ed1e5 state=interrupted status=failed outcome=interrupted metadata={} runtime=0 tags=[] pid=3784 caused_failure={:index=>1, :run_id=>"018c2b95-b764-7615-a924-cc5b910ed1e5", :type=>"Task", :class=>"SimulationTask", :id=>"018c2b95-b764-7615-a924-cc5b910ed1e5", :state=>"interrupted", :status=>"failed", :outcome=>"failed", :metadata=>{}, :runtime=>0, :tags=>[], :pid=>3784} threw_failure={:index=>1, :run_id=>"018c2b95-b764-7615-a924-cc5b910ed1e5", :type=>"Task", :class=>"SimulationTask", :id=>"018c2b95-b764-7615-a924-cc5b910ed1e5", :state=>"interrupted", :status=>"failed", :outcome=>"failed", :metadata=>{}, :runtime=>0, :tags=>[], :pid=>3784}
|
28
|
+
```
|
29
|
+
|
30
|
+
#### Level 2+ subtask failure:
|
31
|
+
```txt
|
32
|
+
E, [2022-07-17T18:43:15.000000 #3784] ERROR -- CMDx: index=0 run_id=018c2b95-b764-7615-a924-cc5b910ed1e5 type=Task class=SimulationTask id=018c2b95-b764-7615-a924-cc5b910ed1e5 state=interrupted status=failed outcome=interrupted metadata={} runtime=0 tags=[] pid=3784 caused_failure={:index=>2, :run_id=>"018c2b95-b764-7615-a924-cc5b910ed1e5", :type=>"Task", :class=>"SimulationTask", :id=>"018c2b95-b764-7615-a924-cc5b910ed1e5", :state=>"interrupted", :status=>"failed", :outcome=>"failed", :metadata=>{}, :runtime=>0, :tags=>[], :pid=>3784} threw_failure={:index=>1, :run_id=>"018c2b95-b764-7615-a924-cc5b910ed1e5", :type=>"Task", :class=>"SimulationTask", :id=>"018c2b95-b764-7615-a924-cc5b910ed1e5", :state=>"interrupted", :status=>"failed", :outcome=>"interrupted", :metadata=>{}, :runtime=>0, :tags=>[], :pid=>3784}
|
33
|
+
```
|
34
|
+
|
35
|
+
## Logger
|
36
|
+
|
37
|
+
CMDx defaults to using Ruby's standard library Logger. Log levels thus follow the
|
38
|
+
[stdlib documentation](http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger.html).
|
39
|
+
|
40
|
+
#### Global settings:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
CMDx.configure do |config|
|
44
|
+
# Single declaration:
|
45
|
+
config.logger = Logger.new($stdout, formatter: CMDx::LogFormatters::Line.new, level: Logger::DEBUG)
|
46
|
+
|
47
|
+
# Multiline declarations:
|
48
|
+
config.logger = Rails.logger
|
49
|
+
config.logger.formatter = CMDx::LogFormatters::Line.new
|
50
|
+
config.logger.level = Logger::WARN
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
#### Task settings:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
class ProcessOrderTask < CMDx::Task
|
58
|
+
|
59
|
+
task_settings!(logger: Rails.logger, log_format: CMDx::LogFormatters::Logstash.new, log_level: Logger::WARN)
|
60
|
+
|
61
|
+
def call
|
62
|
+
# Do work...
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
> [!TIP]
|
69
|
+
> In production environments, a log level of DEBUG may be too verbose for your needs.
|
70
|
+
> For quieter logs that use less disk space, you can change the log level to only show INFO and higher.
|
71
|
+
|
72
|
+
## Write to log
|
73
|
+
|
74
|
+
Write to log via the `logger` method.
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
class ProcessOrderTask < CMDx::Task
|
78
|
+
|
79
|
+
def call
|
80
|
+
logger.info "Processing order"
|
81
|
+
logger.debug { context.to_h }
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
## Output format
|
88
|
+
|
89
|
+
Define a custom log formatter to match your expected output, for example one that changes the JSON keys:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
class CustomCmdxLogFormat
|
93
|
+
def call(severity, time, progname, message)
|
94
|
+
# Return string, hash, array, etc to output...
|
95
|
+
end
|
96
|
+
end
|
97
|
+
```
|
98
|
+
|
99
|
+
---
|
100
|
+
|
101
|
+
- **Prev:** [Batch](https://github.com/drexed/cmdx/blob/main/docs/batch.md)
|
102
|
+
- **Next:** [Tips & Tricks](https://github.com/drexed/cmdx/blob/main/docs/tips_and_tricks.md)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Outcomes - Result
|
2
|
+
|
3
|
+
The result object is returned after a task execution. This is the main object
|
4
|
+
that will be interacted with post call.
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
result = ProcessOrderTask.call
|
8
|
+
result.task #=> <ProcessOrderTask ...>
|
9
|
+
result.context #=> <CMDx::Context ...>
|
10
|
+
result.metadata #=> { ... }
|
11
|
+
result.run #=> <CMDx::Run ...>
|
12
|
+
```
|
13
|
+
|
14
|
+
---
|
15
|
+
|
16
|
+
- **Prev:** [Interruptions - Exceptions](https://github.com/drexed/cmdx/blob/main/docs/interruptions/exceptions.md)
|
17
|
+
- **Next:** [Outcomes - Statuses](https://github.com/drexed/cmdx/blob/main/docs/outcomes/statuses.md)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Outcomes - States
|
2
|
+
|
3
|
+
State represents the condition of all the code task should execute.
|
4
|
+
|
5
|
+
| Status | Description |
|
6
|
+
| ------------- | ----------- |
|
7
|
+
| `initialized` | Initial task state prior to any execution. |
|
8
|
+
| `executing` | Task is actively executing code. |
|
9
|
+
| `complete` | Task executed to completion without halting for any reason. |
|
10
|
+
| `interrupted` | Task could **NOT** be executed to completion due to a fault/exception. |
|
11
|
+
|
12
|
+
> [!CAUTION]
|
13
|
+
> States are automatically transitioned and should **NEVER** be altered manually.
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
result = ProcessOrderTask.call
|
17
|
+
result.state #=> "complete"
|
18
|
+
|
19
|
+
result.pending? #=> false
|
20
|
+
result.executing? #=> false
|
21
|
+
result.complete? #=> true
|
22
|
+
result.interrupted? #=> false
|
23
|
+
|
24
|
+
# `complete` or `interrupted`
|
25
|
+
result.executed?
|
26
|
+
```
|
27
|
+
|
28
|
+
---
|
29
|
+
|
30
|
+
- **Prev:** [Outcomes - Statuses](https://github.com/drexed/cmdx/blob/main/docs/outcomes/statuses.md)
|
31
|
+
- **Next:** [Hooks](https://github.com/drexed/cmdx/blob/main/docs/hooks.md)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Outcomes - Statuses
|
2
|
+
|
3
|
+
Status represents the state of the task logic executed after its called.
|
4
|
+
A status of `success` is returned even if the task has **NOT** been executed.
|
5
|
+
|
6
|
+
| Status | Description |
|
7
|
+
| --------- | ----------- |
|
8
|
+
| `success` | Call execution completed without fault/exception. |
|
9
|
+
| `skipped` | Task stopped completion of call execution early where proceeding is pointless. |
|
10
|
+
| `failed` | Task stopped completion of call execution due to an unsatisfied/invalid condition or a `StandardError`. |
|
11
|
+
|
12
|
+
> [!NOTE]
|
13
|
+
> Statuses (except success) are paired with halt methods used to stop call execution.
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
result = ProcessOrderTask.call
|
17
|
+
result.status #=> "skipped"
|
18
|
+
|
19
|
+
result.success? #=> false
|
20
|
+
result.skipped? #=> true
|
21
|
+
result.failed? #=> false
|
22
|
+
|
23
|
+
# `success` or `skipped`
|
24
|
+
result.good? #=> true
|
25
|
+
|
26
|
+
# `skipped` or `failed`
|
27
|
+
result.bad? #=> true
|
28
|
+
```
|
29
|
+
|
30
|
+
---
|
31
|
+
|
32
|
+
- **Prev:** [Outcomes - Result](https://github.com/drexed/cmdx/blob/main/docs/outcomes/result.md)
|
33
|
+
- **Next:** [Outcomes - States](https://github.com/drexed/cmdx/blob/main/docs/outcomes/states.md)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Parameters - Coercions
|
2
|
+
|
3
|
+
Parameter values will be returned as given (`type: :virtual`) but can be coerced (typecast)
|
4
|
+
to a specific type. This is useful for casting stringified parameter values.
|
5
|
+
|
6
|
+
Supported coercions are: `:array`, `:big_decimal`, `:boolean`, `:complex`, `:datetime`, `:date`,
|
7
|
+
`:float`, `:hash`, `:integer`, `:rational`, `:string`, `:time`, `:virtual`
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
class DetermineBoxSizeTask < CMDx::Task
|
11
|
+
|
12
|
+
# Single type
|
13
|
+
required :width, type: :string
|
14
|
+
|
15
|
+
# Multiple types
|
16
|
+
optional :height, type: [:float, :integer]
|
17
|
+
|
18
|
+
def call
|
19
|
+
width #=> "1"
|
20
|
+
height #=> 2.3
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
# Coerced to value types
|
26
|
+
DetermineBoxSizeTask.call(width: 1, height: "2.3")
|
27
|
+
```
|
28
|
+
|
29
|
+
> [!NOTE]
|
30
|
+
> When passing multiple type, coercions are done in the order they are passed. An example of
|
31
|
+
> numeric casting would be to cast numbers with precision first: `[:float, :integer]`
|
32
|
+
|
33
|
+
## Results
|
34
|
+
|
35
|
+
The following represents a result output example of a failed coercion.
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
result = DetermineBoxSizeTask.call
|
39
|
+
result.state #=> "interrupted"
|
40
|
+
result.status #=> "failed"
|
41
|
+
result.metadata #=> {
|
42
|
+
#=> reason: "height could not be coerced into one of: float, integer.",
|
43
|
+
#=> messages: {
|
44
|
+
#=> height: ["could not be coerced into one of: float, integer"]
|
45
|
+
#=> }
|
46
|
+
#=> }
|
47
|
+
```
|
48
|
+
|
49
|
+
---
|
50
|
+
|
51
|
+
- **Prev:** [Namespacing](https://github.com/drexed/cmdx/blob/main/docs/parameters/namespacing.md)
|
52
|
+
- **Next:** [Validations](https://github.com/drexed/cmdx/blob/main/docs/parameters/validations.md)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Parameters - Defaults
|
2
|
+
|
3
|
+
Assign default values for parameters that return a `nil` value.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
class DetermineBoxSizeTask < CMDx::Task
|
7
|
+
|
8
|
+
# Fixed value
|
9
|
+
required :width, default: 12
|
10
|
+
|
11
|
+
# Proc or lambda
|
12
|
+
optional :length, default: -> { Current.account.usa? ? 12 : 18 }
|
13
|
+
|
14
|
+
# Symbol or string
|
15
|
+
optional :depth, default: :depth_by_country
|
16
|
+
|
17
|
+
def call
|
18
|
+
width #=> 12
|
19
|
+
length #=> 18
|
20
|
+
depth #=> 48
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def depth_by_country
|
26
|
+
case Current.account.country
|
27
|
+
when "usa" then 12
|
28
|
+
when "can" then 18
|
29
|
+
when "mex" then 24
|
30
|
+
else 48
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
# Initializes with default values
|
37
|
+
DetermineBoxSizeTask.call(width: nil, length: nil)
|
38
|
+
```
|
39
|
+
|
40
|
+
> [!NOTE]
|
41
|
+
> Defaults are subject to coercion and validations so take care setting
|
42
|
+
> the fallback value to contain valid conditions.
|
43
|
+
|
44
|
+
---
|
45
|
+
|
46
|
+
- **Prev:** [Validations](https://github.com/drexed/cmdx/blob/main/docs/parameters/validations.md)
|
47
|
+
- **Next:** [Results](https://github.com/drexed/cmdx/blob/main/docs/outcomes.md)
|