Action Handler
ActionHandler is a Rails plugin that helps you write controller functionalities in clean and testable way.
What is a handler?
A handler is a controller-like class. Each public method can be an action method.
But unlike controllers, handlers inherit few methods by default.
Instead of using super class methods such as params, sessions, you can take them as arguments.
And you need to represent a response (data for views) as a single return value,
instead of assigning multiple instance variables.
# Example
class UsersHandler
include ActionHandler::Equip # (optional)
def initialize(ranking: RankingService.new)
@ranking = ranking
end
# Get request information via arguments.
def index(params, cookies)
puts "cookies: #{cookies}"
users = User.order(:id).page(params[:page])
ranks = @ranking.generate(users)
# Return a hash. It will be passed to controller's `render`.
{ locals: { users: users, ranks: ranks } }
end
# Define custom argument.
arg(:user_id) do |ctrl|
ctrl.params[:id]
end
def show(user_id, format)
user = User.find(user_id)
# `render` is available as well. It just returns a given hash.
if format == :html
render locals: { user: user }
else
render json: user
end
end
# ...
endFeatures
Clean and clear structure
- In handlers, action methods take necessary inputs as arguments and represent output as a return value. So easy to read and test.
- Handler is just a class, so you can set up any dependencies via
initializemethod.
Automatic arguments injection
- ActionHandler automatically injects common controller properties (
params,request, etc) just by declaring them as action method's arguments. - You can define custom injectable arguments as well.
This feature is heavily inspired by ActionArgs.
Motivation
- Testability is important as much or more than test itself.
- Prefer clarity and simplicity over code shortness and easiness, for future maintainability.
Getting Started
Installation:
gem install action_handlerA simplest handler is a plain class with no super classes.
class UsersHandler
def index(params)
users = User.order(:id).page(params[:page])
{ locals: { users: users } }
end
def show(params)
user = User.find(params[:id])
{ locals: { user: user } }
end
endTo use this handler, register it in a controller.
class UsersController < ApplicationController
include ActionHandler::Controller
use_handler { UsersHandler.new }
endThis makes the controller implement same name action methods. Now you can define routes to this controller as usual.
resources :users, only: %i[index show]Though you can use plain handler classes, usually you need to include ActionHandler::Equip module
to use basic controller functionalities like redirect_to or custom arguments.