Finally, a canonical implementation for invoking operations and activities.
Allows to configure injected variables like flow_options (for instance, to control which operation should use tracing, or to use ctx aliases.)
Provides a matcher block syntax to easily handle different operation outcomes.
This is mostly used internally, but the new trailblazer-rails controller layer will expose the optional matcher interface.
NOTE: this is still WIP.
Set default options using the block for module!.
# config/initializers/trailblazer.rb
require "trailblazer/invoke"
Trailblazer::Invoke.module!(self) do |activity, options, **|
# Return a hash that provides default options for invoking an operation.
# For example, automatically trace operations when the {WTF} environment variable is set.
options = ENV[:WTF] ? {invoke_method: Trailblazer::Developer::Wtf.method(:invoke)} : {}
# Or, set a "global" ctx alias for "contract.default".
{
flow_options: {
context_options: {
aliases: {"contract.default": :contract},
container_class: Trailblazer::Context::Container::WithAliases,
}
},
**options,
}
endUse the "canonical invoke" to run operations and activities.
signal, (ctx,) = __(Memo::Operation::Create, params: params)
ctx[:contract] #=> <Reform::Form ...>The operation will now be run with the options configured via module!, allowing you to configure it to use aliasing, tracing, and whatever else you need.
Note that in future versions we will make __() an internal concept, as Operation.call will simply use the canonical invoke.
The canonical invoke provides an optional block to handle outcomes.
signal, (ctx,) = __(Memo::Operation::Create, params: params) do
success { |ctx, model:, **| render model }
failure { |ctx, contract:, **| ... }
not_found { ... }
endbefore this, all gems had to override public_call etc, ordering problems, issues with activity vs OP, etc. this also allows for compiling options like flow_options and present_options, invoke_method, etc gems like pro can now in a central place, add their options on each OP call.
invoke is about "centralizing" how OPs and activities are called, configuring what gets injected, and provides a matcher block syntax
E.g. when you want to have a "global" wtf? ENV variable
Basically invoke provides a "central" method to invoke OPs, and some super simple mechanism to compile various options for each call (like tracing yes/no)
invoke compiles its own container_activity and uses the TaskWrap.invoke( container_activity: ) option.
idea is to have an "endpoint" for running activities, like a "meta activity", so everything is consistent.
we also create the Context instance
DISCUSS: don't override call to plug in another wrap_runtime, use an Options step.
- invoking an OP is all about passing options
- this used to be quite "messy", wtf calling trace calling TaskWrap.invoke, manually merging options etc, plus your own monkey-patches
- options merging is now done by options-compiler
- TODO: merging {:wrap_runtime}.
- invoking the actual activity is always TaskWrap.invoke
- also supports block/matcher syntax