https://github.com/collectiveidea/interactor I love this little nifty library which greatly encapsulates my logic and allows me to clear my Controllers. Interactors are a form of Service Objects (others may call an interactor a Use Case Object) but you don’t have to define the interfaces yourself and a way of input, a way of invalidating the action as well as a way of checking if an action was a success or not are already there for you.

There are few things that are characteristic for that kind of objects:

• accepting input,
• performing some action,
• returning result.

If you decide to implement all of these three things you have quite a nut to crack and you will end up with something similar what Interactors gives you out of the box or something worse because you don’t have time for serious design. Of course, in some cases, very simple service object are enough but if your application’s business logic contains a few processes, you might consider something more powerful. That’s why I would like to show you this library because it comes with many features and yet it is light and simple.

## Accepting input

Interactors accepts hash as an input which is great because it provides readable and idiomatic way of passing your data.

class UserController < ApplicationController
def create
result = RegisterUser.call(params: user_params)
# response
end

private

def user_params
end
end


Internally the library converts it to Context object which is available inside interactor under context attribute. This is a way of communication between interactors as well as with interactor itself but that’s for later.

## Performing an action

Every interactor has single method call which starts its piece of logic. Call method must return Context

## Returning result

…because a context is an object which tells us if an action was performed successfully or not. Context has two methods for this - success? or failure?, depends on our preferences. By default context is successful until you fail it inside your interactor.

class RegisterUser
include Interactor

def call
user = User.new(context.params)
if user.save
context.user = user # context.success? returns true
else
context.fail! # context.success? returns false
end
end
end


Knowing this, your controller might look like this:

class UserController < ApplicationController
def create
result = RegisterUser.call(params: user_params)
if result.success?
# 200
else
# 422
end
end

private

def user_params
end
end


That was only simple example and frequently some processes require more steps rather than just creation. In our example we would like to send a confirmation email after we create a user. Logic for that we could put next to user creation, however this process is getting more complex and we want to keep our logic divided by its responsibility and keep it easy to test. So, we can define two steps in this registration process:

1. creating a user,
2. sending him a confirmation email.

I’m going to put each step in its separate interactor - CreateUser, SendConfirmationEmail.

class CreateUser
include Interactor

def call
user = User.new(context.params)
if user.save
context.user = user # context.success? returns true
else
context.fail! # context.success? returns false
end
end
end

class SendConfirmationEmail
include Interactor

def call
UserMailer.confirmation(context.user).deliver!
end
end


Now, we need a way of running both of these interactors in order to fulfill the process of registration. However, we don’t need to create any logic ourselves.

## Organizers

Library defines for us a special interactor which is able to run list of interactors in a sequential manner. That special interactor is called Organizer and in our case it would look like this:

class RegisterUser
include Interactor::Organizer

organize CreateUser, SendConfirmationEmail
end


Organizer call each interactor and passes context from one to the next. If any interactor fail in the middle of whole process, organizer stops and it is marked as failed as well.

## Conclusion

As you can see, we expanded our registration process but our controller didn’t change and its job is only to call interactor and handle its result. Thanks to interactors, controllers stay clean, simple and easy to test.

I hope this post was informative for you and my bad English grammar didn’t make you cry.