Skip to content

Callbacks

Aaron Allen edited this page Jan 26, 2020 · 2 revisions

ActiveInteractor uses ActiveModel::Callbacks and ActiveModel::Validations::Callbacks on context validation, #perform, and #rollback. Callbacks can be defined with a Proc, or Symbol method name and take the same conditional arguments outlined in those two modules.

Validation Callbacks

We can do work before an interactor's context is validated with the .before_context_validation method:

class MyInteractorContext < ActiveInteractor::Context::Base
  attributes :first_name, :last_name, :email
  validates :last_name, presence: true
end

class MyInteractor < ActiveInteractor::Base
  before_context_validation { context.last_name ||= 'Unknown' }
end

result = MyInteractor.perform(first_name: 'Aaron', email: 'hello@aaronmallen.me')
result.valid?
#=> true

result.last_name
#=> 'Unknown'

We can do work after an interactor's context is validated with the .after_context_validation method:

class MyInteractorContext < ActiveInteractor::Context::Base
  attributes :first_name, :last_name, :email
  validates :email, presence: true,
                    format: { with: URI::MailTo::EMAIL_REGEXP }
end

class MyInteractor < ActiveInteractor::Base
  after_context_validation { context.email&.downcase! }
end

result = MyInteractor.perform(first_name: 'Aaron', last_name: 'Allen', email: 'HELLO@AARONMALLEN.ME')
result.valid?
#=> true

result.email
#=> 'hello@aaronmallen.me'

Perform Callbacks

We can do work before #perform is invoked with the .before_perform method:

class MyInteractor < ActiveInteractor::Base
  before_perform :print_start

  def perform
    puts 'Performing'
  end

  private

  def print_start
    puts 'Start'
  end
end

MyInteractor.perform
"Start"
"Performing"
#=> <#MyInteractor::Context...>

We can do work around #perform invokation with the .around_perform method:

class MyInteractor < ActiveInteractor::Base
  around_perform :track_time

  def perform
    sleep(1)
  end

  private

  def track_time
    context.start_time = Time.now.utc
    yield
    context.end_time = Time.now.utc
  end
end

result = MyInteractor.perform
result.start_time #=> 2019-01-01 00:00:00 UTC
result.end_time #=> 2019-01-01 00:00:01 UTC

We can do work after #perform is invoked with the .after_perform method:

class MyInteractor < ActiveInteractor::Base
  after_perform :print_done

  def perform
    puts 'Performing'
  end

  private

  def print_done
    puts 'Done'
  end
end

MyInteractor.perform
"Performing"
"Done"
#=> <#MyInteractor::Context...>

Rollback Callbacks

We can do work before #rollback is invoked with the .before_rollback method:

class MyInteractor < ActiveInteractor::Base
  before_rollback :print_start

  def perform
    context.fail!
  end

  def rollback
    puts 'Rolling Back'
  end

  private

  def print_start
    puts 'Start'
  end
end

MyInteractor.perform
"Start"
"Rolling Back"
#=> <#MyInteractor::Context...>

We can do work around #rollback invokation with the .around_rollback method:

class MyInteractor < ActiveInteractor::Base
  around_rollback :track_time

  def perform
    context.fail!
  end

  def rollback
    sleep(1)
  end

  private

  def track_time
    context.start_time = Time.now.utc
    yield
    context.end_time = Time.now.utc
  end
end

result = MyInteractor.perform
result.start_time
#=> 2019-01-01 00:00:00 UTC

result.end_time
#=> 2019-01-01 00:00:01 UTC

We can do work after #rollback is invoked with the .after_rollback method:

class MyInteractor < ActiveInteractor::Base
  after_rollback :print_done

  def perform
    context.fail!
  end

  def rollback
    puts 'Rolling Back'
  end

  private

  def print_done
    puts 'Done'
  end
end

MyInteractor.perform
"Rolling Back"
"Done"
#=> <#MyInteractor::Context...>

Organizer Callbacks

We can do worker before #perform is invoked on each interactor in an organizer with the .before_each_perform method:

class MyInteractor1 < ActiveInteractor::Base
  def perform
    puts 'MyInteractor1'
  end
end

class MyInteractor2 < ActiveInteractor::Base
  def perform
    puts 'MyInteractor2'
  end
end

class MyOrganizer < ActiveInteractor::Organizer::Base
  before_each_perform :print_start

  organized MyInteractor1, MyInteractor2

  private

  def print_start
    puts "Start"
  end
end

MyOrganizer.perform
"Start"
"MyInteractor1"
"Start"
"MyInteractor2"
#=> <MyOrganizer::Context...>

We can do worker around #perform invokation on each interactor in an organizer with the .around_each_perform method:

 class MyInteractor1 < ActiveInteractor::Base
  def perform
    puts 'MyInteractor1'
    sleep(1)
  end
end

class MyInteractor2 < ActiveInteractor::Base
  def perform
    puts 'MyInteractor2'
    sleep(1)
  end
end

class MyOrganizer < ActiveInteractor::Organizer::Base
  around_each_perform :print_time

  organized MyInteractor1, MyInteractor2

  private

  def print_time
    puts Time.now.utc
    yield
    puts Time.now.utc
  end
end

MyOrganizer.perform
"2019-01-01 00:00:00 UTC"
"MyInteractor1"
"2019-01-01 00:00:01 UTC"
"2019-01-01 00:00:01 UTC"
"MyInteractor2"
"2019-01-01 00:00:02 UTC"
#=> <MyOrganizer::Context...>

We can do worker after #perform is invoked on each interactor in an organizer with the .after_each_perform method:

class MyInteractor1 < ActiveInteractor::Base
  def perform
    puts 'MyInteractor1'
  end
end

class MyInteractor2 < ActiveInteractor::Base
  def perform
    puts 'MyInteractor2'
  end
end

class MyOrganizer < ActiveInteractor::Organizer::Base
  after_each_perform :print_done

  organized MyInteractor1, MyInteractor2

  private

  def print_done
    puts "Done"
  end
end

MyOrganizer.perform
"MyInteractor1"
"Done"
"MyInteractor2"
"Done"
#=> <MyOrganizer::Context...>