Service Object in Rails Using SimpleCommand Gem.

While continuing on working on my skills and developing applications on ruby I got introduce to the service object model. The service object model helps you reduce the complexity of your controllers and code repetition.

The job of a service object is to encapsulate functionality, execute one service, and provide a single point of failure. A service object is just a Plain Old Ruby Object (“PORO”) that follows the following structure.

  • Has an initialization method with a params argument.
  • Has a single public method named call.
  • Returns a payload or an error.

SimpleCommand is a Ruby Gem that makes it simple to create Service Objects. The basic structure of a Service Object Class using Simple Command is as follows.

# define a command class
class AuthenticateUser
# put SimpleCommand before the class' ancestors chain
prepend SimpleCommand


# optional, initialize the command with some arguments
def initialize(params...)
##...
end

# mandatory: define a #call method. its return value will be available
#
def call
# logic .... returns payload or error
end
end

With this, we can create an example to reduce logic in the controller. Let’s observe an example of a login method and how the controller code looks cleaner. An example to use Simplecommand Service Object to authenticate users would be as follows.

class AuthenticateUser
prepend SimpleCommand
def initialize(email, password)
@email = email
@password = password
end
def call
JsonWebToken.encode(user_id: user.id) if user
end
private
attr_accessor :email, :password

def user
user = User.find_by_email(email)
return user if user && user.authenticate(password)
errors.add :user_authentication, 'invalid credentials'
nil
end
end

Then in the controller, our logic would be much simpler since finding the User and Encoding the token all happened in the Service layer.

class AuthenticationController < ApplicationController  def authenticate
command = AuthenticateUser.call(params[:email], params[:password])
if command.success?
render json: { auth_token: command.result }
else
render json: { error: command.errors }, status: :unauthorized
end
end
end

Service objects that are designed to execute one single action in your domain logic and do it well. This allows your controllers to be leaner and your code to be easy to reuse, test, and debug.

Flatiron School Software Engineering Student