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