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.
Files changed (175) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/docs/write-comments.md +36 -0
  3. data/.github/CODEOWNERS +1 -1
  4. data/.github/workflows/ci.yaml +10 -6
  5. data/.gitignore +0 -1
  6. data/.rubocop.yml +7 -1
  7. data/.ruby-version +1 -1
  8. data/CLAUDE.md +2 -2
  9. data/CONTRIBUTING.md +2 -0
  10. data/Gemfile +19 -18
  11. data/Gemfile.lock +35 -58
  12. data/README.md +118 -1432
  13. data/README_LEGACY.md +1464 -0
  14. data/Rakefile +39 -4
  15. data/dev.yml +29 -0
  16. data/dsl/agent_sessions.rb +20 -0
  17. data/dsl/async_cogs.rb +49 -0
  18. data/dsl/async_cogs_complex.rb +67 -0
  19. data/dsl/call.rb +44 -0
  20. data/dsl/collect_from.rb +72 -0
  21. data/dsl/json_output.rb +28 -0
  22. data/dsl/map.rb +55 -0
  23. data/dsl/map_reduce.rb +37 -0
  24. data/dsl/map_with_index.rb +49 -0
  25. data/dsl/next_break.rb +45 -0
  26. data/dsl/next_break_parallel.rb +44 -0
  27. data/dsl/outputs.rb +39 -0
  28. data/dsl/outputs_bang.rb +36 -0
  29. data/dsl/parallel_map.rb +37 -0
  30. data/dsl/prompts/simple_prompt.md.erb +3 -0
  31. data/dsl/prototype.rb +5 -7
  32. data/dsl/repeat_loop_results.rb +53 -0
  33. data/dsl/ruby_cog.rb +72 -0
  34. data/dsl/simple_agent.rb +18 -0
  35. data/dsl/simple_chat.rb +15 -1
  36. data/dsl/simple_repeat.rb +29 -0
  37. data/dsl/skip.rb +36 -0
  38. data/dsl/step_communication.rb +2 -3
  39. data/dsl/targets_and_params.rb +57 -0
  40. data/dsl/temperature.rb +17 -0
  41. data/dsl/temporary_directory.rb +22 -0
  42. data/dsl/tutorial/01_your_first_workflow/README.md +179 -0
  43. data/dsl/tutorial/01_your_first_workflow/configured_chat.rb +33 -0
  44. data/dsl/tutorial/01_your_first_workflow/hello.rb +23 -0
  45. data/dsl/tutorial/02_chaining_cogs/README.md +310 -0
  46. data/dsl/tutorial/02_chaining_cogs/code_review.rb +104 -0
  47. data/dsl/tutorial/02_chaining_cogs/session_resumption.rb +92 -0
  48. data/dsl/tutorial/02_chaining_cogs/simple_chain.rb +84 -0
  49. data/dsl/tutorial/03_targets_and_params/README.md +230 -0
  50. data/dsl/tutorial/03_targets_and_params/multiple_targets.rb +65 -0
  51. data/dsl/tutorial/03_targets_and_params/single_target.rb +65 -0
  52. data/dsl/tutorial/04_configuration_options/README.md +209 -0
  53. data/dsl/tutorial/04_configuration_options/control_display_and_temperature.rb +104 -0
  54. data/dsl/tutorial/04_configuration_options/simple_config.rb +68 -0
  55. data/dsl/tutorial/05_control_flow/README.md +156 -0
  56. data/dsl/tutorial/05_control_flow/conditional_execution.rb +62 -0
  57. data/dsl/tutorial/05_control_flow/handling_failures.rb +77 -0
  58. data/dsl/tutorial/06_reusable_scopes/README.md +172 -0
  59. data/dsl/tutorial/06_reusable_scopes/accessing_scope_outputs.rb +126 -0
  60. data/dsl/tutorial/06_reusable_scopes/basic_scope.rb +63 -0
  61. data/dsl/tutorial/06_reusable_scopes/parameterized_scope.rb +78 -0
  62. data/dsl/tutorial/07_processing_collections/README.md +152 -0
  63. data/dsl/tutorial/07_processing_collections/basic_map.rb +70 -0
  64. data/dsl/tutorial/07_processing_collections/parallel_map.rb +74 -0
  65. data/dsl/tutorial/08_iterative_workflows/README.md +231 -0
  66. data/dsl/tutorial/08_iterative_workflows/basic_repeat.rb +57 -0
  67. data/dsl/tutorial/08_iterative_workflows/conditional_break.rb +57 -0
  68. data/dsl/tutorial/09_async_cogs/README.md +197 -0
  69. data/dsl/tutorial/09_async_cogs/basic_async.rb +38 -0
  70. data/dsl/tutorial/README.md +222 -0
  71. data/dsl/working_directory.rb +16 -0
  72. data/exe/roast +1 -1
  73. data/internal/documentation/architectural-notes.md +115 -0
  74. data/internal/documentation/doc-comments-external.md +686 -0
  75. data/internal/documentation/doc-comments-internal.md +342 -0
  76. data/internal/documentation/doc-comments.md +211 -0
  77. data/lib/roast/dsl/cog/config.rb +274 -3
  78. data/lib/roast/dsl/cog/input.rb +53 -10
  79. data/lib/roast/dsl/cog/output.rb +297 -8
  80. data/lib/roast/dsl/cog/registry.rb +35 -3
  81. data/lib/roast/dsl/cog/stack.rb +1 -1
  82. data/lib/roast/dsl/cog/store.rb +5 -5
  83. data/lib/roast/dsl/cog.rb +70 -14
  84. data/lib/roast/dsl/cog_input_context.rb +36 -1
  85. data/lib/roast/dsl/cog_input_manager.rb +116 -7
  86. data/lib/roast/dsl/cogs/agent/config.rb +465 -0
  87. data/lib/roast/dsl/cogs/agent/input.rb +81 -0
  88. data/lib/roast/dsl/cogs/agent/output.rb +59 -0
  89. data/lib/roast/dsl/cogs/agent/provider.rb +51 -0
  90. data/lib/roast/dsl/cogs/agent/providers/claude/claude_invocation.rb +185 -0
  91. data/lib/roast/dsl/cogs/agent/providers/claude/message.rb +73 -0
  92. data/lib/roast/dsl/cogs/agent/providers/claude/messages/assistant_message.rb +36 -0
  93. data/lib/roast/dsl/cogs/agent/providers/claude/messages/result_message.rb +61 -0
  94. data/lib/roast/dsl/cogs/agent/providers/claude/messages/system_message.rb +47 -0
  95. data/lib/roast/dsl/cogs/agent/providers/claude/messages/text_message.rb +36 -0
  96. data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_result_message.rb +47 -0
  97. data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_use_message.rb +46 -0
  98. data/lib/roast/dsl/cogs/agent/providers/claude/messages/unknown_message.rb +27 -0
  99. data/lib/roast/dsl/cogs/agent/providers/claude/messages/user_message.rb +37 -0
  100. data/lib/roast/dsl/cogs/agent/providers/claude/tool_result.rb +51 -0
  101. data/lib/roast/dsl/cogs/agent/providers/claude/tool_use.rb +48 -0
  102. data/lib/roast/dsl/cogs/agent/providers/claude.rb +31 -0
  103. data/lib/roast/dsl/cogs/agent/stats.rb +92 -0
  104. data/lib/roast/dsl/cogs/agent/usage.rb +62 -0
  105. data/lib/roast/dsl/cogs/agent.rb +75 -0
  106. data/lib/roast/dsl/cogs/chat/config.rb +453 -0
  107. data/lib/roast/dsl/cogs/chat/input.rb +92 -0
  108. data/lib/roast/dsl/cogs/chat/output.rb +64 -0
  109. data/lib/roast/dsl/cogs/chat/session.rb +68 -0
  110. data/lib/roast/dsl/cogs/chat.rb +59 -56
  111. data/lib/roast/dsl/cogs/cmd.rb +251 -61
  112. data/lib/roast/dsl/cogs/ruby.rb +171 -0
  113. data/lib/roast/dsl/command_runner.rb +191 -0
  114. data/lib/roast/dsl/config_manager.rb +58 -11
  115. data/lib/roast/dsl/control_flow.rb +41 -0
  116. data/lib/roast/dsl/execution_manager.rb +162 -32
  117. data/lib/roast/dsl/nil_assertions.rb +23 -0
  118. data/lib/roast/dsl/system_cog/params.rb +32 -0
  119. data/lib/roast/dsl/system_cog.rb +36 -0
  120. data/lib/roast/dsl/system_cogs/call.rb +163 -0
  121. data/lib/roast/dsl/system_cogs/map.rb +454 -0
  122. data/lib/roast/dsl/system_cogs/repeat.rb +242 -0
  123. data/lib/roast/dsl/workflow.rb +26 -16
  124. data/lib/roast/dsl/workflow_context.rb +20 -0
  125. data/lib/roast/dsl/workflow_params.rb +24 -0
  126. data/lib/roast/helpers/minitest_coverage_runner.rb +1 -1
  127. data/lib/roast/sorbet_runtime_stub.rb +154 -0
  128. data/lib/roast/tools/apply_diff.rb +1 -3
  129. data/lib/roast/tools/cmd.rb +4 -3
  130. data/lib/roast/tools/read_file.rb +1 -1
  131. data/lib/roast/tools/update_files.rb +1 -1
  132. data/lib/roast/tools/write_file.rb +1 -1
  133. data/lib/roast/version.rb +1 -1
  134. data/lib/roast/workflow/base_workflow.rb +4 -0
  135. data/lib/roast/workflow/step_loader.rb +14 -2
  136. data/lib/roast-ai.rb +4 -0
  137. data/lib/roast.rb +58 -21
  138. data/{roast.gemspec → roast-ai.gemspec} +9 -13
  139. data/sorbet/rbi/gems/async@2.34.0.rbi +1577 -0
  140. data/sorbet/rbi/gems/cli-kit@5.2.0.rbi +2063 -0
  141. data/sorbet/rbi/gems/{cli-ui@2.3.0.rbi → cli-ui@2.7.0-6bdefd1d06305e5d6ae312ac76f9c88f88658dda.rbi} +1418 -1013
  142. data/sorbet/rbi/gems/console@1.34.2.rbi +1193 -0
  143. data/sorbet/rbi/gems/fiber-annotation@0.2.0.rbi +50 -0
  144. data/sorbet/rbi/gems/fiber-local@1.1.0.rbi +35 -0
  145. data/sorbet/rbi/gems/fiber-storage@1.0.1.rbi +41 -0
  146. data/sorbet/rbi/gems/io-event@1.14.0.rbi +724 -0
  147. data/sorbet/rbi/gems/metrics@0.15.0.rbi +9 -0
  148. data/sorbet/rbi/gems/traces@0.18.2.rbi +9 -0
  149. data/sorbet/rbi/shims/lib/roast/dsl/cog_input_context.rbi +1185 -5
  150. data/sorbet/rbi/shims/lib/roast/dsl/config_context.rbi +311 -5
  151. data/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi +486 -5
  152. data/sorbet/tapioca/config.yml +6 -0
  153. data/sorbet/tapioca/require.rb +2 -0
  154. metadata +157 -30
  155. data/dsl/less_simple.rb +0 -112
  156. data/dsl/scoped_executors.rb +0 -28
  157. data/dsl/simple.rb +0 -8
  158. data/lib/roast/dsl/cogs/execute.rb +0 -46
  159. data/lib/roast/dsl/cogs/graph.rb +0 -53
  160. data/sorbet/rbi/gems/cgi@0.5.0.rbi +0 -2961
  161. data/sorbet/rbi/gems/claude_swarm@0.1.19.rbi +0 -568
  162. data/sorbet/rbi/gems/cli-kit@5.0.1.rbi +0 -1991
  163. data/sorbet/rbi/gems/dry-configurable@1.3.0.rbi +0 -672
  164. data/sorbet/rbi/gems/dry-core@1.1.0.rbi +0 -1894
  165. data/sorbet/rbi/gems/dry-inflector@1.2.0.rbi +0 -659
  166. data/sorbet/rbi/gems/dry-initializer@3.2.0.rbi +0 -781
  167. data/sorbet/rbi/gems/dry-logic@1.6.0.rbi +0 -1127
  168. data/sorbet/rbi/gems/dry-schema@1.14.1.rbi +0 -3727
  169. data/sorbet/rbi/gems/dry-types@1.8.3.rbi +0 -3969
  170. data/sorbet/rbi/gems/fast-mcp-annotations@1.5.3.rbi +0 -1588
  171. data/sorbet/rbi/gems/mime-types-data@3.2025.0617.rbi +0 -136
  172. data/sorbet/rbi/gems/mime-types@3.7.0.rbi +0 -1342
  173. data/sorbet/rbi/gems/rack@2.2.19.rbi +0 -5676
  174. data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +0 -435
  175. 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
- create_registration(Cogs::Cmd)
14
- create_registration(Cogs::Chat)
15
- create_registration(Cogs::Execute)
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)
@@ -5,7 +5,7 @@ module Roast
5
5
  module DSL
6
6
  class Cog
7
7
  class Stack
8
- delegate :map, :push, :size, :empty?, to: :@queue
8
+ delegate :each, :empty?, :last, :map, :push, :size, to: :@queue
9
9
 
10
10
  #: () -> void
11
11
  def initialize
@@ -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
- #: (Symbol, Roast::DSL::Cog) -> Roast::DSL::Cog
21
- def insert(id, inst)
22
- raise CogAlreadyDefinedError if store.key?(id)
20
+ #: (Cog) -> Roast::DSL::Cog
21
+ def insert(cog)
22
+ raise CogAlreadyDefinedError, cog.name if store.key?(cog.name)
23
23
 
24
- store[id] = inst
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 CogAlreadyRanError < CogError; end
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
- @finished = false #: bool
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 #: Cog::Config
57
+ @config = self.class.config_class.new #: untyped
52
58
  end
53
59
 
54
- #: (Cog::Config, CogInputContext) -> void
55
- def run!(config, input_context)
56
- raise CogAlreadyRanError if ran?
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
- @config = config
59
- input_instance = self.class.input_class.new
60
- input_return = input_context.instance_exec(input_instance, &@cog_input_proc) if @cog_input_proc
61
- coerce_and_validate_input!(input_instance, input_return)
62
- @output = execute(input_instance)
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 ran?
68
- @finished
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; end
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
- #: (Cog::Registry, Cog::Store) -> void
13
- def initialize(cog_registry, cogs)
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
- raise CogNotYetRunError unless cog.ran?
42
- raise CogNotDefinedError unless cog.output.present?
43
- end.output
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