class Core::Pipeline::Compiler

public

Compiles a pipeline into evalable code.

Public Class Methods

compile(callable, object) click to toggle source
public

Compiles a callable pipeline in context of an object.

# File lib/core/pipeline/compiler.rb, line 14
        def compile(callable, object)
          <<~CODE
            def call(object, ...)
            #{compile_call(callable, object)}
            rescue Core::Pipeline::Signals::Halted => error
              error.value
            end
          CODE
        end

Private Class Methods

build_finalized_actions(callable, object) click to toggle source
# File lib/core/pipeline/compiler.rb, line 56
        def build_finalized_actions(callable, object)
  ordered_actions(callable).reject { |action|
    callable.skipped.include?(action.name)
  }.map { |action|
    action.finalize(object)
  }.each_with_object({}) { |action, lookup|
    lookup[action.object_id] = action
  }
end
compile_call(callable, object) click to toggle source
# File lib/core/pipeline/compiler.rb, line 24
        def compile_call(callable, object)
  compiled = +""
  finalized_actions(callable, object).each_pair do |object_id, action|
    compiled << "begin; "

    compiled << case action
    when Symbol
      "object.#{action}(...); "
    else
      "@__finalized_actions[#{object_id}].call(...); "
    end

    compiled << "rescue Core::Pipeline::Signals::Rejected; end\n"
  end

  compiled
end
finalized_actions(callable, object) click to toggle source
# File lib/core/pipeline/compiler.rb, line 42
        def finalized_actions(callable, object)
  validate_skipped_actions(callable)
  finalized_actions = build_finalized_actions(callable, object)
  callable.instance_variable_set(:@__finalized_actions, finalized_actions)
end
ordered_actions(callable) click to toggle source
# File lib/core/pipeline/compiler.rb, line 66
        def ordered_actions(callable)
  return callable.actions if callable.actions.empty?

  marked = []
  ordered = []
  working = callable.actions.dup

  callable.actions.each do |action|
    unless action.before || action.after
      ordered << action
      working.delete(action)
    end
  end

  raise "pipeline cannot be finalized because all actions have dependencies" if ordered.empty?

  until working.empty?
    action = working[0]

    anchor_action_name = action.before || action.after

    anchor_action = ordered.find { |ordered_action|
      ordered_action.name == anchor_action_name
    }

    if anchor_action
      insertion_index = if action.before
        ordered.index(anchor_action)
      else
        ordered.index(anchor_action) + 1
      end

      ordered.insert(insertion_index, action)
      working.delete(action)
      marked.delete(action)
    elsif working.find { |working_action| working_action.name == anchor_action_name }
      if marked.include?(action)
        raise "pipeline has one or more recursive dependencies"
      else
        marked << action
        working.delete(action)
        working << action
      end
    else
      raise "unknown action `#{anchor_action_name}`"
    end
  end

  ordered
end
validate_skipped_actions(callable) click to toggle source
# File lib/core/pipeline/compiler.rb, line 48
        def validate_skipped_actions(callable)
  callable.skipped.each do |skipped|
    unless callable.actions.any? { |action| action.name == skipped }
      raise "cannot skip unknown action `#{skipped}`"
    end
  end
end