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.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.rspec +4 -0
  4. data/.rubocop.yml +64 -0
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG.md +5 -0
  7. data/CODE_OF_CONDUCT.md +132 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +76 -0
  10. data/Rakefile +12 -0
  11. data/docs/basics/call.md +31 -0
  12. data/docs/basics/context.md +67 -0
  13. data/docs/basics/run.md +34 -0
  14. data/docs/basics/setup.md +32 -0
  15. data/docs/batch.md +53 -0
  16. data/docs/configuration.md +57 -0
  17. data/docs/example.md +82 -0
  18. data/docs/getting_started.md +47 -0
  19. data/docs/hooks.md +59 -0
  20. data/docs/interruptions/exceptions.md +29 -0
  21. data/docs/interruptions/faults.md +89 -0
  22. data/docs/interruptions/halt.md +80 -0
  23. data/docs/logging.md +102 -0
  24. data/docs/outcomes/result.md +17 -0
  25. data/docs/outcomes/states.md +31 -0
  26. data/docs/outcomes/statuses.md +33 -0
  27. data/docs/parameters/coercions.md +52 -0
  28. data/docs/parameters/defaults.md +47 -0
  29. data/docs/parameters/definitions.md +123 -0
  30. data/docs/parameters/namespacing.md +57 -0
  31. data/docs/parameters/validations.md +312 -0
  32. data/docs/tips_and_tricks.md +79 -0
  33. data/lib/cmdx/.DS_Store +0 -0
  34. data/lib/cmdx/batch.rb +43 -0
  35. data/lib/cmdx/coercions/array.rb +15 -0
  36. data/lib/cmdx/coercions/big_decimal.rb +23 -0
  37. data/lib/cmdx/coercions/boolean.rb +27 -0
  38. data/lib/cmdx/coercions/complex.rb +21 -0
  39. data/lib/cmdx/coercions/date.rb +26 -0
  40. data/lib/cmdx/coercions/date_time.rb +26 -0
  41. data/lib/cmdx/coercions/float.rb +21 -0
  42. data/lib/cmdx/coercions/hash.rb +31 -0
  43. data/lib/cmdx/coercions/integer.rb +21 -0
  44. data/lib/cmdx/coercions/rational.rb +21 -0
  45. data/lib/cmdx/coercions/string.rb +15 -0
  46. data/lib/cmdx/coercions/time.rb +26 -0
  47. data/lib/cmdx/coercions/virtual.rb +15 -0
  48. data/lib/cmdx/configuration.rb +25 -0
  49. data/lib/cmdx/context.rb +15 -0
  50. data/lib/cmdx/core_ext/hash.rb +36 -0
  51. data/lib/cmdx/core_ext/module.rb +48 -0
  52. data/lib/cmdx/core_ext/object.rb +55 -0
  53. data/lib/cmdx/error.rb +23 -0
  54. data/lib/cmdx/errors.rb +92 -0
  55. data/lib/cmdx/fault.rb +47 -0
  56. data/lib/cmdx/faults.rb +11 -0
  57. data/lib/cmdx/immutator.rb +21 -0
  58. data/lib/cmdx/lazy_struct.rb +79 -0
  59. data/lib/cmdx/log_formatters/json.rb +13 -0
  60. data/lib/cmdx/log_formatters/key_value.rb +13 -0
  61. data/lib/cmdx/log_formatters/line.rb +14 -0
  62. data/lib/cmdx/log_formatters/logstash.rb +18 -0
  63. data/lib/cmdx/log_formatters/raw.rb +13 -0
  64. data/lib/cmdx/logger.rb +16 -0
  65. data/lib/cmdx/parameter.rb +101 -0
  66. data/lib/cmdx/parameter_inspector.rb +23 -0
  67. data/lib/cmdx/parameter_serializer.rb +20 -0
  68. data/lib/cmdx/parameter_validator.rb +19 -0
  69. data/lib/cmdx/parameter_value.rb +136 -0
  70. data/lib/cmdx/parameters.rb +34 -0
  71. data/lib/cmdx/parameters_inspector.rb +13 -0
  72. data/lib/cmdx/parameters_serializer.rb +13 -0
  73. data/lib/cmdx/railtie.rb +32 -0
  74. data/lib/cmdx/result.rb +170 -0
  75. data/lib/cmdx/result_inspector.rb +31 -0
  76. data/lib/cmdx/result_logger.rb +22 -0
  77. data/lib/cmdx/result_serializer.rb +38 -0
  78. data/lib/cmdx/run.rb +33 -0
  79. data/lib/cmdx/run_inspector.rb +21 -0
  80. data/lib/cmdx/run_serializer.rb +16 -0
  81. data/lib/cmdx/task.rb +151 -0
  82. data/lib/cmdx/task_hook.rb +18 -0
  83. data/lib/cmdx/utils/datetime_formatter.rb +17 -0
  84. data/lib/cmdx/utils/method_name.rb +24 -0
  85. data/lib/cmdx/utils/runtime.rb +19 -0
  86. data/lib/cmdx/validators/custom.rb +20 -0
  87. data/lib/cmdx/validators/exclusion.rb +51 -0
  88. data/lib/cmdx/validators/format.rb +27 -0
  89. data/lib/cmdx/validators/inclusion.rb +51 -0
  90. data/lib/cmdx/validators/length.rb +114 -0
  91. data/lib/cmdx/validators/numeric.rb +114 -0
  92. data/lib/cmdx/validators/presence.rb +27 -0
  93. data/lib/cmdx/version.rb +7 -0
  94. data/lib/cmdx.rb +80 -0
  95. data/lib/generators/cmdx/batch_generator.rb +30 -0
  96. data/lib/generators/cmdx/install_generator.rb +15 -0
  97. data/lib/generators/cmdx/task_generator.rb +30 -0
  98. data/lib/generators/cmdx/templates/batch.rb.tt +7 -0
  99. data/lib/generators/cmdx/templates/install.rb +23 -0
  100. data/lib/generators/cmdx/templates/task.rb.tt +9 -0
  101. data/lib/locales/en.yml +36 -0
  102. data/lib/locales/es.yml +36 -0
  103. 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)