Trailblazer 2.1: Activity, the new Operation!

Trailblazer 2.1: Activity, the new Operation!

In Trailblazer 2.1, we are introduced to a new concept: Activity.

Unlike Trailblazer 2.0, where Operation was king, activities have taken over as the main orchestrator of business logic. Good old operations are still there, but now they act as a wrapper around activities. Its one and the same, except activities are more powerful! Don’t worry though, its super easy to pick up and its backward compatibility makes switching a breaze.

So, let’s jump right in. What does an activity look like?

module Broadcasts::Cancel
  extend Trailblazer::Activity::Railway()
  
  module_function

  def set_model(ctx, broadcast_id:, **)
    ctx[:model] = Broadcast.find_by(id: broadcast_id)
  end

  def set_step_status(ctx, model:, **)
    ctx[:step] = model.action.step.first
    ctx[:step].status = 'pause'
    ctx[:step].save
  end

  def set_broadcast_status(ctx, model:, **)
    model.status = 'cancelled'
    model.save
  end

  def set_queued_emails_status(ctx, step:, **)
    step.queued_emails.update_all(status: 'cancelled', completed_at: Time.now)
  end

  def set_job_ids(ctx, action_funnel_step:, **)
    ctx[:ids] = step.queued_emails.pluck(:job_id)
  end

  step method(:set_model)
  step method(:set_step_status)
  step method(:set_broadcast_status)
  step method(:set_queued_emails_status)
  step method(:set_job_ids)
  merge!(Sidekiq::InvalidateJobs)
end

Here we have an activity that cancels a broadcast. What this code exactly does is not important for the purpose of this article, so lets just examine the activity itself:

  • Activities are contained in modules compared to class defined operations.
  • Broadcasts::Cancel module extends Trailblazer::Activity::Railway(). This is all you need to use activities.
  • module_function, a ruby function, is used to define “module methods” as if the module was a class.
  • steps are defined just the same as operations in trb 2.0, except now we use “ctx”, standing for context, instead of “options” as the first parameter.
  • merge! copies steps from another operation (Sidekiq::InvalidateJobs) and runs them as its own. Order matters!

And this is how you call the activity:

event, (ctx, *) = Broadcasts::Cancel.call(broadcast_id: params[:broadcast_id]) 
if event.to_h[:semantic] == :success 
  ... 
else 
  ... 
end
  • ctx contains all the computed information from your activity. In this example, it would contain :model, :step, and :ids
  • event contains the success or failure of the activity

That’s it! Keep in mind this is a simple operation doing a simple thing. There is much more that you can do with activities. But you get the gist of it…