An activity is a collection of connected tasks with one start event and one (or many) end events.
To use activities you need one gem, only.
gem "trailblazer-activity"
Since TRB 2.1, we use BPMN lingo and concepts for describing workflows and processes.
An activity is a workflow that contains one or several tasks. It is the main concept to organize control flow in Trailblazer.
The following diagram illustrates an exemplary workflow where a user writes and publishes a blog post.
After writing and spell-checking, the author has the chance to publish the post or, in case of typos, go back, correct, and go through the same flow, again. Note that there's only a handful of defined transistions, or connections. An author, for example, is not allowed to jump from "correct" into "publish" without going through the check.
The activity
gem allows you to define this activity and takes care of implementing the control flow, running the activity and making sure no invalid paths are taken.
Your job is solely to implement the tasks and deciders put into this activity - Trailblazer makes sure it is executed it in the right order, and so on.
To eventually run this activity, three things have to be done.
- The activity needs be defined. Easiest is to use the Activity.from_hash builder.
- It's the programmer's job (that's you!) to implement the actual tasks (the "boxes"). Use tasks for that.
- After defining and implementing, you can run the activity with any data by
call
ing it.
An Activity
allows to define and maintain a graph, that at runtime will be used as a "circuit". Or, in other words, it defines the boxes, circles, arrows and signals between them, and makes sure when running the activity, the circuit with your rules will be executed.
Please note that an Operation
simply provides a neat DSL for creating an Activity
with a railway-oriented wiring (left and right track). An operation always maintains an activity internally.
class Create < Trailblazer::Operation
step :exists?, pass_fast: true
step :policy
step :validate
fail :log_err
step :persist
fail :log_db_err
step :notify
end
Check the operation above. The DSL to create the activity with its graph is very different to Activity
, but the outcome is a simple activity instance.
When call
ing an operation, several transformations on the arguments are applied, and those are passed to the Activity#call
invocation. After the activity finished, its output is transformed into a Result
object.
To understand how an activity works and what it performs in your application logic, it's easiest to see how activities are defined, and used.
Instead of using an operation, you can manually define activities by using the Activity.from_hash
builder.
activity = Activity.from_hash do |start, _end|
{
start => { Trailblazer::Circuit::Right => Blog::Write },
Blog::Write => { Trailblazer::Circuit::Right => Blog::SpellCheck },
Blog::SpellCheck => { Trailblazer::Circuit::Right => Blog::Publish,
Trailblazer::Circuit::Left => Blog::Correct },
Blog::Correct => { Trailblazer::Circuit::Right => Blog::SpellCheck },
Blog::Publish => { Trailblazer::Circuit::Right => _end }
}
end
The block yields a generic start and end event instance. You then connect every task in that hash (hash keys) to another task or event via the emitted signal.
To run the activity, you want to call
it.
my_options = {}
last_signal, options, flow_options, _ = activity.( nil, my_options, {} )
- The
start
event iscall
ed and per default returns the generic signalTrailblazer::Circuit::Right
. - This emitted (or returned) signal is connected to the next task
Blog::Write
, which is nowcall
ed. Blog::Write
emits anotherRight
signal that leads toBlog::SpellCheck
beingcall
ed.Blog::SpellCheck
defines two outgoing signals and hence can decide what next task to call by emitting eitherRight
if the spell check was ok, orLeft
if the post contains typos.- ...and so on.
Visualizing an activity as a graph makes it very straight-forward to understanding the mechanics of the flow.
Note how signals translate to edges (or connections) in the graph, and tasks become vertices (or nodes).
The return values are the last_signal
, which is usually the end event (they return themselves as a signal), the last options
that usually contains all kinds of data from running the activity, and additional args.
The full documentation for this gem and many more interesting things can be found on the Trailblazer website.