# Interactors
- Es una gema que implementa el patron Command
- Construye clases simples de unico propósito
- Provee de hooks: `around`, `before`, `after`
- Único método publico `call`
- Tenemos la chance de crear pasarelas de interactos diseñando lo que se llama `Organizers`
## Ejemplo básico
``` ruby
class TrucksController < ApplicationController
def create
@truck = Truck.find_or_initialize_by(account_id: current_account.id, identifier: truck_identifier)
if @truck.persisted && @truck.active?
return render json: active_truck_message_error, status: :unprocessable_entity
end
@truck.attributes = truck_params.merge(active: true)
if @truck.save
@truck.create_activity(
:create,
trackable: @truck,
owner: active_user,
recipient: current_account,
parameters: { name: @truck.identifier }
)
render json: TruckSerializer.new(@truck).serialized_json
else
render json: @truck.errors, status: :unprocessable_entity
end
end
private
def active_truck_message_error
{
identifier: [
I18n.t(
'errors.trucks.already_exists',
identifier: truck_identifier
)
]
}
end
def truck_identifier
@truck_identifier ||= Parsers::BeeParser.parse_plate(params[:identifier])
end
def truck_params
params.permit(
:vehicle_type,
:capacity,
group_ids: []
)
end
end
```
``` ruby
class TrucksController < ApplicationController
def create
response = Trucks::Create.call(
params: truck_params,
account: current_account,
user: active_user
)
if response.successful?
render json: TruckSerializer.new(response.truck).serialized_json
else
render json: response.errors, status: :unprocessable_entity
end
end
private
def truck_params
params.permit(
:vehicle_type,
:capacity,
group_ids: []
)
end
end
```
``` ruby
# app/interactors/trucks/create.rb
module Trucks
class Create
include Interactor
delegate :params, :account, :user, to: :context
before do
params.merge!(
{
identifier: ::Parsers::BeeParser.parse_plate(params[:identifier]),
active: true,
account_id: account.id
}
)
end
def call
context.truck = Truck.find_or_initialize_by(account_id: current_account.id, identifier: truck_identifier)
context.fail!(error: active_truck_message_error) if truck.persited? && truck.active?
context.fail!(error: truck.errors) unless truck.update(params)
end
def active_truck_message_error
{
identifier: [
I18n.t(
'errors.trucks.already_exists',
identifier: truck_identifier
)
]
}
end
after do
truck.create_activity(
:create,
trackable: truck,
owner: user,
recipient: account,
parameters: { name: truck.identifier }
)
end
def rollback
# undo any database operation or send an email to inform
end
end
end
```
## Organizers
``` ruby
class PlaceOrder
include Interactor::Organizer
organize CreateOrder, ChargeCard, SendThankYou
end
```
## Hooks
``` ruby
around do |interactor|
puts "around before 1"
interactor.call
puts "around after 1"
end
around do |interactor|
puts "around before 2"
interactor.call
puts "around after 2"
end
before do
puts "before 1"
end
before do
puts "before 2"
end
after do
puts "after 1"
end
after do
puts "after 2"
end
```
## Concerns
``` ruby
module InteractorTimer
extend ActiveSupport::Concern
included do
around do |interactor|
context.start_time = Time.now
interactor.call
context.finish_time = Time.now
end
end
end
# app/interactors/trucks/create.rb
module Trucks
class Create
include Interactor
include InteractorTimer
...
end
end
```
## Dónde ubicar los interactors
```
▾ app/
▸ controllers/
▸ helpers/
▾ interactors/
▾trucks/
create.rb
authenticate_user.rb
cancel_account.rb
publish_post.rb
register_user.rb
remove_post.rb
▸ mailers/
▸ models/
▸ views/
```