I created a Ruby gem called Actor. It helps you harmonize your application’s service objects.
Service objects are a way of making your web applications grow in complexity while keeping your controllers and models thin. For that you’d create classes that represent your actions, for example,
At the start, these actions might hold very little code and look a lot like a regular controller. But as your app grows, you’ll realize that something simple like creating a comment can turn into a serie of actions:
These actions deserve a way of being represented as first-class citizens in your application. That’s where actor comes into play!
With the Actor gem, your business logic is represented by a class that starts with a verb. It declares what arguments it accepts and what argument it returns. Here’s an example of creating a comment in a Ruby app:
# app/actors/create_comment.rb class CreateComment < Actor input :author input :text output :comment def call self.comment = Comment.create(author: author, text: text) end end
Now, if placing a comment takes more steps, here’s how it could look like thanks to the way Actor chains the output of the previous actors into the next when using
class PlaceComment < Actor play CreateComment, CheckCommentSpam, NotifyUserOfComment, UpdateCommentCounters, ClearCommentCacheKey, NotifyNewCommentOnSlack end
If an actor along the way encounters an error, it can call
fail! which halts the chain and allows previous successful actors a chance to rollback their changes.
Actor doesn’t depend on Rails, but Rails controllers are a good example of where you’d use your actors:
class CommentsController < ApplicationController before_action :authenticate_user! def create authorize result = PlaceComment.result(author: current_user, text: params[:text]) if result.success? redirect_to comment_path(result.comment) else redirect_to :back, alert: result.error end end end
Now your controller is only responsible for calling an actor, testing its success and deciding how to handle it. Your business logic can now be tested by itself and be called from a console, a job, or an another controller.
Actor is similar to the Interactor gem that I have been using on a number of different projects at Cults and KissKissBankBank and love the way it has helped give a common interface for all our services.
If you’re coming from Interactor, know that Actor:
< Actorvs having to
call. This way, the default is to raise an error and failures are not hidden away because you forgot to use
All this led me to create my own version, which has been smoothly running in production at Cults.
Install it by adding the following lines to your application’s Gemfile:
# Composable service objects gem 'service_actor'
Read more about the gem on Actor’s readme on GitHub. Don’t hesitate to send your questions, star the project, submit ideas as issues.