Responders gem is really awesome. It sets flash message based on the controller action and resource status allowing you to dry up your Rails controllers. Trailblazer is a thin layer on top of Rails. It gently enforces encapsulation, an intuitive code structure and gives you an object-oriented architecture. When combining both of them together, you can achieve really slim and functional controllers:

class ContractsController < BaseController
  def index
    respond_with(collection Contracts::Index)
  end

  def show
    respond_with(present Contracts::Update)
  end

  def new
    respond_with(form Contracts::Create)
  end

  def create
    respond_with(run Contracts::Create)
  end

  def edit
    respond_with(form Contracts::Update)
  end

  def update
    respond_with(run Contracts::Update)
  end

  def destroy
    respond_with(run Contracts::Destroy)
  end
end

Unfortunately there’s one downside: Since Responders #respond_with method expects a model as a first argument, it will think that your operation is a model (if you return it the way I did) and try to figure out it’s human name. Probably it won’t end up as you expect and you will see a message similar to this one:

Users/campaigns/update was successfully updated.

It happens because of this piece of code from Responders:

resource_name = if resource.class.respond_to?(:model_name)
  resource.class.model_name.human
else
  resource.class.name.underscore.humanize
end

Trailblazer operations don’t respond to #model_name, that’s why all the operations will be translated using the else statement. To make it work as expected, we just need to implement the #model_name class method:

class ApplicationOperation < Trailblazer::Operation
  def self.model_name
    ::ActiveModel::Name.new(model_class)
  end
end

I follow the ApplicationController, ApplicationRecord, ApplicationHelper “pattern” also with operations. That way I don’t need to add this code into Trailblazer::Operation or into each of the operations separately. #model_class method is a method that will return the model class that we’ve set using Trailblazer::Operation #method:

class Operation < ApplicationOperation
 include Model
 model Contract
end

Operation.model_class #=> Contract
Operation.model_name #=> ActiveModel::Name:0x0000000575b778 @name="Contract"...

From now on, all the Trailblazer operations will introduce themselves to Responders with the name of the model on which we operate.