diff --git a/dsl/step_communication.rb b/dsl/step_communication.rb new file mode 100644 index 00000000..f6b3ce13 --- /dev/null +++ b/dsl/step_communication.rb @@ -0,0 +1,18 @@ +# typed: false +# frozen_string_literal: true + +# How do we pass information between steps? +# Demonstrate by passing result of a command output to another step + +config do + cmd(:echo) { display! } +end + +execute do + cmd(:ls) { "ls -al" } + cmd(:echo) do + # TODO: this is a bespoke output object for cmd, is there a generic one we can offer + first_line = cmd(:ls).command_output.split("\n").second + "echo '#{first_line}'" + end +end diff --git a/lib/roast/dsl/cog.rb b/lib/roast/dsl/cog.rb index 51714810..a7dd1ddb 100644 --- a/lib/roast/dsl/cog.rb +++ b/lib/roast/dsl/cog.rb @@ -4,12 +4,14 @@ module Roast module DSL class Cog + class CogAlreadyRanError < StandardError; end + class << self def on_create eigen = self proc do |instance_name = Random.uuid, &action| - #: self as Roast::DSL::ExecutionContext - add_cog_instance(instance_name, eigen.new(action)) + #: self as Roast::DSL::WorkflowExecutionContext + add_cog_instance(instance_name, eigen.new(instance_name, action)) end end @@ -39,23 +41,28 @@ def find_child_config_or_default end end - attr_reader :output + attr_reader :name, :output - def initialize(cog_input_proc) + def initialize(name, cog_input_proc) + @name = name @cog_input_proc = cog_input_proc + @finished = false end - def input - @cog_input_proc.call - end + def run!(config, cog_execution_context) + raise CogAlreadyRanError if ran? - def run!(config) @config = config - @output = execute + @output = execute(cog_execution_context.instance_exec(&@cog_input_proc)) + @finished = true + end + + def ran? + @finished end # Inheriting cog must implement this - def execute + def execute(input) raise NotImplementedError end end diff --git a/lib/roast/dsl/cog/store.rb b/lib/roast/dsl/cog/store.rb index 4b4ebc6f..3b9907bc 100644 --- a/lib/roast/dsl/cog/store.rb +++ b/lib/roast/dsl/cog/store.rb @@ -7,10 +7,7 @@ class Cog class Store class CogAlreadyDefinedError < Roast::Error; end - #: (Symbol) -> Roast::DSL::Cog? - def find(id) - store[id] - end + delegate :[], to: :store #: (Symbol, Roast::DSL::Cog) -> Roast::DSL::Cog def insert(id, inst) diff --git a/lib/roast/dsl/cog_execution_context.rb b/lib/roast/dsl/cog_execution_context.rb new file mode 100644 index 00000000..e9ab9fa9 --- /dev/null +++ b/lib/roast/dsl/cog_execution_context.rb @@ -0,0 +1,29 @@ +# typed: true +# frozen_string_literal: true + +module Roast + module DSL + # Contains the cogs already executed in this run. + class CogExecutionContext + # Raises if you access a cog in an execution block that hasn't already been run. + class IncompleteCogExecutionAccessError < StandardError; end + + def initialize(cogs, bound_names) + @cogs = cogs + bind_cog_methods(bound_names) + end + + private + + def bind_cog_methods(bound_names) + bound_names.map do |name| + define_singleton_method(name.to_sym, ->(name) do + @cogs[name].tap do |cog| + raise IncompleteCogExecutionAccessError unless cog.ran? + end.output + end) + end + end + end + end +end diff --git a/lib/roast/dsl/cogs/cmd.rb b/lib/roast/dsl/cogs/cmd.rb index 62ea18e2..d6a96143 100644 --- a/lib/roast/dsl/cogs/cmd.rb +++ b/lib/roast/dsl/cogs/cmd.rb @@ -7,7 +7,7 @@ module Cogs class Cmd < Cog class Output #: String? - attr_reader :output + attr_reader :command_output #: String? attr_reader :err @@ -21,7 +21,7 @@ class Output #| Process::Status? status #| ) -> void def initialize(output, error, status) - @output = output + @command_output = output @err = error @status = status end @@ -37,12 +37,16 @@ def print_all! def print_all? !!@values[:print_all] end + + def display! + print_all! + end end - #: () -> Output - def execute + #: (String) -> Output + def execute(input) result = Output.new(*Roast::Helpers::CmdRunner.capture3(input)) - puts result.output if @config.print_all? + puts result.command_output if @config.print_all? result end end diff --git a/lib/roast/dsl/executor.rb b/lib/roast/dsl/executor.rb index fa438dc5..842c3b9d 100644 --- a/lib/roast/dsl/executor.rb +++ b/lib/roast/dsl/executor.rb @@ -32,7 +32,7 @@ def prepare!(input) @config_context = ConfigContext.new(@cogs, @config_proc) @config_context.prepare! - @execution_context = ExecutionContext.new(@cogs, @cog_stack, @execution_proc) + @execution_context = WorkflowExecutionContext.new(@cogs, @cog_stack, @execution_proc) @execution_context.prepare! @prepared = true @@ -44,7 +44,10 @@ def start! raise ExecutorAlreadyCompletedError if @completed @cog_stack.map do |name, cog| - cog.run!(@config_context.fetch_merged_config(cog.class, name.to_sym)) + cog.run!( + @config_context.fetch_merged_config(cog.class, name.to_sym), + @execution_context.cog_execution_context, + ) end @completed = true @@ -63,7 +66,7 @@ def config(&block) @config_proc = block end - #: { () [self: ExecutionContext] -> void} -> void + #: { () [self: WorkflowExecutionContext] -> void} -> void def execute(&block) @execution_proc = block end diff --git a/lib/roast/dsl/execution_context.rb b/lib/roast/dsl/workflow_execution_context.rb similarity index 72% rename from lib/roast/dsl/execution_context.rb rename to lib/roast/dsl/workflow_execution_context.rb index 0f1d6c7e..3b5cec08 100644 --- a/lib/roast/dsl/execution_context.rb +++ b/lib/roast/dsl/workflow_execution_context.rb @@ -3,11 +3,12 @@ module Roast module DSL - class ExecutionContext + class WorkflowExecutionContext def initialize(cogs, cog_stack, execution_proc) @cogs = cogs @cog_stack = cog_stack @execution_proc = execution_proc + @bound_names = [] end def prepare! @@ -15,6 +16,10 @@ def prepare! instance_eval(&@execution_proc) end + def cog_execution_context + @cog_execution_context ||= CogExecutionContext.new(@cogs, @bound_names) + end + private def add_cog_instance(name, cog) @@ -22,12 +27,17 @@ def add_cog_instance(name, cog) @cog_stack.push([name, cog]) end + def output(name) + @cogs[name].output + end + #: () -> void def bind_default_cogs bind_cog(Cogs::Cmd, :cmd) end def bind_cog(cog_class, name) + @bound_names << name instance_eval do define_singleton_method(name, &cog_class.on_create) end diff --git a/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi b/sorbet/rbi/shims/lib/roast/dsl/workflow_execution_context.rbi similarity index 85% rename from sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi rename to sorbet/rbi/shims/lib/roast/dsl/workflow_execution_context.rbi index e57a9484..aab875f9 100644 --- a/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi +++ b/sorbet/rbi/shims/lib/roast/dsl/workflow_execution_context.rbi @@ -3,7 +3,7 @@ module Roast module DSL - class ExecutionContext + class WorkflowExecutionContext #: (?Symbol?) {() [self: Roast::DSL::Cogs::Cmd] -> String} -> void def cmd(name = nil, &block); end end