# frozen_string_literal: true require 'dry/initializer' module Dry module Schema # Steps for the Dry::Schema::Processor # # There are 4 main steps: # # 1. `key_coercer` - Prepare input hash using a key map # 2. `filter_schema` - Apply pre-coercion filtering rules # (optional step, used only when `filter` was used) # 3. `value_coercer` - Apply value coercions based on type specifications # 4. `rule_applier` - Apply rules # # @see Processor # # @api public class ProcessorSteps extend Dry::Initializer STEPS_IN_ORDER = %i[key_coercer filter_schema value_coercer rule_applier].freeze option :steps, default: -> { EMPTY_HASH.dup } option :before_steps, default: -> { EMPTY_HASH.dup } option :after_steps, default: -> { EMPTY_HASH.dup } def call(result) STEPS_IN_ORDER.each do |name| before_steps[name]&.each { |step| process_step(step, result) } process_step(steps[name], result) after_steps[name]&.each { |step| process_step(step, result) } end result end # Return step by name # # @param [Symbol] name The step name # # @api public def [](name) steps[name] end # Sets step by name # # @param [Symbol] name The step name # # @api public def []=(name, value) validate_step_name(name) steps[name] = value end # Add passed block before mentioned step # # @param [Symbol] name The step name # # @return [ProcessorSteps] # # @api public def after(name, &block) validate_step_name(name) after_steps[name] ||= EMPTY_ARRAY.dup after_steps[name] << block.to_proc self end # Add passed block before mentioned step # # @param [Symbol] name The step name # # @return [ProcessorSteps] # # @api public def before(name, &block) validate_step_name(name) before_steps[name] ||= EMPTY_ARRAY.dup before_steps[name] << block.to_proc self end # @api private def process_step(step, result) return unless step output = step.(result) result.replace(output) if output.is_a?(::Hash) end # @api private def validate_step_name(name) return if STEPS_IN_ORDER.include?(name) raise ArgumentError, "Undefined step name #{name}. Available names: #{STEPS_IN_ORDER}" end end end end