wolflow 0.0.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 +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +239 -0
- data/README.md +393 -0
- data/lib/wolflow/cycle.rb +67 -0
- data/lib/wolflow/dsl.rb +93 -0
- data/lib/wolflow/errors.rb +13 -0
- data/lib/wolflow/exclusive_choice.rb +106 -0
- data/lib/wolflow/extensions.rb +20 -0
- data/lib/wolflow/multi_choice.rb +127 -0
- data/lib/wolflow/multi_merge.rb +20 -0
- data/lib/wolflow/operators/attribute.rb +22 -0
- data/lib/wolflow/operators/base.rb +67 -0
- data/lib/wolflow/operators/literal.rb +22 -0
- data/lib/wolflow/operators/operation.rb +42 -0
- data/lib/wolflow/recursion.rb +21 -0
- data/lib/wolflow/simple.rb +134 -0
- data/lib/wolflow/simple_merge.rb +19 -0
- data/lib/wolflow/start.rb +9 -0
- data/lib/wolflow/structured_loop.rb +106 -0
- data/lib/wolflow/structured_synchronized_merge.rb +45 -0
- data/lib/wolflow/synchronization.rb +17 -0
- data/lib/wolflow/task.rb +195 -0
- data/lib/wolflow/task_spec.rb +114 -0
- data/lib/wolflow/version.rb +5 -0
- data/lib/wolflow/workflow.rb +82 -0
- data/lib/wolflow/workflow_spec.rb +82 -0
- data/lib/wolflow.rb +25 -0
- data/sig/cycle.rbs +7 -0
- data/sig/dsl.rbs +36 -0
- data/sig/exclusive_choice.rbs +12 -0
- data/sig/multi_choice.rbs +23 -0
- data/sig/multi_merge.rbs +6 -0
- data/sig/operators/attribute.rbs +11 -0
- data/sig/operators/base.rbs +21 -0
- data/sig/operators/literal.rbs +15 -0
- data/sig/operators/operation.rbs +10 -0
- data/sig/operators.rbs +7 -0
- data/sig/recursion.rbs +4 -0
- data/sig/simple.rbs +22 -0
- data/sig/simple_merge.rbs +6 -0
- data/sig/start.rbs +4 -0
- data/sig/structured_loop.rbs +8 -0
- data/sig/structured_synchronized_merge.rbs +6 -0
- data/sig/synchronization.rbs +6 -0
- data/sig/task.rbs +66 -0
- data/sig/task_spec.rbs +52 -0
- data/sig/wolflow.rbs +18 -0
- data/sig/workflow.rbs +33 -0
- data/sig/workflow_spec.rbs +20 -0
- metadata +115 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wolflow
|
4
|
+
class Workflow
|
5
|
+
attr_reader :id, :workflow_spec, :root, :data
|
6
|
+
|
7
|
+
def initialize(
|
8
|
+
workflow_spec:,
|
9
|
+
id: object_id,
|
10
|
+
root: Task.new(task_spec: workflow_spec.start, workflow: self),
|
11
|
+
data: {}
|
12
|
+
)
|
13
|
+
@id = id
|
14
|
+
@workflow_spec = workflow_spec
|
15
|
+
@data = data
|
16
|
+
|
17
|
+
raise Error, "#{root} has no task spec" unless root.task_spec
|
18
|
+
|
19
|
+
if root.task_spec && @workflow_spec != root.task_spec.workflow_spec
|
20
|
+
raise Error, "#{root} is from a different workflow spec"
|
21
|
+
end
|
22
|
+
|
23
|
+
@root = root
|
24
|
+
end
|
25
|
+
|
26
|
+
def each(&)
|
27
|
+
@root.each(&)
|
28
|
+
end
|
29
|
+
|
30
|
+
def complete_one
|
31
|
+
each do |task|
|
32
|
+
next if task.completed?
|
33
|
+
|
34
|
+
task.complete!
|
35
|
+
|
36
|
+
return task if task.completed?
|
37
|
+
end
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def complete_all
|
42
|
+
while complete_one do; end
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_hash
|
46
|
+
{
|
47
|
+
id: @id,
|
48
|
+
workflow_spec: @workflow_spec.to_hash,
|
49
|
+
tasks: each.map(&:to_hash),
|
50
|
+
data: @data.to_hash
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
class << self
|
55
|
+
def from_hash(hash)
|
56
|
+
workflow_spec = WorkflowSpec.from_hash(hash[:workflow_spec])
|
57
|
+
|
58
|
+
# @type var tasks: Hash[String, Task]
|
59
|
+
tasks = {}
|
60
|
+
|
61
|
+
hash[:tasks].each do |task_hash|
|
62
|
+
task_spec_id = task_hash.delete(:task_spec_id)
|
63
|
+
task_hash[:task_spec] = workflow_spec.each.find do |task_spec|
|
64
|
+
task_spec.id == task_spec_id
|
65
|
+
end || raise(Error, "no task spec found for `#{task_spec_id}`)")
|
66
|
+
task = Task.from_hash(task_hash)
|
67
|
+
tasks[task.id] = task
|
68
|
+
end
|
69
|
+
|
70
|
+
tasks.each_value do |task|
|
71
|
+
task.connects_with(tasks)
|
72
|
+
end
|
73
|
+
|
74
|
+
start_task = tasks.each_value.find do |task|
|
75
|
+
task.parents.empty?
|
76
|
+
end or raise WorkflowError, "no start task found"
|
77
|
+
|
78
|
+
new(id: hash[:id], root: start_task, workflow_spec: workflow_spec, data: hash[:data])
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wolflow
|
4
|
+
class WorkflowSpec
|
5
|
+
attr_reader :id, :start
|
6
|
+
|
7
|
+
def initialize(id: object_id.to_s, start: Start.new(workflow_spec: self))
|
8
|
+
@id = id
|
9
|
+
@start = start
|
10
|
+
end
|
11
|
+
|
12
|
+
def connect(*task_specs)
|
13
|
+
if @start
|
14
|
+
@start.connect(*task_specs)
|
15
|
+
else
|
16
|
+
start_task = task_specs.first
|
17
|
+
if start_task.workflow_spec && start_task.workflow_spec != self
|
18
|
+
raise TaskSpecError, "#{start_task} already defined in a workflow spec"
|
19
|
+
end
|
20
|
+
|
21
|
+
start_task.workflow_spec = self
|
22
|
+
@start = start_task
|
23
|
+
end
|
24
|
+
|
25
|
+
if block_given?
|
26
|
+
yield(*task_specs)
|
27
|
+
return self
|
28
|
+
end
|
29
|
+
|
30
|
+
return task_specs.first if task_specs.size <= 1
|
31
|
+
|
32
|
+
task_specs
|
33
|
+
end
|
34
|
+
|
35
|
+
def each(&blk)
|
36
|
+
return unless @start
|
37
|
+
|
38
|
+
return enum_for(__method__) unless blk
|
39
|
+
|
40
|
+
blk.call(@start)
|
41
|
+
|
42
|
+
@start.each_successor([self], &blk)
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_hash
|
46
|
+
{
|
47
|
+
id: @id,
|
48
|
+
task_specs: @start ? @start.to_hash_tree.uniq : []
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
class << self
|
53
|
+
def from_hash(hash)
|
54
|
+
workflow_spec = new(id: hash[:id], start: nil)
|
55
|
+
|
56
|
+
# @type var tasks: Hash[String, TaskSpec]
|
57
|
+
tasks = {}
|
58
|
+
|
59
|
+
hash[:task_specs].each do |task_hash|
|
60
|
+
raise Error, "no :name found" unless task_hash.key?(:name)
|
61
|
+
|
62
|
+
cls = TaskSpec.spec_types[task_hash[:name]]
|
63
|
+
task_hash[:workflow_spec] = workflow_spec
|
64
|
+
task_spec = cls.from_hash(task_hash)
|
65
|
+
tasks[task_spec.id] = task_spec
|
66
|
+
end
|
67
|
+
|
68
|
+
tasks.each_value do |task_spec|
|
69
|
+
task_spec.connects_with(tasks)
|
70
|
+
end
|
71
|
+
|
72
|
+
start_task = tasks.each_value.find do |spec|
|
73
|
+
spec.prev_tasks.empty?
|
74
|
+
end or raise WorkflowSpecError, "no start task found"
|
75
|
+
|
76
|
+
workflow_spec.connect(start_task)
|
77
|
+
|
78
|
+
workflow_spec
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/wolflow.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "wolflow/version"
|
4
|
+
|
5
|
+
module Wolflow
|
6
|
+
end
|
7
|
+
|
8
|
+
require_relative "wolflow/errors"
|
9
|
+
require_relative "wolflow/extensions"
|
10
|
+
require_relative "wolflow/operators/base"
|
11
|
+
require_relative "wolflow/workflow_spec"
|
12
|
+
require_relative "wolflow/task_spec"
|
13
|
+
require_relative "wolflow/simple"
|
14
|
+
require_relative "wolflow/simple_merge"
|
15
|
+
require_relative "wolflow/synchronization"
|
16
|
+
require_relative "wolflow/multi_choice"
|
17
|
+
require_relative "wolflow/exclusive_choice"
|
18
|
+
require_relative "wolflow/structured_synchronized_merge"
|
19
|
+
require_relative "wolflow/multi_merge"
|
20
|
+
require_relative "wolflow/cycle"
|
21
|
+
require_relative "wolflow/structured_loop"
|
22
|
+
require_relative "wolflow/recursion"
|
23
|
+
require_relative "wolflow/start"
|
24
|
+
require_relative "wolflow/task"
|
25
|
+
require_relative "wolflow/workflow"
|
data/sig/cycle.rbs
ADDED
data/sig/dsl.rbs
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Wolflow
|
2
|
+
module DSL
|
3
|
+
def self.load!: () -> void
|
4
|
+
|
5
|
+
module WolflowDSL
|
6
|
+
def spec: (String id) ?{ (WorkflowSpec) -> void } -> (WorkflowSpec & WorkflowSpecDSL)
|
7
|
+
end
|
8
|
+
|
9
|
+
module WorkflowSpecDSL
|
10
|
+
def connect: (*String ids) { (*simple task_specs) -> void } -> self
|
11
|
+
| (*String ids) -> Array[simple]
|
12
|
+
| (*TaskSpec task_specs) { (*TaskSpec task_specs) -> void } -> self
|
13
|
+
| (*TaskSpec task_specs) -> Array[TaskSpec]
|
14
|
+
end
|
15
|
+
|
16
|
+
module SimpleDSL
|
17
|
+
@on_complete_callbacks: Array[^(Task task) -> void]
|
18
|
+
|
19
|
+
def on_perform: () { (Task task) -> void } -> self
|
20
|
+
|
21
|
+
def connect: (*String ids) { (*simple task_specs) -> void } -> self
|
22
|
+
| (*String ids) -> Array[simple]
|
23
|
+
| (*TaskSpec task_specs) { (*TaskSpec task_specs) -> void } -> self
|
24
|
+
| (*TaskSpec task_specs) -> Array[TaskSpec]
|
25
|
+
|
26
|
+
def choose: (*String ids, **untyped) ?{ (choice choice, *simple else_task_specs) -> void } -> Array[simple]# [choice, *simple]
|
27
|
+
| (*TaskSpec else_task_specs, **untyped) ?{ (ExclusiveChoice choice, *TaskSpec else_task_specs) -> void } -> Array[TaskSpec]# [ExclusiveChoice, *TaskSpec]
|
28
|
+
end
|
29
|
+
|
30
|
+
module ChoiceDSL
|
31
|
+
end
|
32
|
+
|
33
|
+
type simple = Simple & SimpleDSL
|
34
|
+
type choice = ExclusiveChoice & ChoiceDSL
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Wolflow
|
2
|
+
class ExclusiveChoice < MultiChoice
|
3
|
+
|
4
|
+
attr_reader else_tasks: Array[TaskSpec]
|
5
|
+
|
6
|
+
def connect_else: (*TaskSpec task_specs) -> (Array[TaskSpec] | TaskSpec)
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def initialize: (?else_tasks: Array[TaskSpec], **untyped) -> void
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Wolflow
|
2
|
+
class MultiChoice < TaskSpec
|
3
|
+
interface _Call
|
4
|
+
def call: (Task task) -> untyped
|
5
|
+
end
|
6
|
+
|
7
|
+
type condition = Operators::Base | (Object & _Call)
|
8
|
+
|
9
|
+
attr_reader condition_next_tasks: Array[[condition, Array[TaskSpec]]] # Array[[condition, *TaskSpec]]
|
10
|
+
|
11
|
+
# TODO: remove :reset_task when rbs allows overriding attr_writer in subclass
|
12
|
+
attr_writer connects_to: Hash[:reset_task | :condition_next_tasks | :else_tasks, untyped]
|
13
|
+
|
14
|
+
def connect: (condition condition, *TaskSpec task_specs) { (*TaskSpec tasks) -> void } -> self
|
15
|
+
| (condition condition, *TaskSpec task_specs) -> Array[TaskSpec]# [ExclusiveChoice, *TaskSpec]
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def initialize: (?condition_next_tasks: Array[TaskSpec], **untyped) -> void
|
20
|
+
|
21
|
+
def connect_cond: (condition condition, Array[TaskSpec]) -> void
|
22
|
+
end
|
23
|
+
end
|
data/sig/multi_merge.rbs
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Wolflow
|
2
|
+
module Operators
|
3
|
+
class Base
|
4
|
+
@type: String
|
5
|
+
|
6
|
+
attr_reader self.op_type: String
|
7
|
+
|
8
|
+
public
|
9
|
+
|
10
|
+
def self.from_hash: (Hash[Symbol, untyped] hash) -> Hash[Symbol, untyped]
|
11
|
+
|
12
|
+
def call: (Task task) -> untyped
|
13
|
+
|
14
|
+
def to_hash: () -> Hash[Symbol, untyped]
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def initialize: (?type: String) -> void
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/sig/operators.rbs
ADDED
data/sig/recursion.rbs
ADDED
data/sig/simple.rbs
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Wolflow
|
2
|
+
class Simple < TaskSpec
|
3
|
+
@next_tasks: Array[TaskSpec]
|
4
|
+
|
5
|
+
attr_writer connects_to: Array[String]
|
6
|
+
|
7
|
+
public
|
8
|
+
|
9
|
+
def choose: (*TaskSpec else_task_specs, **untyped) ?{ (ExclusiveChoice choice, *TaskSpec else_task_specs) -> void } -> Array[TaskSpec]# [ExclusiveChoice, *TaskSpec]
|
10
|
+
|
11
|
+
def connect: (*TaskSpec task_specs) { (*TaskSpec tasks) -> void } -> self
|
12
|
+
| (*TaskSpec task_specs) -> Array[TaskSpec]# [ExclusiveChoice, *TaskSpec]
|
13
|
+
|
14
|
+
def workflow_spec=: (WorkflowSpec workflow_spec) -> void
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def connect_one: (TaskSpec task_spec) -> void
|
19
|
+
|
20
|
+
def initialize: (?next_tasks: Array[TaskSpec], **untyped) -> void
|
21
|
+
end
|
22
|
+
end
|
data/sig/start.rbs
ADDED
data/sig/task.rbs
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
module Wolflow
|
2
|
+
class Task
|
3
|
+
type record = {
|
4
|
+
id: String,
|
5
|
+
parents: Array[String],
|
6
|
+
children: Array[String],
|
7
|
+
completed: bool,
|
8
|
+
task_spec_id: String?
|
9
|
+
}
|
10
|
+
|
11
|
+
def self.from_hash: (untyped hash) -> instance
|
12
|
+
|
13
|
+
attr_reader id: String
|
14
|
+
|
15
|
+
attr_reader task_spec: TaskSpec
|
16
|
+
|
17
|
+
attr_reader workflow: Workflow?
|
18
|
+
|
19
|
+
attr_reader completed: bool
|
20
|
+
|
21
|
+
attr_reader root: Task
|
22
|
+
|
23
|
+
attr_reader parents: Array[Task]
|
24
|
+
|
25
|
+
attr_reader children: Array[Task]
|
26
|
+
|
27
|
+
attr_reader data: Hash[untyped, untyped]
|
28
|
+
|
29
|
+
public
|
30
|
+
|
31
|
+
def build_task_tree: () -> void
|
32
|
+
|
33
|
+
def complete!: () -> void
|
34
|
+
|
35
|
+
def completed?: () -> bool
|
36
|
+
|
37
|
+
def connect: (*Task tasks) { (*Task tasks) -> void } -> self
|
38
|
+
| (*Task tasks) -> Array[Task]
|
39
|
+
|
40
|
+
def copy_task_tree: (Integer i) -> void
|
41
|
+
|
42
|
+
def disconnect: (Task task) -> void
|
43
|
+
|
44
|
+
def each: (?Array[Task] visited) { (Task task) -> void } -> self
|
45
|
+
| (?Array[Task] visited) -> Enumerator[Task, self]
|
46
|
+
|
47
|
+
def each_parent: (?Array[Task] visited) { (Task task) -> void } -> self
|
48
|
+
| (?Array[Task] visited) -> Enumerator[Task, self]
|
49
|
+
|
50
|
+
def mark_as_complete!: () -> void
|
51
|
+
|
52
|
+
def reset!: () -> void
|
53
|
+
|
54
|
+
def to_hash: () -> record
|
55
|
+
|
56
|
+
def root=: (Task rt) -> void
|
57
|
+
|
58
|
+
def connects_with: (Hash[String, Task] tasks) -> void
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def connect_one: (Task task) -> void
|
63
|
+
|
64
|
+
def initialize: (?id: String, ?task_spec: TaskSpec, ?workflow: Workflow, ?completed: bool, ?root: Task, ?parents: Array[Task], ?children: Array[Task], ?data: Hash[untyped, untyped]) -> void
|
65
|
+
end
|
66
|
+
end
|
data/sig/task_spec.rbs
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Wolflow
|
2
|
+
class TaskSpec
|
3
|
+
def self.from_hash: (Hash[Symbol, untyped] hash) -> TaskSpec
|
4
|
+
|
5
|
+
def self.spec_type: () -> String
|
6
|
+
|
7
|
+
def self.spec_types: () -> Hash[String, singleton(TaskSpec)]
|
8
|
+
|
9
|
+
attr_reader id: String
|
10
|
+
attr_reader name: String
|
11
|
+
attr_reader workflow_spec: WorkflowSpec
|
12
|
+
attr_reader prev_tasks: Array[TaskSpec]
|
13
|
+
|
14
|
+
public
|
15
|
+
|
16
|
+
def on_complete: (Task task) -> void
|
17
|
+
|
18
|
+
def next_tasks: () -> Array[TaskSpec]
|
19
|
+
|
20
|
+
def child_of?: (TaskSpec spec) -> bool
|
21
|
+
|
22
|
+
def each_ancestor: (?Array[TaskSpec] visited) { (TaskSpec spec) -> void } -> void
|
23
|
+
| (?Array[TaskSpec] visited) -> ::Enumerator[TaskSpec, void]
|
24
|
+
|
25
|
+
def each_successor: (?Array[TaskSpec] visited) { (TaskSpec spec) -> void } -> void
|
26
|
+
| (?Array[TaskSpec] visited) -> ::Enumerator[TaskSpec, void]
|
27
|
+
|
28
|
+
def each_child: (Task task) { (Task task) -> void } -> void
|
29
|
+
| (Task task) -> ::Enumerator[Task, void]
|
30
|
+
|
31
|
+
def each_parent: (Task task) { (Task task) -> void } -> void
|
32
|
+
| (Task task) -> ::Enumerator[Task, void]
|
33
|
+
|
34
|
+
def to_hash: () -> Hash[Symbol, untyped]
|
35
|
+
|
36
|
+
def to_hash_tree: () -> Array[Hash[Symbol, untyped]]
|
37
|
+
|
38
|
+
def precedes?: (TaskSpec task_spec) -> void
|
39
|
+
|
40
|
+
def workflow_spec=: (untyped workflow_spec) -> untyped
|
41
|
+
|
42
|
+
def connects_with: (Hash[String, TaskSpec] tasks) -> void
|
43
|
+
|
44
|
+
def build_next_tasks: (Task task, ?i: Integer, ?next_tasks: Array[TaskSpec]) -> void
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def initialize: (?id: String, ?name: String, ?workflow_spec: WorkflowSpec, ?prev_tasks: Array[TaskSpec]) -> void
|
49
|
+
|
50
|
+
def predict: (Task task, Array[TaskSpec] next_tasks) -> void
|
51
|
+
end
|
52
|
+
end
|
data/sig/wolflow.rbs
ADDED
data/sig/workflow.rbs
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Wolflow
|
2
|
+
class Workflow
|
3
|
+
type record = {
|
4
|
+
id: String,
|
5
|
+
workflow_spec: WorkflowSpec::record,
|
6
|
+
tasks: Array[Task::record],
|
7
|
+
data: Hash[untyped, untyped]
|
8
|
+
}
|
9
|
+
|
10
|
+
attr_reader id: String
|
11
|
+
|
12
|
+
attr_reader workflow_spec: WorkflowSpec
|
13
|
+
|
14
|
+
attr_reader root: Task
|
15
|
+
|
16
|
+
attr_reader data: Hash[untyped, untyped]
|
17
|
+
|
18
|
+
def self.from_hash: (untyped hash) -> instance
|
19
|
+
|
20
|
+
def complete_all: () -> untyped
|
21
|
+
|
22
|
+
def complete_one: () -> Task?
|
23
|
+
|
24
|
+
def each: () { (Task task) -> void } -> self
|
25
|
+
| () -> Enumerator[Task, self]
|
26
|
+
|
27
|
+
def to_hash: () -> record
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def initialize: (workflow_spec: WorkflowSpec, ?id: String, ?root: Task, ?data: Hash[untyped, untyped]) -> void
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Wolflow
|
2
|
+
class WorkflowSpec
|
3
|
+
type record = { id: String, task_specs: Array[Array[Hash[Symbol, untyped]]] }
|
4
|
+
|
5
|
+
attr_reader id: String
|
6
|
+
|
7
|
+
attr_reader start: Simple?
|
8
|
+
|
9
|
+
def self.from_hash: (untyped hash) -> instance
|
10
|
+
|
11
|
+
def connect: (*TaskSpec task_specs) { (*TaskSpec task_specs) -> void } -> self
|
12
|
+
| (*TaskSpec task_specs) -> Array[TaskSpec]
|
13
|
+
|
14
|
+
def to_hash: () -> record
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def initialize: (?id: String, ?start: Simple?) -> void
|
19
|
+
end
|
20
|
+
end
|