Context

When you create an instance of interactor, the options you pass to it are first validated and then put into an object called context which can be accessed by the instance methods of your interactor. Under the hood there are actually three different types of context.

Input Context

The first of the three is the ActiveInteractor::Context::Input class which we interfaced with when we called the argument method in our interactor. The argument method simply tells the input context what fields it should expect to have, their types, a description, and whether or not that field is required. If a field is specified as required or if a value passed to a field doesn’t match the type declared by the argument method, the interactor will fail on input before our interact method is ever called.


class Ping < ActiveInteractor::Interactor::Base
  argument :message, String, 'A message to output to console', required: true

  ...
end

Runtime Context

The second context object is the ActiveInteractor::Context::Runtime context which behaves much like an OpenStruct. You can think of this context object as our scratch pad. We can declare any variables we want on it. This is the context object you’re actually interfacing with in your interactor’s instance methods. After the input context passes validation, we inject all of it’s keys and values onto the runtime context. Any fields declared by the argument method will have their values injected into the runtime context. You can see an example of that below.


class Ping < ActiveInteractor::Interactor::Base
  argument :message, String, 'A message to output to console', required: true

  def interact
    # This context object is our runtime context
    puts context.message
  end
end

Ping.perform(message: 'Hello World!')
"Hello World"
#=> <# ActiveInteractor::Result>

Output Context

The last of the three context objects is the ActiveInteractor::Context::Output object. Much like the input context we interfaced with this context object by using the returns method. Any fields defined by the returns method will be placed in the returned ActiveInteractor::Result#data object when an interactor runs successfully. We’ll dive in deeper on the ActiveInteractor::Result object later. Any fields you had on your runtime context that are not declared by the returns method will be discarded and will not appear on the output context object.


class Ping < ActiveInteractor::Interactor::Base
  argument :message, String, 'A message to output to console', required: true

  returns :time_to_complete_call, Float, 'The amount of time in seconds it took to send a message', required: true

  def interact
    start = Time.current
    context.some_other_variable = 'foo'
    puts context.message
    context.time_to_complete_call = Time.current - start
  end
end

result = Ping.perform(message: 'Hello World!')
"Hello World"
#=> <#ActiveInteractor::Result>
result.success?
#=> true
result.data.time_to_complete_call
#=> 1.341372
result.data.some_other_variable
#=> undefined method `some_other_variable' for #<Ping> (NoMethodError)