roast-ai 0.4.8 → 0.4.10

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +1 -0
  4. data/Gemfile +6 -7
  5. data/Gemfile.lock +15 -3
  6. data/README.md +9 -5
  7. data/dsl/demo/Gemfile +4 -0
  8. data/dsl/demo/Gemfile.lock +120 -0
  9. data/dsl/demo/cogs/local.rb +15 -0
  10. data/dsl/demo/simple_external_cog.rb +17 -0
  11. data/dsl/less_simple.rb +112 -0
  12. data/dsl/plugin-gem-example/.gitignore +8 -0
  13. data/dsl/plugin-gem-example/Gemfile +13 -0
  14. data/dsl/plugin-gem-example/Gemfile.lock +178 -0
  15. data/dsl/plugin-gem-example/lib/other.rb +17 -0
  16. data/dsl/plugin-gem-example/lib/plugin_gem_example.rb +5 -0
  17. data/dsl/plugin-gem-example/lib/simple.rb +15 -0
  18. data/dsl/plugin-gem-example/lib/version.rb +10 -0
  19. data/dsl/plugin-gem-example/plugin-gem-example.gemspec +28 -0
  20. data/dsl/prototype.rb +25 -0
  21. data/dsl/scoped_executors.rb +28 -0
  22. data/dsl/simple.rb +5 -7
  23. data/dsl/simple_chat.rb +12 -0
  24. data/dsl/step_communication.rb +24 -0
  25. data/examples/grading/README.md +46 -0
  26. data/examples/grading/analyze_coverage/prompt.md +52 -0
  27. data/examples/grading/calculate_final_grade.rb +64 -0
  28. data/examples/grading/format_result.rb +61 -0
  29. data/examples/grading/generate_grades/prompt.md +105 -0
  30. data/examples/grading/generate_recommendations/output.txt +17 -0
  31. data/examples/grading/generate_recommendations/prompt.md +60 -0
  32. data/examples/grading/read_dependencies/prompt.md +15 -0
  33. data/examples/grading/verify_mocks_and_stubs/prompt.md +12 -0
  34. data/examples/grading/verify_test_helpers/prompt.md +53 -0
  35. data/examples/grading/workflow.md +5 -0
  36. data/examples/grading/workflow.yml +28 -0
  37. data/lib/roast/dsl/cog/config.rb +36 -0
  38. data/lib/roast/dsl/cog/input.rb +30 -0
  39. data/lib/roast/dsl/cog/output.rb +24 -0
  40. data/lib/roast/dsl/cog/registry.rb +39 -0
  41. data/lib/roast/dsl/cog/stack.rb +22 -0
  42. data/lib/roast/dsl/cog/store.rb +29 -0
  43. data/lib/roast/dsl/cog.rb +91 -0
  44. data/lib/roast/dsl/cog_input_context.rb +9 -0
  45. data/lib/roast/dsl/cog_input_manager.rb +47 -0
  46. data/lib/roast/dsl/cogs/chat.rb +78 -0
  47. data/lib/roast/dsl/cogs/cmd.rb +132 -0
  48. data/lib/roast/dsl/cogs/execute.rb +46 -0
  49. data/lib/roast/dsl/cogs/graph.rb +53 -0
  50. data/lib/roast/dsl/config_context.rb +9 -0
  51. data/lib/roast/dsl/config_manager.rb +96 -0
  52. data/lib/roast/dsl/execution_context.rb +9 -0
  53. data/lib/roast/dsl/execution_manager.rb +137 -0
  54. data/lib/roast/dsl/workflow.rb +113 -0
  55. data/lib/roast/error.rb +7 -0
  56. data/lib/roast/errors.rb +3 -3
  57. data/lib/roast/graph/edge.rb +25 -0
  58. data/lib/roast/graph/node.rb +40 -0
  59. data/lib/roast/graph/quantum_edge.rb +27 -0
  60. data/lib/roast/graph/threaded_exec.rb +93 -0
  61. data/lib/roast/graph.rb +233 -0
  62. data/lib/roast/resources/api_resource.rb +2 -2
  63. data/lib/roast/resources/url_resource.rb +2 -2
  64. data/lib/roast/tools/apply_diff.rb +1 -1
  65. data/lib/roast/tools/ask_user.rb +1 -1
  66. data/lib/roast/tools/bash.rb +1 -1
  67. data/lib/roast/tools/cmd.rb +2 -2
  68. data/lib/roast/tools/coding_agent.rb +2 -2
  69. data/lib/roast/tools/grep.rb +1 -1
  70. data/lib/roast/tools/read_file.rb +1 -1
  71. data/lib/roast/tools/search_file.rb +1 -1
  72. data/lib/roast/tools/swarm.rb +1 -1
  73. data/lib/roast/tools/update_files.rb +2 -2
  74. data/lib/roast/tools/write_file.rb +1 -1
  75. data/lib/roast/tools.rb +1 -1
  76. data/lib/roast/value_objects/api_token.rb +1 -1
  77. data/lib/roast/value_objects/uri_base.rb +1 -1
  78. data/lib/roast/value_objects/workflow_path.rb +1 -1
  79. data/lib/roast/version.rb +1 -1
  80. data/lib/roast/workflow/base_workflow.rb +38 -2
  81. data/lib/roast/workflow/command_executor.rb +1 -1
  82. data/lib/roast/workflow/configuration_loader.rb +1 -1
  83. data/lib/roast/workflow/error_handler.rb +1 -1
  84. data/lib/roast/workflow/step_executor_registry.rb +1 -1
  85. data/lib/roast/workflow/step_loader.rb +1 -1
  86. data/lib/roast/workflow/workflow_executor.rb +1 -1
  87. data/lib/roast.rb +4 -3
  88. data/roast.gemspec +1 -0
  89. data/sorbet/config +3 -0
  90. data/sorbet/rbi/annotations/.gitattributes +1 -0
  91. data/sorbet/rbi/annotations/activesupport.rbi +495 -0
  92. data/sorbet/rbi/annotations/faraday.rbi +17 -0
  93. data/sorbet/rbi/annotations/minitest.rbi +119 -0
  94. data/sorbet/rbi/annotations/mocha.rbi +34 -0
  95. data/sorbet/rbi/annotations/rainbow.rbi +269 -0
  96. data/sorbet/rbi/annotations/webmock.rbi +9 -0
  97. data/sorbet/rbi/gems/marcel@1.1.0.rbi +239 -0
  98. data/sorbet/rbi/gems/{rack@2.2.17.rbi → rack@2.2.19.rbi} +55 -38
  99. data/sorbet/rbi/gems/{rexml@3.4.1.rbi → rexml@3.4.2.rbi} +284 -239
  100. data/sorbet/rbi/gems/ruby_llm@1.8.2.rbi +5703 -0
  101. data/sorbet/rbi/shims/lib/roast/dsl/cog_input_context.rbi +17 -0
  102. data/sorbet/rbi/shims/lib/roast/dsl/config_context.rbi +17 -0
  103. data/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi +17 -0
  104. data/sorbet/rbi/todo.rbi +7 -0
  105. metadata +84 -6
  106. data/lib/roast/dsl/executor.rb +0 -27
  107. data/package-lock.json +0 -6
@@ -0,0 +1,28 @@
1
+ name: Test Grading
2
+
3
+ tools:
4
+ - Roast::Tools::Grep
5
+ - Roast::Tools::ReadFile
6
+ - Roast::Tools::SearchFile
7
+
8
+ steps:
9
+ - read_dependencies
10
+ -
11
+ - verify_test_helpers
12
+ - verify_mocks_and_stubs
13
+ - generate_grades
14
+ - calculate_final_grade
15
+ - format_result
16
+ - generate_recommendations
17
+
18
+ analyze_coverage:
19
+ json: true
20
+
21
+ generate_grades:
22
+ model: o3
23
+ json: true
24
+
25
+ generate_recommendations:
26
+ model: o3
27
+ json: true
28
+
@@ -0,0 +1,36 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ class Cog
7
+ class Config
8
+ #: Hash[Symbol, untyped]
9
+ attr_reader :values
10
+
11
+ #: (?Hash[Symbol, untyped]) -> void
12
+ def initialize(initial = {})
13
+ @values = initial
14
+ end
15
+
16
+ #: (Cog::Config) -> Cog::Config
17
+ def merge(config_object)
18
+ self.class.new(values.merge(config_object.values))
19
+ end
20
+
21
+ # It is recommended to implement a custom config object for a nicer interface,
22
+ # but for simple cases where it would just be a key value store we provide one by default.
23
+
24
+ #: (Symbol, untyped) -> void
25
+ def []=(key, value)
26
+ @values[key] = value
27
+ end
28
+
29
+ #: (Symbol) -> untyped
30
+ def [](key)
31
+ @values[key]
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,30 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ class Cog
7
+ # Abstract parent class for inputs provided to a cog when it runs.
8
+ # Cogs should extend this class with their own input types
9
+ # Input classes should be instantiatable with a no-arg constructor,
10
+ # and expose methods to incrementally set their values.
11
+ class Input
12
+ class InputError < Roast::Error; end
13
+ class InvalidInputError < InputError; end
14
+
15
+ # Validate that the input instance has all required parameters set in an acceptable manner.
16
+ # Inheriting cog must implement this for its input class.
17
+ #: () -> void
18
+ def validate!
19
+ raise NotImplementedError
20
+ end
21
+
22
+ # Use the value returned from the cog's input block to attempt to coerce the input to a valid state.
23
+ # to coerce the input to a valid state.
24
+ # Inheriting cog may implement this for its input class; it is optional
25
+ #: (untyped) -> void
26
+ def coerce(input_return_value); end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ class Cog
7
+ # Generic output from running a cog.
8
+ # Cogs should extend this class with their own output types.
9
+ class Output
10
+ #: bool
11
+ attr_reader :success
12
+
13
+ #: bool
14
+ attr_reader :abort
15
+
16
+ #: (?success: bool, ?abort_workflow: bool) -> void
17
+ def initialize(success: true, abort_workflow: false)
18
+ @success = success #: bool
19
+ @abort = abort_workflow #: bool
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,39 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ class Cog
7
+ class Registry
8
+ class CogRegistryError < Roast::Error; end
9
+ class CouldNotDeriveCogNameError < CogRegistryError; end
10
+
11
+ def initialize
12
+ @cogs = {}
13
+ create_registration(Cogs::Cmd)
14
+ create_registration(Cogs::Chat)
15
+ create_registration(Cogs::Execute)
16
+ end
17
+
18
+ #: Hash[Symbol, singleton(Cog)]
19
+ attr_reader :cogs
20
+
21
+ #: (singleton(Roast::DSL::Cog)) -> void
22
+ def use(cog_class)
23
+ reg = create_registration(cog_class)
24
+ cogs[reg.first] = reg.second
25
+ end
26
+
27
+ private
28
+
29
+ #: (singleton(Roast::DSL::Cog)) -> Array(Symbol, singleton(Cog))
30
+ def create_registration(cog_class)
31
+ cog_class_name = cog_class.name
32
+ raise CouldNotDeriveCogNameError if cog_class_name.nil?
33
+
34
+ [cog_class_name.demodulize.underscore.to_sym, cog_class]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,22 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ class Cog
7
+ class Stack
8
+ delegate :map, :push, :size, :empty?, to: :@queue
9
+
10
+ #: () -> void
11
+ def initialize
12
+ @queue = [] #: Array[Cog]
13
+ end
14
+
15
+ #: () -> Roast::DSL::Cog?
16
+ def pop
17
+ @queue.shift
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ class Cog
7
+ class Store
8
+ class CogAlreadyDefinedError < Roast::Error; end
9
+
10
+ delegate :[], to: :store
11
+
12
+ #: Hash[Symbol, Cog]
13
+ attr_reader :store
14
+
15
+ #: () -> void
16
+ def initialize
17
+ @store = {}
18
+ end
19
+
20
+ #: (Symbol, Roast::DSL::Cog) -> Roast::DSL::Cog
21
+ def insert(id, inst)
22
+ raise CogAlreadyDefinedError if store.key?(id)
23
+
24
+ store[id] = inst
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,91 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ class Cog
7
+ class CogError < Roast::Error; end
8
+
9
+ class CogAlreadyRanError < CogError; end
10
+
11
+ class << self
12
+ #: () -> singleton(Cog::Config)
13
+ def config_class
14
+ @config_class ||= find_child_config_or_default
15
+ end
16
+
17
+ #: () -> singleton(Cog::Input)
18
+ def input_class
19
+ @input_class ||= find_child_input_or_default #: singleton(Cog::Input)?
20
+ end
21
+
22
+ private
23
+
24
+ #: () -> singleton(Cog::Config)
25
+ def find_child_config_or_default
26
+ config_constant = "#{name}::Config"
27
+ const_defined?(config_constant) ? const_get(config_constant) : Cog::Config # rubocop:disable Sorbet/ConstantsFromStrings
28
+ end
29
+
30
+ #: () -> singleton(Cog::Input)
31
+ def find_child_input_or_default
32
+ input_constant = "#{name}::Input"
33
+ const_defined?(input_constant) ? const_get(input_constant) : Cog::Input # rubocop:disable Sorbet/ConstantsFromStrings
34
+ end
35
+ end
36
+
37
+ #: Symbol
38
+ attr_reader :name
39
+
40
+ #: Cog::Output?
41
+ attr_reader :output
42
+
43
+ #: (Symbol, ^(Cog::Input) -> untyped) -> void
44
+ def initialize(name, cog_input_proc)
45
+ @name = name
46
+ @cog_input_proc = cog_input_proc #: ^(Cog::Input) -> untyped
47
+ @output = nil #: Cog::Output?
48
+ @finished = false #: bool
49
+
50
+ # 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
52
+ end
53
+
54
+ #: (Cog::Config, CogInputContext) -> void
55
+ def run!(config, input_context)
56
+ raise CogAlreadyRanError if ran?
57
+
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
64
+ end
65
+
66
+ #: () -> bool
67
+ def ran?
68
+ @finished
69
+ end
70
+
71
+ # Inheriting cog must implement this
72
+ #: (Cog::Input) -> Cog::Output
73
+ def execute(input)
74
+ raise NotImplementedError
75
+ end
76
+
77
+ private
78
+
79
+ #: (Cog::Input, untyped) -> void
80
+ def coerce_and_validate_input!(input, return_value)
81
+ # Check if the input is already valid
82
+ input.validate!
83
+ rescue Cog::Input::InvalidInputError
84
+ # If it's not valid, attempt to coerce if possible
85
+ input.coerce(return_value)
86
+ # Re-validate because coerce! should not be responsible for validation
87
+ input.validate!
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,9 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ # Context in which the individual cog input blocks within the `execute` block of a workflow definition are evaluated
7
+ class CogInputContext; end
8
+ end
9
+ end
@@ -0,0 +1,47 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ # Context in which an individual cog block within the `execute` block of a workflow is evaluated
7
+ class CogInputManager
8
+ class CogOutputAccessError < Roast::Error; end
9
+ class CogNotYetRunError < CogOutputAccessError; end
10
+ class CogNotDefinedError < CogOutputAccessError; end
11
+
12
+ #: (Cog::Registry, Cog::Store) -> void
13
+ def initialize(cog_registry, cogs)
14
+ @cog_registry = cog_registry
15
+ @cogs = cogs
16
+ @context = CogInputContext.new
17
+ bind_registered_cogs
18
+ end
19
+
20
+ #: CogInputContext
21
+ attr_reader :context
22
+
23
+ private
24
+
25
+ #: () -> void
26
+ def bind_registered_cogs
27
+ @cog_registry.cogs.keys.each(&method(:bind_cog))
28
+ end
29
+
30
+ #: (Symbol) -> void
31
+ def bind_cog(cog_method_name)
32
+ cog_output_method = method(:cog_output)
33
+ @context.instance_eval do
34
+ define_singleton_method(cog_method_name, proc { |cog_name| cog_output_method.call(cog_name) })
35
+ end
36
+ end
37
+
38
+ #: (Symbol) -> Cog::Output
39
+ def cog_output(cog_name)
40
+ @cogs[cog_name].tap do |cog|
41
+ raise CogNotYetRunError unless cog.ran?
42
+ raise CogNotDefinedError unless cog.output.present?
43
+ end.output
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,78 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ module Cogs
7
+ class Chat < Cog
8
+ class Input < Cog::Input
9
+ #: String?
10
+ attr_accessor :prompt
11
+
12
+ #: () -> void
13
+ def initialize
14
+ super
15
+ @prompt = nil #: String?
16
+ end
17
+
18
+ #: () -> void
19
+ def validate!
20
+ raise Cog::Input::InvalidInputError, "'prompt' is required" unless prompt.present?
21
+ end
22
+
23
+ #: (untyped) -> void
24
+ def coerce!(input_return_value)
25
+ if input_return_value.is_a?(String)
26
+ self.prompt = input_return_value
27
+ end
28
+ end
29
+ end
30
+
31
+ class Output < Cog::Output
32
+ #: String
33
+ attr_reader :response
34
+
35
+ #: (String response) -> void
36
+ def initialize(response)
37
+ super()
38
+ @response = response
39
+ end
40
+ end
41
+
42
+ class Config < Cog::Config
43
+ #: () -> String?
44
+ def openai_api_key
45
+ @values[:openai_api_key] ||= ENV["OPENAI_API_KEY"]
46
+ end
47
+
48
+ #: () -> String?
49
+ def openai_api_base_url
50
+ @values[:openai_api_base_url] ||= ENV["OPENAI_API_BASE_URL"]
51
+ end
52
+ end
53
+
54
+ #: (Input) -> Output
55
+ def execute(input)
56
+ config = @config #: as Config
57
+ RubyLLM.configure do |ruby_llm_config|
58
+ ruby_llm_config.openai_api_key = config.openai_api_key
59
+ ruby_llm_config.openai_api_base = config.openai_api_base_url
60
+ end
61
+
62
+ chat = RubyLLM.chat
63
+ resp = chat.ask(input.prompt)
64
+ puts "Model: #{resp.model_id}"
65
+ puts "Role: #{resp.role}"
66
+ puts "Input Tokens: #{resp.input_tokens}"
67
+ puts "Output Tokens: #{resp.output_tokens}"
68
+
69
+ chat.messages.each do |message|
70
+ puts "[#{message.role.to_s.upcase}] #{message.content}"
71
+ end
72
+
73
+ Output.new(resp.content)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,132 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ module Cogs
7
+ class Cmd < Cog
8
+ class Input < Cog::Input
9
+ #: String?
10
+ attr_accessor :command
11
+
12
+ #: Array[String]
13
+ attr_accessor :args
14
+
15
+ #: () -> void
16
+ def initialize
17
+ super
18
+ @command = nil
19
+ @args = []
20
+ end
21
+
22
+ #: () -> void
23
+ def validate!
24
+ raise Cog::Input::InvalidInputError, "'command' is required" unless command.present?
25
+ end
26
+
27
+ #: (String | Array[untyped]) -> void
28
+ def coerce(input_return_value)
29
+ case input_return_value
30
+ when String
31
+ self.command = input_return_value
32
+ when Array
33
+ input_return_value.map!(&:to_s)
34
+ self.command = input_return_value.shift
35
+ self.args = input_return_value
36
+ end
37
+ end
38
+ end
39
+
40
+ class Output < Cog::Output
41
+ #: String
42
+ attr_reader :out
43
+
44
+ #: String
45
+ attr_reader :err
46
+
47
+ #: Process::Status
48
+ attr_reader :status
49
+
50
+ #: ( String, String, Process::Status) -> void
51
+ def initialize(out, err, status)
52
+ super()
53
+ @out = out #: String
54
+ @err = err #: String
55
+ @status = status #: Process::Status
56
+ end
57
+ end
58
+
59
+ class Config < Cog::Config
60
+ #: () -> void
61
+ def print_all!
62
+ @values[:print_stdout] = true
63
+ @values[:print_stderr] = true
64
+ end
65
+
66
+ #: () -> void
67
+ def print_stdout!
68
+ @values[:print_stdout] = true
69
+ end
70
+
71
+ #: () -> void
72
+ def print_stderr!
73
+ @values[:print_stderr] = true
74
+ end
75
+
76
+ #: () -> bool
77
+ def print_stdout?
78
+ !!@values[:print_stdout]
79
+ end
80
+
81
+ #: () -> bool
82
+ def print_stderr?
83
+ !!@values[:print_stderr]
84
+ end
85
+
86
+ #: () -> void
87
+ def display!
88
+ print_all!
89
+ end
90
+ end
91
+
92
+ #: (Input) -> Output
93
+ def execute(input)
94
+ config = @config #: as Config
95
+ result = T.unsafe(Roast::Helpers::CmdRunner).popen3(input.command, *input.args) do |stdin, stdout, stderr, wait_thread|
96
+ stdin.close
97
+ command_output = ""
98
+ command_error = ""
99
+
100
+ # Thread to read and accumulate stdout
101
+ stdout_thread = Thread.new do
102
+ stdout.each_line do |line|
103
+ command_output += line
104
+ $stdout.puts(line) if config.print_stdout?
105
+ end
106
+ rescue IOError => e
107
+ Roast::Helpers::Logger.debug("IOError while reading stdout: #{e.message}")
108
+ end
109
+
110
+ # Thread to read and accumulate stderr
111
+ stderr_thread = Thread.new do
112
+ stderr.each_line do |line|
113
+ command_error += line
114
+ $stderr.puts(line) if config.print_stderr?
115
+ end
116
+ rescue IOError => e
117
+ Roast::Helpers::Logger.debug("IOError while reading stderr: #{e.message}")
118
+ end
119
+
120
+ # Wait for threads to finish
121
+ stdout_thread.join
122
+ stderr_thread.join
123
+
124
+ [command_output, command_error, wait_thread.value]
125
+ end #: as [String, String, Process::Status]
126
+
127
+ Output.new(*result)
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,46 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ module Cogs
7
+ class Execute < Cog
8
+ class Input < Cog::Input
9
+ #: Symbol?
10
+ attr_accessor :scope
11
+
12
+ #: () -> void
13
+ def initialize
14
+ super
15
+ @scope = nil
16
+ end
17
+
18
+ #: () -> void
19
+ def validate!
20
+ raise Cog::Input::InvalidInputError, "'scope' is required" unless scope.present?
21
+ end
22
+
23
+ #: (Symbol) -> void
24
+ def coerce(input_return_value)
25
+ self.scope = input_return_value
26
+ end
27
+ end
28
+
29
+ #: (Symbol, ^(Input) -> untyped, ^(Input) -> void) -> void
30
+ def initialize(name, cog_input_proc, trigger)
31
+ # NOTE: Sorbet expects the proc passed to super to be declared as taking a Cog::Input explicitly,
32
+ # not a subclass of Cog::Input.
33
+ cog_input_proc = cog_input_proc #: as ^(Cog::Input) -> untyped
34
+ super(name, cog_input_proc)
35
+ @trigger = trigger
36
+ end
37
+
38
+ #: (Input) -> Cog::Output
39
+ def execute(input)
40
+ @trigger.call(input)
41
+ Cog::Output.new
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,53 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ module Cogs
7
+ class Graph < Roast::DSL::Cog
8
+ #: Proc
9
+ attr_reader :block
10
+
11
+ #: (Symbol) { (Cog::Input) -> untyped } -> void
12
+ def initialize(name, &block)
13
+ @name = name
14
+ @block = block
15
+ @graph = Roast::Graph.new
16
+ super(name, block)
17
+ end
18
+
19
+ #: () -> void
20
+ def on_invoke
21
+ populate!(@graph)
22
+ end
23
+
24
+ #: () -> Symbol
25
+ def store_id
26
+ @name.to_sym
27
+ end
28
+
29
+ #: (Roast::DSL::Cog) -> void
30
+ def update(other)
31
+ return unless other.is_a?(Roast::DSL::Cogs::Graph)
32
+
33
+ return if other.block.nil?
34
+
35
+ other.populate!(@graph)
36
+ end
37
+
38
+ # Populates the provided graph in-place, with the definition of how to populate in the block
39
+ #: (Cog::Input) -> void
40
+ def populate!(graph)
41
+ return if @block.nil?
42
+
43
+ @block.call(graph)
44
+ end
45
+
46
+ #: () -> void
47
+ def execute
48
+ @graph.execute
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ # Context in which the `config` blocks of a workflow definition are evaluated
7
+ class ConfigContext; end
8
+ end
9
+ end