Often when working with Rails we hear about fatty controllers and skinny models. We also come across complex user interactions where we need to perform tasks sequentially on particular user action.
There are various ways to solve the above problems using service objects, form objects, policy objects, etc.
One such way to refactor code is to use Interactor gem.
We need to add the interactor gem in our Gemfile and execute
As seen in the above example
We created a small class
Interactorin the class.
We passed two arguments to
b. They can be accessed inside the interactor using the
We also set the
context.sum = context.a + context.b.
contextcontains everything the interactor needs to do its work.
AddNumbers.call(a: 1, b: 2)returns a
contextobject and we can check if the above code executed successfully or not by checking
We can check the sum by executing
If something goes wrong in the interactor we can flag the context as failed by
context.fail!and also set the error message as
context.fail!(error: "Error message"). Above example can be modified as
interactor also provides
after hooks. We can think
of them to be similar to
rescue in the above example we can add a
before hook to check if the
numbers are integer or not and fail the context accordingly.
We can initialize
sum to 0 as shown below.
The above interactor is a single-purpose unit interactor. A complex system might involve multiple interactors which need to be called in a sequence.
To execute a sequence of interactors we can use organizers provided by this gem. Imagine a situation where we need to pull data of users from different social media accounts and import it into our database.
The basic steps we might follow to import the users will be:
- Fetch data from the social media account.
- Convert data into standard format which can be imported to our system.
- Import data into our system.
Each of the above steps can be seen as a single unit and we can create interactors for each step. The above steps can be grouped in an organizer as below:
We have created three interactors inside
The three interactors will be called one after another in a sequence.
If any interactor fails then next interactors are not called and the respective error is returned.
So to check if the flow was executed successfully we can use
method as below
As per the above example, we observe
Facebook.new is passed as a client to the
If we decide to integrate
organizer as below:
The interactor and organizers help to refactor the code into smaller single units making them easily testable and reusable.