class ActiveInteraction::Base
@abstract Subclass and override {#execute} to implement a custom
ActiveInteraction::Base class.
Provides interaction functionality. Subclass this to create an interaction.
@example
class ExampleInteraction < ActiveInteraction::Base # Required boolean :a # Optional boolean :b, default: false def execute a && b end end outcome = ExampleInteraction.run(a: true) if outcome.valid? outcome.result else outcome.errors end
Public Class Methods
Get or set the description.
@example
core.desc # => nil core.desc('Description!') core.desc # => "Description!"
@param desc [String, nil] What to set the description to.
@return [String, nil] The description.
# File lib/active_interaction/base.rb, line 72 def desc(desc = nil) if desc.nil? @_interaction_desc = nil unless instance_variable_defined?(:@_interaction_desc) else @_interaction_desc = desc end @_interaction_desc end
Get all the filters defined on this interaction.
@return [Hash{Symbol => Filter}]
# File lib/active_interaction/base.rb, line 85 def filters # rubocop:disable Naming/MemoizedInstanceVariableName @_interaction_filters ||= {} # rubocop:enable Naming/MemoizedInstanceVariableName end
@private rubocop:disable Style/MissingRespondToMissing
ActiveInteraction::Missable#method_missing
# File lib/active_interaction/base.rb, line 93 def method_missing(*args, &block) super do |klass, names, options| raise InvalidFilterError, 'missing attribute name' if names.empty? names.each { |name| add_filter(klass, name, options, &block) } end end
@private
# File lib/active_interaction/base.rb, line 162 def initialize(inputs = {}) @_interaction_raw_inputs = inputs populate_filters_and_inputs(Inputs.process(inputs)) end
Private Class Methods
@param klass [Class] @param name [Symbol] @param options [Hash]
# File lib/active_interaction/base.rb, line 107 def add_filter(klass, name, options, &block) raise InvalidFilterError, %("#{name}" is a reserved name) if Inputs.reserved?(name) initialize_filter(klass.new(name, options, &block)) end
@param filter [Filter]
# File lib/active_interaction/base.rb, line 155 def eagerly_evaluate_default(filter) default = filter.options[:default] filter.default if default && !default.is_a?(Proc) end
Import filters from another interaction.
@param klass [Class] The other interaction. @param options [Hash]
@option options [Array<Symbol>, nil] :only Import only these filters. @option options [Array<Symbol>, nil] :except Import all filters except
for these.
@return (see .filters)
@!visibility public
# File lib/active_interaction/base.rb, line 125 def import_filters(klass, options = {}) only = options[:only] except = options[:except] other_filters = klass.filters.dup other_filters.select! { |k, _| [*only].include?(k) } if only other_filters.reject! { |k, _| [*except].include?(k) } if except other_filters.each_value { |filter| initialize_filter(filter) } end
@param klass [Class]
# File lib/active_interaction/base.rb, line 137 def inherited(klass) klass.instance_variable_set(:@_interaction_filters, filters.dup) super end
@param filter [Filter]
# File lib/active_interaction/base.rb, line 144 def initialize_filter(filter) attribute = filter.name warn "WARNING: Redefining #{name}##{attribute} filter" if filters.key?(attribute) filters[attribute] = filter attr_accessor attribute eagerly_evaluate_default(filter) end
Public Instance Methods
Returns `true` if the given key was in the hash passed to {.run}. Otherwise returns `false`. Use this to figure out if an input was given, even if it was `nil`. Keys within nested hash filter can also be checked by passing them in series. Arrays can be checked in the same manor as hashes by passing an index.
@example
class Example < ActiveInteraction::Base integer :x, default: nil def execute; given?(:x) end end Example.run!() # => false Example.run!(x: nil) # => true Example.run!(x: rand) # => true
@example Nested checks
class Example < ActiveInteraction::Base hash :x, default: {} do integer :y, default: nil end array :a, default: [] do integer end def execute; given?(:x, :y) || given?(:a, 2) end end Example.run!() # => false Example.run!(x: nil) # => false Example.run!(x: {}) # => false Example.run!(x: { y: nil }) # => true Example.run!(x: { y: rand }) # => true Example.run!(a: [1, 2]) # => false Example.run!(a: [1, 2, 3]) # => true
@param input [#to_sym]
@return [Boolean]
@since 2.1.0 rubocop:disable all
# File lib/active_interaction/base.rb, line 233 def given?(input, *rest) filter_level = self.class input_level = @_interaction_raw_inputs [input, *rest].each do |key_or_index| if key_or_index.is_a?(Symbol) || key_or_index.is_a?(String) key = key_or_index.to_sym key_to_s = key_or_index.to_s filter_level = filter_level.filters[key] break false if filter_level.nil? || input_level.nil? if filter_level.accepts_grouped_inputs? break false unless input_level.key?(key) || input_level.key?(key_to_s) || Inputs.keys_for_group?(input_level.keys, key) else break false unless input_level.key?(key) || input_level.key?(key_to_s) end input_level = input_level[key] || input_level[key_to_s] else index = key_or_index filter_level = filter_level.filters.first.last break false if filter_level.nil? || input_level.nil? break false unless index.between?(-input_level.size, input_level.size - 1) input_level = input_level[index] end end && true end
Returns the inputs provided to {.run} or {.run!} after being cast based
on the filters in the class.
@return [Hash{Symbol => Object}] All inputs passed to {.run} or {.run!}.
# File lib/active_interaction/base.rb, line 190 def inputs @_interaction_inputs end
Protected Instance Methods
rubocop:enable all
# File lib/active_interaction/base.rb, line 266 def run_validations! type_check super if errors.empty? end
Private Instance Methods
# File lib/active_interaction/base.rb, line 274 def populate_filters_and_inputs(inputs) @_interaction_inputs = Inputs.new self.class.filters.each do |name, filter| value = begin filter.clean(inputs[name], self) rescue InvalidValueError, MissingValueError, NoDefaultError # #type_check will add errors if appropriate. # We'll get the original value for the error. inputs[name] end @_interaction_inputs[name] = value public_send("#{name}=", value) end @_interaction_inputs.freeze end
# File lib/active_interaction/base.rb, line 294 def type_check run_callbacks(:type_check) do Validation.validate(self, self.class.filters, inputs).each do |attr, type, kwargs = {}| errors.add(attr, type, **kwargs) end end end