roast-ai 0.4.10 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/commands/docs/write-comments.md +36 -0
- data/.github/CODEOWNERS +1 -1
- data/.github/workflows/ci.yaml +10 -6
- data/.gitignore +0 -1
- data/.rubocop.yml +7 -1
- data/.ruby-version +1 -1
- data/CLAUDE.md +2 -2
- data/CONTRIBUTING.md +2 -0
- data/Gemfile +19 -18
- data/Gemfile.lock +35 -58
- data/README.md +118 -1432
- data/README_LEGACY.md +1464 -0
- data/Rakefile +39 -4
- data/dev.yml +29 -0
- data/dsl/agent_sessions.rb +20 -0
- data/dsl/async_cogs.rb +49 -0
- data/dsl/async_cogs_complex.rb +67 -0
- data/dsl/call.rb +44 -0
- data/dsl/collect_from.rb +72 -0
- data/dsl/json_output.rb +28 -0
- data/dsl/map.rb +55 -0
- data/dsl/map_reduce.rb +37 -0
- data/dsl/map_with_index.rb +49 -0
- data/dsl/next_break.rb +45 -0
- data/dsl/next_break_parallel.rb +44 -0
- data/dsl/outputs.rb +39 -0
- data/dsl/outputs_bang.rb +36 -0
- data/dsl/parallel_map.rb +37 -0
- data/dsl/prompts/simple_prompt.md.erb +3 -0
- data/dsl/prototype.rb +5 -7
- data/dsl/repeat_loop_results.rb +53 -0
- data/dsl/ruby_cog.rb +72 -0
- data/dsl/simple_agent.rb +18 -0
- data/dsl/simple_chat.rb +15 -1
- data/dsl/simple_repeat.rb +29 -0
- data/dsl/skip.rb +36 -0
- data/dsl/step_communication.rb +2 -3
- data/dsl/targets_and_params.rb +57 -0
- data/dsl/temperature.rb +17 -0
- data/dsl/temporary_directory.rb +22 -0
- data/dsl/tutorial/01_your_first_workflow/README.md +179 -0
- data/dsl/tutorial/01_your_first_workflow/configured_chat.rb +33 -0
- data/dsl/tutorial/01_your_first_workflow/hello.rb +23 -0
- data/dsl/tutorial/02_chaining_cogs/README.md +310 -0
- data/dsl/tutorial/02_chaining_cogs/code_review.rb +104 -0
- data/dsl/tutorial/02_chaining_cogs/session_resumption.rb +92 -0
- data/dsl/tutorial/02_chaining_cogs/simple_chain.rb +84 -0
- data/dsl/tutorial/03_targets_and_params/README.md +230 -0
- data/dsl/tutorial/03_targets_and_params/multiple_targets.rb +65 -0
- data/dsl/tutorial/03_targets_and_params/single_target.rb +65 -0
- data/dsl/tutorial/04_configuration_options/README.md +209 -0
- data/dsl/tutorial/04_configuration_options/control_display_and_temperature.rb +104 -0
- data/dsl/tutorial/04_configuration_options/simple_config.rb +68 -0
- data/dsl/tutorial/05_control_flow/README.md +156 -0
- data/dsl/tutorial/05_control_flow/conditional_execution.rb +62 -0
- data/dsl/tutorial/05_control_flow/handling_failures.rb +77 -0
- data/dsl/tutorial/06_reusable_scopes/README.md +172 -0
- data/dsl/tutorial/06_reusable_scopes/accessing_scope_outputs.rb +126 -0
- data/dsl/tutorial/06_reusable_scopes/basic_scope.rb +63 -0
- data/dsl/tutorial/06_reusable_scopes/parameterized_scope.rb +78 -0
- data/dsl/tutorial/07_processing_collections/README.md +152 -0
- data/dsl/tutorial/07_processing_collections/basic_map.rb +70 -0
- data/dsl/tutorial/07_processing_collections/parallel_map.rb +74 -0
- data/dsl/tutorial/08_iterative_workflows/README.md +231 -0
- data/dsl/tutorial/08_iterative_workflows/basic_repeat.rb +57 -0
- data/dsl/tutorial/08_iterative_workflows/conditional_break.rb +57 -0
- data/dsl/tutorial/09_async_cogs/README.md +197 -0
- data/dsl/tutorial/09_async_cogs/basic_async.rb +38 -0
- data/dsl/tutorial/README.md +222 -0
- data/dsl/working_directory.rb +16 -0
- data/exe/roast +1 -1
- data/internal/documentation/architectural-notes.md +115 -0
- data/internal/documentation/doc-comments-external.md +686 -0
- data/internal/documentation/doc-comments-internal.md +342 -0
- data/internal/documentation/doc-comments.md +211 -0
- data/lib/roast/dsl/cog/config.rb +274 -3
- data/lib/roast/dsl/cog/input.rb +53 -10
- data/lib/roast/dsl/cog/output.rb +297 -8
- data/lib/roast/dsl/cog/registry.rb +35 -3
- data/lib/roast/dsl/cog/stack.rb +1 -1
- data/lib/roast/dsl/cog/store.rb +5 -5
- data/lib/roast/dsl/cog.rb +70 -14
- data/lib/roast/dsl/cog_input_context.rb +36 -1
- data/lib/roast/dsl/cog_input_manager.rb +116 -7
- data/lib/roast/dsl/cogs/agent/config.rb +465 -0
- data/lib/roast/dsl/cogs/agent/input.rb +81 -0
- data/lib/roast/dsl/cogs/agent/output.rb +59 -0
- data/lib/roast/dsl/cogs/agent/provider.rb +51 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/claude_invocation.rb +185 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/message.rb +73 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/assistant_message.rb +36 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/result_message.rb +61 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/system_message.rb +47 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/text_message.rb +36 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_result_message.rb +47 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_use_message.rb +46 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/unknown_message.rb +27 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/user_message.rb +37 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/tool_result.rb +51 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/tool_use.rb +48 -0
- data/lib/roast/dsl/cogs/agent/providers/claude.rb +31 -0
- data/lib/roast/dsl/cogs/agent/stats.rb +92 -0
- data/lib/roast/dsl/cogs/agent/usage.rb +62 -0
- data/lib/roast/dsl/cogs/agent.rb +75 -0
- data/lib/roast/dsl/cogs/chat/config.rb +453 -0
- data/lib/roast/dsl/cogs/chat/input.rb +92 -0
- data/lib/roast/dsl/cogs/chat/output.rb +64 -0
- data/lib/roast/dsl/cogs/chat/session.rb +68 -0
- data/lib/roast/dsl/cogs/chat.rb +59 -56
- data/lib/roast/dsl/cogs/cmd.rb +251 -61
- data/lib/roast/dsl/cogs/ruby.rb +171 -0
- data/lib/roast/dsl/command_runner.rb +191 -0
- data/lib/roast/dsl/config_manager.rb +58 -11
- data/lib/roast/dsl/control_flow.rb +41 -0
- data/lib/roast/dsl/execution_manager.rb +162 -32
- data/lib/roast/dsl/nil_assertions.rb +23 -0
- data/lib/roast/dsl/system_cog/params.rb +32 -0
- data/lib/roast/dsl/system_cog.rb +36 -0
- data/lib/roast/dsl/system_cogs/call.rb +163 -0
- data/lib/roast/dsl/system_cogs/map.rb +454 -0
- data/lib/roast/dsl/system_cogs/repeat.rb +242 -0
- data/lib/roast/dsl/workflow.rb +26 -16
- data/lib/roast/dsl/workflow_context.rb +20 -0
- data/lib/roast/dsl/workflow_params.rb +24 -0
- data/lib/roast/helpers/minitest_coverage_runner.rb +1 -1
- data/lib/roast/sorbet_runtime_stub.rb +154 -0
- data/lib/roast/tools/apply_diff.rb +1 -3
- data/lib/roast/tools/cmd.rb +4 -3
- data/lib/roast/tools/read_file.rb +1 -1
- data/lib/roast/tools/update_files.rb +1 -1
- data/lib/roast/tools/write_file.rb +1 -1
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/base_workflow.rb +4 -0
- data/lib/roast/workflow/step_loader.rb +14 -2
- data/lib/roast-ai.rb +4 -0
- data/lib/roast.rb +58 -21
- data/{roast.gemspec → roast-ai.gemspec} +9 -13
- data/sorbet/rbi/gems/async@2.34.0.rbi +1577 -0
- data/sorbet/rbi/gems/cli-kit@5.2.0.rbi +2063 -0
- data/sorbet/rbi/gems/{cli-ui@2.3.0.rbi → cli-ui@2.7.0-6bdefd1d06305e5d6ae312ac76f9c88f88658dda.rbi} +1418 -1013
- data/sorbet/rbi/gems/console@1.34.2.rbi +1193 -0
- data/sorbet/rbi/gems/fiber-annotation@0.2.0.rbi +50 -0
- data/sorbet/rbi/gems/fiber-local@1.1.0.rbi +35 -0
- data/sorbet/rbi/gems/fiber-storage@1.0.1.rbi +41 -0
- data/sorbet/rbi/gems/io-event@1.14.0.rbi +724 -0
- data/sorbet/rbi/gems/metrics@0.15.0.rbi +9 -0
- data/sorbet/rbi/gems/traces@0.18.2.rbi +9 -0
- data/sorbet/rbi/shims/lib/roast/dsl/cog_input_context.rbi +1185 -5
- data/sorbet/rbi/shims/lib/roast/dsl/config_context.rbi +311 -5
- data/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi +486 -5
- data/sorbet/tapioca/config.yml +6 -0
- data/sorbet/tapioca/require.rb +2 -0
- metadata +157 -30
- data/dsl/less_simple.rb +0 -112
- data/dsl/scoped_executors.rb +0 -28
- data/dsl/simple.rb +0 -8
- data/lib/roast/dsl/cogs/execute.rb +0 -46
- data/lib/roast/dsl/cogs/graph.rb +0 -53
- data/sorbet/rbi/gems/cgi@0.5.0.rbi +0 -2961
- data/sorbet/rbi/gems/claude_swarm@0.1.19.rbi +0 -568
- data/sorbet/rbi/gems/cli-kit@5.0.1.rbi +0 -1991
- data/sorbet/rbi/gems/dry-configurable@1.3.0.rbi +0 -672
- data/sorbet/rbi/gems/dry-core@1.1.0.rbi +0 -1894
- data/sorbet/rbi/gems/dry-inflector@1.2.0.rbi +0 -659
- data/sorbet/rbi/gems/dry-initializer@3.2.0.rbi +0 -781
- data/sorbet/rbi/gems/dry-logic@1.6.0.rbi +0 -1127
- data/sorbet/rbi/gems/dry-schema@1.14.1.rbi +0 -3727
- data/sorbet/rbi/gems/dry-types@1.8.3.rbi +0 -3969
- data/sorbet/rbi/gems/fast-mcp-annotations@1.5.3.rbi +0 -1588
- data/sorbet/rbi/gems/mime-types-data@3.2025.0617.rbi +0 -136
- data/sorbet/rbi/gems/mime-types@3.7.0.rbi +0 -1342
- data/sorbet/rbi/gems/rack@2.2.19.rbi +0 -5676
- data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +0 -435
- data/sorbet/rbi/gems/yard@0.9.37.rbi +0 -18492
|
@@ -4,20 +4,52 @@
|
|
|
4
4
|
module Roast
|
|
5
5
|
module DSL
|
|
6
6
|
class Cog
|
|
7
|
+
# Registry for managing available cogs in a workflow
|
|
7
8
|
class Registry
|
|
9
|
+
# Parent class for all cog registry errors
|
|
8
10
|
class CogRegistryError < Roast::Error; end
|
|
11
|
+
|
|
12
|
+
# Raised when a cog name cannot be derived from a cog class
|
|
9
13
|
class CouldNotDeriveCogNameError < CogRegistryError; end
|
|
10
14
|
|
|
15
|
+
# Initialize a new cog registry with standard cogs
|
|
16
|
+
#
|
|
17
|
+
# Automatically registers all system cogs and standard cogs provided by core Roast.
|
|
18
|
+
#
|
|
19
|
+
# #### See Also
|
|
20
|
+
# - `use`
|
|
21
|
+
# - `cogs`
|
|
22
|
+
#
|
|
11
23
|
def initialize
|
|
12
24
|
@cogs = {}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
25
|
+
use(SystemCogs::Call)
|
|
26
|
+
use(SystemCogs::Map)
|
|
27
|
+
use(SystemCogs::Repeat)
|
|
28
|
+
use(Cogs::Cmd)
|
|
29
|
+
use(Cogs::Chat)
|
|
30
|
+
use(Cogs::Agent)
|
|
31
|
+
use(Cogs::Ruby)
|
|
16
32
|
end
|
|
17
33
|
|
|
34
|
+
# Hash mapping cog names to cog classes
|
|
35
|
+
#
|
|
36
|
+
# #### See Also
|
|
37
|
+
# - `use`
|
|
38
|
+
#
|
|
18
39
|
#: Hash[Symbol, singleton(Cog)]
|
|
19
40
|
attr_reader :cogs
|
|
20
41
|
|
|
42
|
+
# Register a cog class for use in workflows
|
|
43
|
+
#
|
|
44
|
+
# Adds the provided cog class to the registry, deriving its name from the class name.
|
|
45
|
+
# The cog name is the underscored, demodulized version of the class name
|
|
46
|
+
# (e.g., `Roast::DSL::Cogs::MyCustomCog` becomes `:my_custom_cog`).
|
|
47
|
+
#
|
|
48
|
+
# Raises `CouldNotDeriveCogNameError` if the cog class name cannot be determined.
|
|
49
|
+
#
|
|
50
|
+
# #### See Also
|
|
51
|
+
# - `cogs`
|
|
52
|
+
#
|
|
21
53
|
#: (singleton(Roast::DSL::Cog)) -> void
|
|
22
54
|
def use(cog_class)
|
|
23
55
|
reg = create_registration(cog_class)
|
data/lib/roast/dsl/cog/stack.rb
CHANGED
data/lib/roast/dsl/cog/store.rb
CHANGED
|
@@ -7,7 +7,7 @@ module Roast
|
|
|
7
7
|
class Store
|
|
8
8
|
class CogAlreadyDefinedError < Roast::Error; end
|
|
9
9
|
|
|
10
|
-
delegate :[], to: :store
|
|
10
|
+
delegate :[], :key?, to: :store
|
|
11
11
|
|
|
12
12
|
#: Hash[Symbol, Cog]
|
|
13
13
|
attr_reader :store
|
|
@@ -17,11 +17,11 @@ module Roast
|
|
|
17
17
|
@store = {}
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
#: (
|
|
21
|
-
def insert(
|
|
22
|
-
raise CogAlreadyDefinedError if store.key?(
|
|
20
|
+
#: (Cog) -> Roast::DSL::Cog
|
|
21
|
+
def insert(cog)
|
|
22
|
+
raise CogAlreadyDefinedError, cog.name if store.key?(cog.name)
|
|
23
23
|
|
|
24
|
-
store[
|
|
24
|
+
store[cog.name] = cog
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
end
|
data/lib/roast/dsl/cog.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Roast
|
|
|
6
6
|
class Cog
|
|
7
7
|
class CogError < Roast::Error; end
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class CogAlreadyStartedError < CogError; end
|
|
10
10
|
|
|
11
11
|
class << self
|
|
12
12
|
#: () -> singleton(Cog::Config)
|
|
@@ -19,6 +19,11 @@ module Roast
|
|
|
19
19
|
@input_class ||= find_child_input_or_default #: singleton(Cog::Input)?
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
#: () -> Symbol
|
|
23
|
+
def generate_fallback_name
|
|
24
|
+
Random.uuid.to_sym
|
|
25
|
+
end
|
|
26
|
+
|
|
22
27
|
private
|
|
23
28
|
|
|
24
29
|
#: () -> singleton(Cog::Config)
|
|
@@ -45,29 +50,80 @@ module Roast
|
|
|
45
50
|
@name = name
|
|
46
51
|
@cog_input_proc = cog_input_proc #: ^(Cog::Input) -> untyped
|
|
47
52
|
@output = nil #: Cog::Output?
|
|
48
|
-
@
|
|
53
|
+
@skipped = false #: bool
|
|
54
|
+
@failed = false #: bool
|
|
49
55
|
|
|
50
56
|
# Make sure a config is always defined, so we don't have to worry about nils
|
|
51
|
-
@config = self.class.config_class.new #:
|
|
57
|
+
@config = self.class.config_class.new #: untyped
|
|
52
58
|
end
|
|
53
59
|
|
|
54
|
-
#: (Cog::Config, CogInputContext) ->
|
|
55
|
-
def run!(config, input_context)
|
|
56
|
-
raise
|
|
60
|
+
#: (Async::Barrier, Cog::Config, CogInputContext, untyped, Integer) -> Async::Task
|
|
61
|
+
def run!(barrier, config, input_context, executor_scope_value, executor_scope_index)
|
|
62
|
+
raise CogAlreadyStartedError if @task
|
|
63
|
+
|
|
64
|
+
@task = barrier.async(finished: false) do |task|
|
|
65
|
+
task.annotate("#{self.class.name.not_nil!.demodulize.camelcase} Cog: #{@name}")
|
|
66
|
+
@config = config
|
|
67
|
+
input_instance = self.class.input_class.new
|
|
68
|
+
input_return = input_context.instance_exec(
|
|
69
|
+
input_instance, executor_scope_value, executor_scope_index, &@cog_input_proc
|
|
70
|
+
) if @cog_input_proc
|
|
71
|
+
coerce_and_validate_input!(input_instance, input_return)
|
|
72
|
+
@output = execute(input_instance)
|
|
73
|
+
rescue ControlFlow::SkipCog
|
|
74
|
+
# TODO: do something with the message passed to skip!
|
|
75
|
+
@skipped = true
|
|
76
|
+
rescue ControlFlow::FailCog => e
|
|
77
|
+
# TODO: do something with the message passed to fail!
|
|
78
|
+
@failed = true
|
|
79
|
+
# TODO: better / cleaner handling in the workflow execution manager for a workflow failure
|
|
80
|
+
# just re-raising this exception for now
|
|
81
|
+
raise e if config.abort_on_failure?
|
|
82
|
+
rescue ControlFlow::Next, ControlFlow::Break => e
|
|
83
|
+
@skipped = true
|
|
84
|
+
raise e
|
|
85
|
+
rescue StandardError => e
|
|
86
|
+
@failed = true
|
|
87
|
+
raise e
|
|
88
|
+
end
|
|
89
|
+
end
|
|
57
90
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
@finished = true
|
|
91
|
+
#: () -> void
|
|
92
|
+
def wait
|
|
93
|
+
@task&.wait
|
|
94
|
+
rescue
|
|
95
|
+
# Do nothing if the cog's task raised an exception. That is handled elsewhere.
|
|
64
96
|
end
|
|
65
97
|
|
|
66
98
|
#: () -> bool
|
|
67
|
-
def
|
|
68
|
-
@
|
|
99
|
+
def started?
|
|
100
|
+
@task.present?
|
|
69
101
|
end
|
|
70
102
|
|
|
103
|
+
#: () -> bool
|
|
104
|
+
def skipped?
|
|
105
|
+
@skipped
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
#: () -> bool
|
|
109
|
+
def failed?
|
|
110
|
+
@failed || !!@task&.failed?
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
#: () -> bool
|
|
114
|
+
def stopped?
|
|
115
|
+
!!@task&.stopped?
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
#: () -> bool
|
|
119
|
+
def succeeded?
|
|
120
|
+
# NOTE: explicitly checking `@output == nil` because the `ruby` cog's Output class delegates
|
|
121
|
+
# all missing methods to its `value`, which may be nil when the Output object is actually present.
|
|
122
|
+
@output != nil && @task&.finished? # rubocop:disable Style/NonNilCheck
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
protected
|
|
126
|
+
|
|
71
127
|
# Inheriting cog must implement this
|
|
72
128
|
#: (Cog::Input) -> Cog::Output
|
|
73
129
|
def execute(input)
|
|
@@ -4,6 +4,41 @@
|
|
|
4
4
|
module Roast
|
|
5
5
|
module DSL
|
|
6
6
|
# Context in which the individual cog input blocks within the `execute` block of a workflow definition are evaluated
|
|
7
|
-
class CogInputContext
|
|
7
|
+
class CogInputContext
|
|
8
|
+
include SystemCogs::Call::InputContext
|
|
9
|
+
include SystemCogs::Map::InputContext
|
|
10
|
+
|
|
11
|
+
class CogInputContextError < Roast::Error; end
|
|
12
|
+
|
|
13
|
+
class ContextNotFoundError < CogInputContextError; end
|
|
14
|
+
|
|
15
|
+
#: (?String?) -> void
|
|
16
|
+
def skip!(message = nil)
|
|
17
|
+
raise ControlFlow::SkipCog, message
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
#: (?String?) -> void
|
|
21
|
+
def fail!(message = nil)
|
|
22
|
+
raise ControlFlow::FailCog, message
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
#: (?String?) -> void
|
|
26
|
+
def next!(message = nil)
|
|
27
|
+
raise ControlFlow::Next, message
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
#: (?String?) -> void
|
|
31
|
+
def break!(message = nil)
|
|
32
|
+
raise ControlFlow::Break, message
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#: (String, ?Hash) -> String
|
|
36
|
+
def template(path, args = {})
|
|
37
|
+
path = "prompts/#{path}.md.erb" unless File.exist?(path)
|
|
38
|
+
fail!("The prompt #{path} could not be found") unless File.exist?(path)
|
|
39
|
+
|
|
40
|
+
ERB.new(File.read(path)).result_with_hash(args)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
8
43
|
end
|
|
9
44
|
end
|
|
@@ -6,15 +6,25 @@ module Roast
|
|
|
6
6
|
# Context in which an individual cog block within the `execute` block of a workflow is evaluated
|
|
7
7
|
class CogInputManager
|
|
8
8
|
class CogOutputAccessError < Roast::Error; end
|
|
9
|
+
|
|
10
|
+
class CogDoesNotExistError < CogOutputAccessError; end
|
|
11
|
+
|
|
9
12
|
class CogNotYetRunError < CogOutputAccessError; end
|
|
10
|
-
class CogNotDefinedError < CogOutputAccessError; end
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
class CogSkippedError < CogOutputAccessError; end
|
|
15
|
+
|
|
16
|
+
class CogFailedError < CogOutputAccessError; end
|
|
17
|
+
|
|
18
|
+
class CogStoppedError < CogOutputAccessError; end
|
|
19
|
+
|
|
20
|
+
#: (Cog::Registry, Cog::Store, WorkflowContext) -> void
|
|
21
|
+
def initialize(cog_registry, cogs, workflow_context)
|
|
14
22
|
@cog_registry = cog_registry
|
|
15
23
|
@cogs = cogs
|
|
24
|
+
@workflow_context = workflow_context
|
|
16
25
|
@context = CogInputContext.new
|
|
17
26
|
bind_registered_cogs
|
|
27
|
+
bind_workflow_context
|
|
18
28
|
end
|
|
19
29
|
|
|
20
30
|
#: CogInputContext
|
|
@@ -29,18 +39,117 @@ module Roast
|
|
|
29
39
|
|
|
30
40
|
#: (Symbol) -> void
|
|
31
41
|
def bind_cog(cog_method_name)
|
|
42
|
+
cog_question_method_name = (cog_method_name.to_s + "?").to_sym
|
|
43
|
+
cog_bang_method_name = (cog_method_name.to_s + "!").to_sym
|
|
32
44
|
cog_output_method = method(:cog_output)
|
|
45
|
+
cog_output_question_method = method(:cog_output?)
|
|
46
|
+
cog_output_bang_method = method(:cog_output!)
|
|
33
47
|
@context.instance_eval do
|
|
34
48
|
define_singleton_method(cog_method_name, proc { |cog_name| cog_output_method.call(cog_name) })
|
|
49
|
+
define_singleton_method(cog_question_method_name, proc { |cog_name| cog_output_question_method.call(cog_name) })
|
|
50
|
+
define_singleton_method(cog_bang_method_name, proc { |cog_name| cog_output_bang_method.call(cog_name) })
|
|
35
51
|
end
|
|
36
52
|
end
|
|
37
53
|
|
|
38
|
-
#: (Symbol) -> Cog::Output
|
|
54
|
+
#: (Symbol) -> Cog::Output?
|
|
39
55
|
def cog_output(cog_name)
|
|
56
|
+
cog_output!(cog_name)
|
|
57
|
+
rescue CogOutputAccessError => e
|
|
58
|
+
# Even this method should raise an exception if the requested cog does not exist at all
|
|
59
|
+
raise e if e.is_a?(CogDoesNotExistError)
|
|
60
|
+
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
#: (Symbol) -> bool
|
|
65
|
+
def cog_output?(cog_name)
|
|
66
|
+
!cog_output(cog_name).nil?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
#: (Symbol) -> Cog::Output
|
|
70
|
+
def cog_output!(cog_name)
|
|
71
|
+
raise CogDoesNotExistError, cog_name unless @cogs.key?(cog_name)
|
|
72
|
+
|
|
40
73
|
@cogs[cog_name].tap do |cog|
|
|
41
|
-
|
|
42
|
-
raise
|
|
43
|
-
|
|
74
|
+
cog.wait # attempting to access the output of a running cog will block until that cog completes
|
|
75
|
+
raise CogSkippedError, cog_name if cog.skipped?
|
|
76
|
+
raise CogFailedError, cog_name if cog.failed?
|
|
77
|
+
raise CogStoppedError, cog_name if cog.stopped?
|
|
78
|
+
raise CogNotYetRunError, cog_name unless cog.succeeded?
|
|
79
|
+
end.output.deep_dup
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
#: () -> void
|
|
83
|
+
def bind_workflow_context
|
|
84
|
+
target_bang_method = method(:target!)
|
|
85
|
+
targets_method = method(:targets)
|
|
86
|
+
arg_question_method = method(:arg?)
|
|
87
|
+
args_method = method(:args)
|
|
88
|
+
kwarg_method = method(:kwarg)
|
|
89
|
+
kwarg_bang_method = method(:kwarg!)
|
|
90
|
+
kwarg_question_method = method(:kwarg?)
|
|
91
|
+
kwargs_method = method(:kwargs)
|
|
92
|
+
tmpdir_method = method(:tmpdir)
|
|
93
|
+
@context.instance_eval do
|
|
94
|
+
define_singleton_method(:target!, proc { target_bang_method.call })
|
|
95
|
+
define_singleton_method(:targets, proc { targets_method.call })
|
|
96
|
+
define_singleton_method(:arg?, proc { |value| arg_question_method.call(value) })
|
|
97
|
+
define_singleton_method(:args, proc { args_method.call })
|
|
98
|
+
define_singleton_method(:kwarg, proc { |key| kwarg_method.call(key) })
|
|
99
|
+
define_singleton_method(:kwarg!, proc { |key| kwarg_bang_method.call(key) })
|
|
100
|
+
define_singleton_method(:kwarg?, proc { |key| kwarg_question_method.call(key) })
|
|
101
|
+
define_singleton_method(:kwargs, proc { kwargs_method.call })
|
|
102
|
+
define_singleton_method(:tmpdir, proc { tmpdir_method.call })
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
#: () -> String
|
|
107
|
+
def target!
|
|
108
|
+
raise ArgumentError, "expected exactly one target" unless @workflow_context.params.targets.length == 1
|
|
109
|
+
|
|
110
|
+
@workflow_context.params.targets.first #: as String
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
#: () -> Array[String]
|
|
114
|
+
def targets
|
|
115
|
+
@workflow_context.params.targets.dup
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
#: (Symbol) -> bool
|
|
119
|
+
def arg?(value)
|
|
120
|
+
@workflow_context.params.args.include?(value)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
#: () -> Array[Symbol]
|
|
124
|
+
def args
|
|
125
|
+
@workflow_context.params.args.dup
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
#: (Symbol) -> String?
|
|
129
|
+
def kwarg(key)
|
|
130
|
+
@workflow_context.params.kwargs[key]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
#: (Symbol) -> String
|
|
134
|
+
def kwarg!(key)
|
|
135
|
+
raise ArgumentError, "expected keyword argument '#{key}' to be present" unless @workflow_context.params.kwargs.include?(key)
|
|
136
|
+
|
|
137
|
+
@workflow_context.params.kwargs[key] #: as String
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
#: (Symbol) -> bool
|
|
141
|
+
def kwarg?(key)
|
|
142
|
+
@workflow_context.params.kwargs.include?(key)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
#: () -> Hash[Symbol, String]
|
|
146
|
+
def kwargs
|
|
147
|
+
@workflow_context.params.kwargs.dup
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
#: () -> Pathname
|
|
151
|
+
def tmpdir
|
|
152
|
+
Pathname.new(@workflow_context.tmpdir).realpath
|
|
44
153
|
end
|
|
45
154
|
end
|
|
46
155
|
end
|