---
# System prepended metadata

title: Interactors

---

# 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/
```
