wolflow 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|