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.