ActiveInteractor

Gem Build Status Code Climate Test Coverage license

Simple use case interactor for Rails apps based on ActiveModel.

It is heavily inspired by Hanami::Interactor.

Installation

Add this line to your application's Gemfile:

gem 'active_interactor'

And then execute:

$ bundle

Or install it yourself as:

$ gem install active_interactor

Usage

Include ActiveInteractor into a class to define your interactor. It must implement #call method and may declare which instance variables to be exposed:

class CreateProduct
  include ActiveInteractor

  expose :product

  def initialize(repository: ProductRepository)
    @repository = repository
  end

  def call(attributes)
    @product = @repository.create(attributes)
  end
end

Calling an interactor instance returns a ActiveInteractor::Result. It has success or failure state:

result = CreateProduct.new.call(params)
result.is_a? ActiveInteractor::Result #=> true
result.success? # `true` or `false`. In this case `true`
result.failure? # opposite of #success?

It responds to the exposed messages above:

result.product #=> The object returned by `@repository.create`
result.repository #=> NeMethodError

You may use the interactor in your controllers:

class ProductsController < ApplicationController
  def create
    result = CreateProduct.new.call(product_params)
    if result.success?
      redirect_to result.product
    else
      @errors = result.errors
      render :edit
    end
  end

  private

  def product_params
    params.require(:product).permit(:name)
  end
end

Failure

Adding a message to #errors causes failure result.

def call(*)
  errors.add(:base, 'fail')
end
result = FailureInteractor.new.call

result.success? #=> false
result.errors.full_messages_for(:base) #=> ['fail']

You can use #merge_errors utility to merge another ActiveModel::Errors into #errors:

def call(params)
  @product = @repository.create(params)
  # Return failure result if @product is invalid
  merge_errors(@product.errors) if @product.invalid?
end

Validation

class CreateProduct
  include ActiveInteractor

  # Declare all key names
  validations(:name) do
    # Write your validation rules with Rails DSL    
    validates :name, presence: true
  end

  call(params)
    # You can assume `params` pass the validations described above.
    # If some validation fails, this method isn't invoked.
  end
end
result = CreateProduct.new.call(name: nil)
result.success? #=> false
result.errors.full_messages_for(:name) #=> ["name can't be blank"]

I18n

ja:
  activeinteractor:
    models:
      create_product: :activerecord:models:product
    attributes:
      create_product: :activerecord:attributes:product
    errors:
      models:
        create_product:
          attributes:
            name:
              blank: を入力してください

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/increments/active_interactor. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the ActiveInteractor project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.