lib/active_interactor/interactor/context.rb
# frozen_string_literal: true
module ActiveInteractor
module Interactor
# Interactor context methods. Because {Context} is a module classes should include {Context} rather than inherit
# from it.
#
# @author Aaron Allen <hello@aaronmallen.me>
# @since 0.1.0
module Context
# Interactor context class methods. Because {ClassMethods} is a module classes should extend {ClassMethods}
# rather than inherit from it.
#
# @author Aaron Allen <hello@aaronmallen.me>
# @since 0.1.0
module ClassMethods
# @!method context_attributes(*attributes)
# Call {ActiveInteractor::Context::Attributes#attributes .attributes} on the {Base interactor} class'
# {#context_class context class}
#
# @since 0.1.0
#
# @example
# class MyInteractor < ActiveInteractor::Base
# context_attributes :first_name, :last_name
# end
#
# MyInteractor.context_class.attributes
# #=> [:first_name, :last_name]
#
# @!method context_attribute_missing(match, *args, &block)
# Call {ActiveInteractor::Context::Base.attribute_missing .attribute_missing} on the {Base interactor} class'
# {#context_class context class}
#
# @since 1.0.1
#
# @!method context_respond_to_without_attributes?(method, include_private_methods = false)
# Call {ActiveInteractor::Context::Base.respond_to_without_attributes? .respond_to_without_attributes?} on the
# {Base interactor} class' {#context_class context class}
#
# @since 1.0.1
delegate :attributes, :attribute_missing, :respond_to_without_attributes?, to: :context_class, prefix: :context
# @!method context_attribute(name, type=Type::Value.new, **options)
# Call {ActiveInteractor::Context::Base.attribute .attribute} on the {Base interactor} class'
# {#context_class context class}
#
# @since 1.0.1
#
# @example Setting default values on the {ActiveInteractor::Context::Base context} class
# class MyInteractor < ActiveInteractor::Base
# context_attribute :first_name, default: -> { 'Aaron' }
# end
#
# MyInteractor.perform
# #=> <#MyInteractor::Context first_name='Aaron'>
#
# MyInteractor.perform(first_name: 'Bob')
# #=> <#MyInteractor::Context first_name='Bob'>
#
# @!method context_attribute_names
# Call {ActiveInteractor::Context::Base.attribute_names .attribute_names} on the {Base interactor} class'
# {#context_class context class}
#
# @since 1.0.1
delegate(*ActiveModel::Attributes::ClassMethods.instance_methods, to: :context_class, prefix: :context)
# @!method context_attribute_method?(attribute)
# Call {ActiveInteractor::Context::Base.attribute_method? .attribute_method?} on the {Base interactor} class'
# {#context_class context class}
#
# @since 0.1.0
#
# @!method context_clear_validators!(attribute)
# Call {ActiveInteractor::Context::Base.clear_validators! .clear_validators!} on the {Base interactor} class'
# {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validate(*args, &block)
# Call {ActiveInteractor::Context::Base.validate .validate} on the {Base interactor} class'
# {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validates(*attributes)
# Call {ActiveInteractor::Context::Base.validates .validates} on the {Base interactor} class'
# {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validates!(*attributes)
# Call {ActiveInteractor::Context::Base.validates! .validates!} on the {Base interactor} class'
# {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validates_each(*attr_names, &block)
# Call {ActiveInteractor::Context::Base.validates_each .validates_each} on the {Base interactor} class'
# {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validates_with(*args, &block)
# Call {ActiveInteractor::Context::Base.validates_with .validates_with} on the {Base interactor} class'
# {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validators
# Call {ActiveInteractor::Context::Base.validators .validators} on the {Base interactor} class'
# {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validators_on(*attributes)
# Call {ActiveInteractor::Context::Base.validators_on .validators_on} on the {Base interactor} class'
# {#context_class context class}
#
# @since 0.1.0
delegate(*ActiveModel::Validations::ClassMethods.instance_methods, to: :context_class, prefix: :context)
# @!method context_validates_absence_of(*attr_names)
# Call {ActiveInteractor::Context::Base.validates_absence_of .validates_absence_of} on the {Base interactor}
# class' {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validates_acceptance_of(*attr_names)
# Call {ActiveInteractor::Context::Base.validates_acceptance_of .validates_acceptance_of} on the
# {Base interactor} class' {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validates_confirmation_of(*attr_names)
# Call {ActiveInteractor::Context::Base.validates_confirmation_of .validates_confirmation_of} on the
# {Base interactor} class' {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validates_exclusion_of(*attr_names)
# Call {ActiveInteractor::Context::Base.validates_exclusion_of .validates_exclusion_of} on the
# {Base interactor} class' {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validates_format_of(*attr_names)
# Call {ActiveInteractor::Context::Base.validates_format_of .validates_format_of} on the {Base interactor}
# class' {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validates_inclusion_of(*attr_names)
# Call {ActiveInteractor::Context::Base.validates_inclusion_of .validates_inclusion_of} on the
# {Base interactor} class' {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validates_length_of(*attr_names)
# Call {ActiveInteractor::Context::Base.validates_length_of .validates_length_of} on the {Base interactor}
# class' {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validates_numericality_of(*attr_names)
# Call {ActiveInteractor::Context::Base.validates_numericality_of .validates_numericality_of} on the
# {Base interactor} class' {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validates_presence_of(*attr_names)
# Call {ActiveInteractor::Context::Base.validates_presence_of .validates_presence_of} on the {Base interactor}
# class' {#context_class context class}
#
# @since 0.1.0
#
# @!method context_validates_size_of(*attr_names)
# Call {ActiveInteractor::Context::Base.validates_size_of .validates_size_of} on the {Base interactor} class'
# {#context_class context class}
#
# @since 0.1.0
delegate(*ActiveModel::Validations::HelperMethods.instance_methods, to: :context_class, prefix: :context)
# Get the {Base interactor} class' {ActiveInteractor::Context::Base context} class. If no class is found or no
# class is set via the {#contextualize_with} method a new class is created.
#
# @see ActiveInteractor::Context::Loader.find_or_create
#
# @return [Const] the {Base interactor} class' {ActiveInteractor::Context::Base context} class
def context_class
@context_class ||= ActiveInteractor::Context::Loader.find_or_create(self)
end
# Set the {Base interactor} class' {#context_class context class}
#
# @since 1.0.0
#
# @example
# class User < ActiveRecord::Base
# end
#
# class MyInteractor < ActiveInteractor::Base
# contextualize_with :user
# end
#
# MyInteractor.context_class
# #=> User
#
# @param klass [Const, Symbol, String] the class to use as context
# @raise [Error::InvalidContextClass] if the class can not be found.
# @return [Const] the {Base interactor} class' {ActiveInteractor::Context::Base context} class
def contextualize_with(klass)
@context_class = begin
context_class = klass.to_s.camelize.safe_constantize
raise(ActiveInteractor::Error::InvalidContextClass, klass) unless context_class
context_class
end
end
end
# @!method context_attribute_missing(match, *args, &block)
# Call {ActiveInteractor::Context::Base#attribute_missing #attribute_missing} on the {Base interactor} instance's
# {ActiveInteractor::Context::Base context} instance
#
# @since 1.0.1
#
# @!method context_attribute_names
# Call {ActiveInteractor::Context::Base#attribute_names #attribute_names} on the {Base interactor} instance's
# {ActiveInteractor::Context::Base context} instance
#
# @since 1.0.1
#
# @!method context_fail!(errors = nil)
# Call {ActiveInteractor::Context::Status#fail! #fail!} on the {Base interactor} instance's
# {ActiveInteractor::Context::Base context} instance
#
# @since 1.0.0
#
# @!method context_respond_to_without_attributes?(method, include_private_methods = false)
# Call {ActiveInteractor::Context::Base#respond_to_without_attributes? #respond_to_without_attributes?} on the
# {Base interactor} instance's {ActiveInteractor::Context::Base context} instance
#
# @since 1.0.1
#
# @!method context_rollback!
# Call {ActiveInteractor::Context::Status#rollback! #rollback!} on the {Base interactor} instance's
# {ActiveInteractor::Context::Base context} instance
#
# @since 1.0.0
delegate :attribute_missing, :attribute_names, :fail!, :respond_to_without_attributes?, :rollback!,
to: :context, prefix: true
# @!method context_errors
# Call {ActiveInteractor::Context::Base#errors #errors} on the {Base interactor} instance's
# {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
#
# @!method context_invalid?(context = nil)
# Call {ActiveInteractor::Context::Base#invalid? #invalid?} on the {Base interactor} instance's
# {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
#
# @!method context_valid?(context = nil)
# Call {ActiveInteractor::Context::Base#valid? #valid?} on the {Base interactor} instance's
# {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
#
# @!method context_validate(context = nil)
# Call {ActiveInteractor::Context::Base#validate #validate} on the {Base interactor} instance's
# {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
#
# @!method context_validate!(context = nil)
# Call {ActiveInteractor::Context::Base#validate! #validate!} on the {Base interactor} instance's
# {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
#
# @!method context_validates_with(*args, &block)
# Call {ActiveInteractor::Context::Base#validates_with #validates_with} on the {Base interactor} instance's
# {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
delegate(*ActiveModel::Validations.instance_methods, to: :context, prefix: true)
# @!method context_validates_absence_of(*attr_names)
# Call {ActiveInteractor::Context::Base#validates_absence_of #validates_absence_of} on the {Base interactor}
# instance's {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
#
# @!method context_validates_acceptance_of(*attr_names)
# Call {ActiveInteractor::Context::Base#validates_acceptance_of #validates_acceptance_of} on the
# {Base interactor} instance's {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
#
# @!method context_validates_confirmation_of(*attr_names)
# Call {ActiveInteractor::Context::Base#validates_confirmation_of #validates_confirmation_of} on the
# {Base interactor} instance's {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
#
# @!method context_validates_exclusion_of(*attr_names)
# Call {ActiveInteractor::Context::Base#validates_exclusion_of #validates_exclusion_of} on the {Base interactor}
# instance's {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
#
# @!method context_validates_format_of(*attr_names)
# Call {ActiveInteractor::Context::Base#validates_format_of #validates_format_of} on the {Base interactor}
# instance's {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
#
# @!method context_validates_inclusion_of(*attr_names)
# Call {ActiveInteractor::Context::Base#validates_inclusion_of #validates_inclusion_of} on the
# {Base interactor} instance's {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
#
# @!method context_validates_length_of(*attr_names)
# Call {ActiveInteractor::Context::Base#validates_length_of #validates_length_of} on the {Base interactor}
# instance's {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
#
# @!method context_validates_numericality_of(*attr_names)
# Call {ActiveInteractor::Context::Base#validates_numericality_of #validates_numericality_of} on the
# {Base interactor} instance's {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
#
# @!method context_validates_presence_of(*attr_names)
# Call {ActiveInteractor::Context::Base#validates_presence_of #validates_presence_of} on the {Base interactor}
# instance's {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
#
# @!method context_validates_size_of(*attr_names)
# Call {ActiveInteractor::Context::Base#validates_size_of #validates_size_of} on the {Base interactor}
# instance's {ActiveInteractor::Context::Base context} instance
#
# @since 0.1.0
delegate(*ActiveModel::Validations::HelperMethods.instance_methods, to: :context, prefix: true)
# Initialize a new instance of {Base}
#
# @param context [Hash, Class] attributes to assign to the {Base interactor} instance's
# {ActiveInteractor::Context::Base context} instance
# @return [Base] a new instance of {Base}
def initialize(context = {})
@context = self.class.context_class.new(context)
end
# Mark the {Base interactor} instance as called on the instance's {ActiveInteractor::Context::Base context}
# instance and return the {ActiveInteractor::Context::Base context} instance.
#
# @since 1.0.0
#
# @return [Class] the {ActiveInteractor::Context::Base context} instance
def finalize_context!
context.called!(self)
context.resolve
context
end
private
attr_accessor :context
end
end
end