trailblazer-activity 0.15.1 → 0.16.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +62 -0
- data/Gemfile +3 -2
- data/Rakefile +1 -1
- data/lib/trailblazer/activity/adds.rb +122 -119
- data/lib/trailblazer/activity/circuit/task_adapter.rb +3 -3
- data/lib/trailblazer/activity/circuit.rb +6 -2
- data/lib/trailblazer/activity/deprecate.rb +4 -5
- data/lib/trailblazer/activity/introspect/render.rb +53 -0
- data/lib/trailblazer/activity/introspect.rb +27 -113
- data/lib/trailblazer/activity/schema/compiler.rb +106 -0
- data/lib/trailblazer/activity/schema/implementation.rb +2 -1
- data/lib/trailblazer/activity/schema/intermediate.rb +6 -86
- data/lib/trailblazer/activity/schema.rb +27 -4
- data/lib/trailblazer/activity/structures.rb +9 -7
- data/lib/trailblazer/activity/task_wrap/call_task.rb +4 -1
- data/lib/trailblazer/activity/task_wrap/extension.rb +12 -6
- data/lib/trailblazer/activity/task_wrap/pipeline.rb +1 -1
- data/lib/trailblazer/activity/task_wrap/runner.rb +8 -7
- data/lib/trailblazer/activity/task_wrap.rb +7 -5
- data/lib/trailblazer/activity/testing.rb +47 -126
- data/lib/trailblazer/activity/version.rb +1 -1
- data/lib/trailblazer/activity.rb +14 -15
- data/trailblazer-activity.gemspec +0 -1
- metadata +4 -17
- data/lib/trailblazer/activity/config.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4aface39f365f86ef2fdc9c271821747c47724ff939e3b2d9ff9c914e67fe42
|
4
|
+
data.tar.gz: 0b9ad9c43f7d1194d6d36be9a52b18f64ea2153eb08002c7ae6d90afca21c1a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1327d35677ed3fcc721aaf12dd58c8f54f253303b1eb27a01dcf02446c10e1f63ef3004ca1cda4ec7146839029077072893b149c365d3d88e5eb9c7b5f471d2
|
7
|
+
data.tar.gz: 25caa1eece8642ed5d564a3f6acc958c0498f1aff016cc1b61a6cb54c3761f60f8a52ab23b6e0cbed00c714196b821091bd96edce17f6fcb0ab0d69667fd1154
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,65 @@
|
|
1
|
+
# 0.16.1
|
2
|
+
|
3
|
+
* Allow overriding `Activity.call`.
|
4
|
+
|
5
|
+
# 0.16.0
|
6
|
+
|
7
|
+
* Remove `Activity#[]`. Please use `activity.to_h[:config]`.
|
8
|
+
* Change `Activity#to_h[:nodes]`. This is now a `Schema::Nodes` "hash" that is keyed by task that
|
9
|
+
points to `Nodes::Attributes` data structures (a replacement for `Activity::NodeAttributes`).
|
10
|
+
This decision reduces logic and improves performance: it turned out that most of the time an introspect
|
11
|
+
lookup queries for a task, not ID.
|
12
|
+
* Introduce `Activity::Introspect.Nodes()` as a consistent and fast interface for introspection
|
13
|
+
and remove `Activity::Introspect::TaskMap`.
|
14
|
+
* Remove `Activity::NodeAttributes`.
|
15
|
+
* Move `Introspect::Graph` to `trailblazer-developer`. It's a data structure very specific
|
16
|
+
to rendering, which is not a part of pure runtime behavior. `Activity::Introspect.Graph()` is now deprecated.
|
17
|
+
* `TaskWrap.container_activity_for` now accepts `:id` for setting an ID for the containered activity to
|
18
|
+
anything other than `nil`.
|
19
|
+
* Re-add `:nodes` to the container activity hash as this provides a consistent way for treating all `Activity`s.
|
20
|
+
* Remove `Activity::Config`. This immutable hash interface was used in one place, only, and can easily
|
21
|
+
be replaced with `config.merge()`.
|
22
|
+
* Add `Introspect::Render`. Please consider this private.
|
23
|
+
|
24
|
+
## Intermediate/Implementation
|
25
|
+
|
26
|
+
* Remove `Intermediate.call`, this is now done through `Intermediate::Compiler`.
|
27
|
+
* Introduce `Intermediate::Compiler` which is simplified and is 10% faster.
|
28
|
+
* A terminus ("end event") in `Schema::Intermediate` no longer has outputs but an empty array. The
|
29
|
+
`stop_event: true` option is still required to mark the `TaskRef` as a terminus.
|
30
|
+
* `Schema::Intermediate` now keeps a map `{<terminus ID> => :semantic}` instead of the flat termini ID list and
|
31
|
+
one default start event instead of an array. This looks as follows.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
Schema::Intermediate.new(
|
35
|
+
{
|
36
|
+
# ...
|
37
|
+
Intermediate::TaskRef("End.success", stop_event: true) => [Inter::Out(:success, nil)]
|
38
|
+
},
|
39
|
+
["End.success"],
|
40
|
+
[:a] # start
|
41
|
+
```
|
42
|
+
|
43
|
+
Now becomes
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
Schema::Intermediate.new(
|
47
|
+
{
|
48
|
+
# ...
|
49
|
+
Intermediate::TaskRef("End.success", stop_event: true) => []
|
50
|
+
},
|
51
|
+
{"End.success" => :success},
|
52
|
+
:a # start
|
53
|
+
```
|
54
|
+
* In line with the change in `Intermediate`, the `Implemention` termini `Task`s now don't have outputs anymore.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
implementation = {
|
58
|
+
# ...
|
59
|
+
"End.success" => Schema::Implementation::Task(Activity::End.new(semantic: :success), [], []) # No need for outputs here.
|
60
|
+
}
|
61
|
+
```
|
62
|
+
|
1
63
|
# 0.15.1
|
2
64
|
|
3
65
|
* Introduce `Extension.WrapStatic()` as a consistent interface for creating wrap_static extensions
|
data/Gemfile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
source "https://rubygems.org"
|
2
2
|
gemspec
|
3
3
|
|
4
|
-
# gem "trailblazer-developer",
|
5
|
-
|
4
|
+
# gem "trailblazer-developer", path: "../trailblazer-developer"
|
5
|
+
gem "benchmark-ips"
|
6
|
+
gem "standard"
|
data/Rakefile
CHANGED
@@ -4,7 +4,7 @@ require "rake/testtask"
|
|
4
4
|
Rake::TestTask.new(:test) do |t|
|
5
5
|
t.libs << "test"
|
6
6
|
t.libs << "lib"
|
7
|
-
t.test_files = FileList["test/**/*_test.rb"]
|
7
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
8
8
|
end
|
9
9
|
|
10
10
|
task default: %i[test]
|
@@ -1,154 +1,157 @@
|
|
1
1
|
module Trailblazer
|
2
2
|
class Activity
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
3
|
+
# Developer's docs: https://trailblazer.to/2.1/docs/internals#internals-wiring-api-adds-interface
|
4
|
+
#
|
5
|
+
# The Adds interface are mechanics to alter sequences/pipelines.
|
6
|
+
# "one" ADDS structure: {row: ..., insert: [Insert, "id"]}
|
7
|
+
#
|
8
|
+
# To work with the instructions provided here, the pipeline structure
|
9
|
+
# needs to expose {#to_a}.
|
10
|
+
module Adds
|
11
|
+
module_function
|
12
|
+
|
13
|
+
# @returns Sequence/Pipeline New sequence instance
|
14
|
+
# @private
|
15
|
+
def insert_row(pipeline, row:, insert:)
|
16
|
+
insert_function, *args = insert
|
17
|
+
|
18
|
+
insert_function.(pipeline, row, *args)
|
19
|
+
end
|
14
20
|
|
15
|
-
|
21
|
+
# Inserts one or more {Adds} into {pipeline}.
|
22
|
+
def apply_adds(pipeline, adds)
|
23
|
+
adds.each do |add|
|
24
|
+
pipeline = insert_row(pipeline, **add)
|
16
25
|
end
|
17
26
|
|
18
|
-
|
19
|
-
|
20
|
-
adds.each do |add|
|
21
|
-
pipeline = insert_row(pipeline, **add)
|
22
|
-
end
|
23
|
-
|
24
|
-
pipeline
|
25
|
-
end
|
27
|
+
pipeline
|
28
|
+
end
|
26
29
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
# Adds.apply_adds(pipeline, adds)
|
32
|
-
# end
|
33
|
-
|
34
|
-
module FriendlyInterface
|
35
|
-
# @public
|
36
|
-
# @return Array of ADDS
|
37
|
-
#
|
38
|
-
# Translate a collection of friendly interface to ADDS.
|
39
|
-
# This is a mini-DSL, if you want.
|
40
|
-
def self.adds_for(inserts)
|
41
|
-
inserts.collect do |task, options|
|
42
|
-
build_adds(task, **options)
|
43
|
-
end
|
44
|
-
end
|
30
|
+
# @param inserts Array of friendly interface insertions
|
31
|
+
# def call(pipeline, *inserts)
|
32
|
+
# adds = build_adds_for_friendly_interface(inserts)
|
45
33
|
|
46
|
-
|
47
|
-
|
48
|
-
insert, insert_id =
|
49
|
-
append === false ? [:Prepend, prepend] : [:Append, append]
|
34
|
+
# Adds.apply_adds(pipeline, adds)
|
35
|
+
# end
|
50
36
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
37
|
+
module FriendlyInterface
|
38
|
+
# @public
|
39
|
+
# @return Array of ADDS
|
40
|
+
#
|
41
|
+
# Translate a collection of friendly interface to ADDS.
|
42
|
+
# This is a mini-DSL, if you want.
|
43
|
+
def self.adds_for(inserts)
|
44
|
+
inserts.collect do |task, options|
|
45
|
+
build_adds(task, **options)
|
55
46
|
end
|
56
47
|
end
|
57
48
|
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
49
|
+
# @private
|
50
|
+
def self.build_adds(task, id:, prepend: "task_wrap.call_task", append: false)
|
51
|
+
insert, insert_id =
|
52
|
+
(append === false) ? [:Prepend, prepend] : [:Append, append]
|
53
|
+
|
54
|
+
{
|
55
|
+
insert: [Activity::Adds::Insert.method(insert), insert_id],
|
56
|
+
row: TaskWrap::Pipeline::Row(id, task)
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
65
60
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
61
|
+
# Functions to alter the Sequence/Pipeline by inserting, replacing, or deleting a row.
|
62
|
+
#
|
63
|
+
# they don't mutate the data structure but rebuild it, has to respond to {to_a}
|
64
|
+
#
|
65
|
+
# These methods are invoked via {Adds.apply_adds} and should never be called directly.
|
66
|
+
module Insert
|
67
|
+
module_function
|
70
68
|
|
71
|
-
|
72
|
-
|
69
|
+
# Append {new_row} after {insert_id}.
|
70
|
+
def Append(pipeline, new_row, insert_id = nil)
|
71
|
+
build_from_ary(pipeline, insert_id) do |ary, index|
|
72
|
+
index = ary.size if index.nil? # append to end of pipeline.
|
73
|
+
|
74
|
+
range_before_index(ary, index + 1) + [new_row] + Array(ary[index + 1..-1])
|
73
75
|
end
|
76
|
+
end
|
74
77
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
78
|
+
# Insert {new_row} before {insert_id}.
|
79
|
+
def Prepend(pipeline, new_row, insert_id = nil)
|
80
|
+
build_from_ary(pipeline, insert_id) do |ary, index|
|
81
|
+
index = 0 if index.nil? # Prepend to beginning of pipeline.
|
79
82
|
|
80
|
-
|
81
|
-
end
|
83
|
+
range_before_index(ary, index) + [new_row] + ary[index..-1]
|
82
84
|
end
|
85
|
+
end
|
83
86
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
end
|
87
|
+
def Replace(pipeline, new_row, insert_id)
|
88
|
+
build_from_ary(pipeline, insert_id) do |ary, index|
|
89
|
+
range_before_index(ary, index) + [new_row] + ary[index + 1..-1]
|
88
90
|
end
|
91
|
+
end
|
89
92
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
end
|
93
|
+
def Delete(pipeline, _, insert_id)
|
94
|
+
build_from_ary(pipeline, insert_id) do |ary, index|
|
95
|
+
range_before_index(ary, index) + ary[index + 1..-1]
|
94
96
|
end
|
97
|
+
end
|
95
98
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
99
|
+
# @private
|
100
|
+
def build(sequence, rows)
|
101
|
+
sequence.class.new(rows)
|
102
|
+
end
|
100
103
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
104
|
+
# @private
|
105
|
+
def find_index(ary, insert_id)
|
106
|
+
ary.find_index { |row| row.id == insert_id }
|
107
|
+
end
|
105
108
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
end
|
121
|
-
|
122
|
-
_new_ary = yield(ary, index) # call the block.
|
109
|
+
# Converts the pipeline structure to an array,
|
110
|
+
# automatically finds the index for {insert_id},
|
111
|
+
# and calls the user block with the computed values.
|
112
|
+
#
|
113
|
+
# Single-entry point, could be named {#call}.
|
114
|
+
# @private
|
115
|
+
def apply_on_ary(pipeline, insert_id, raise_index_error: true, &block)
|
116
|
+
ary = pipeline.to_a
|
117
|
+
|
118
|
+
if insert_id.nil?
|
119
|
+
index = nil
|
120
|
+
else
|
121
|
+
index = find_index(ary, insert_id) # DISCUSS: this only makes sense if there are more than {Append} using this.
|
122
|
+
raise IndexError.new(pipeline, insert_id) if index.nil? && raise_index_error
|
123
123
|
end
|
124
124
|
|
125
|
-
|
126
|
-
|
125
|
+
_new_ary = yield(ary, index) # call the block.
|
126
|
+
end
|
127
127
|
|
128
|
-
|
129
|
-
|
130
|
-
end
|
128
|
+
def build_from_ary(pipeline, insert_id, &block)
|
129
|
+
new_ary = apply_on_ary(pipeline, insert_id, &block)
|
131
130
|
|
132
|
-
#
|
133
|
-
|
134
|
-
|
135
|
-
def range_before_index(ary, index)
|
136
|
-
return [] if index == 0
|
137
|
-
ary[0..index-1]
|
138
|
-
end
|
139
|
-
end # Insert
|
131
|
+
# Wrap the sequence/pipeline array into a concrete Sequence/Pipeline.
|
132
|
+
build(pipeline, new_ary)
|
133
|
+
end
|
140
134
|
|
141
|
-
|
142
|
-
|
143
|
-
|
135
|
+
# Always returns a valid, concat-able array for all indices
|
136
|
+
# before the {index}.
|
137
|
+
# @private
|
138
|
+
def range_before_index(ary, index)
|
139
|
+
return [] if index == 0
|
140
|
+
ary[0..index - 1]
|
141
|
+
end
|
142
|
+
end # Insert
|
144
143
|
|
145
|
-
|
146
|
-
|
147
|
-
|
144
|
+
class IndexError < ::IndexError
|
145
|
+
def initialize(sequence, step_id)
|
146
|
+
valid_ids = sequence.to_a.collect { |row| row.id.inspect }
|
148
147
|
|
149
|
-
|
150
|
-
|
148
|
+
message = "\n" \
|
149
|
+
"\e[31m#{step_id.inspect} is not a valid step ID. Did you mean any of these ?\e[0m\n" \
|
150
|
+
"\e[32m#{valid_ids.join("\n")}\e[0m"
|
151
|
+
|
152
|
+
super(message)
|
151
153
|
end
|
152
154
|
end
|
155
|
+
end
|
153
156
|
end
|
154
157
|
end
|
@@ -22,8 +22,8 @@ module Trailblazer
|
|
22
22
|
# and returns the return value of the user's callable. By design, it is *not* circuit-interface compatible.
|
23
23
|
class Step
|
24
24
|
def initialize(step, user_proc, **)
|
25
|
-
@step
|
26
|
-
@user_proc
|
25
|
+
@step = step
|
26
|
+
@user_proc = user_proc
|
27
27
|
end
|
28
28
|
|
29
29
|
# Translate the circuit interface to the step's step-interface. However,
|
@@ -91,7 +91,7 @@ module Trailblazer
|
|
91
91
|
def inspect # TODO: make me private!
|
92
92
|
user_step = @circuit_step.instance_variable_get(:@user_proc) # DISCUSS: to we want Step#to_h?
|
93
93
|
|
94
|
-
%
|
94
|
+
%(#<Trailblazer::Activity::TaskBuilder::Task user_proc=#{Trailblazer::Activity::Introspect.render_task(user_step)}>)
|
95
95
|
end
|
96
96
|
alias_method :to_s, :inspect
|
97
97
|
end
|
@@ -58,7 +58,7 @@ module Trailblazer
|
|
58
58
|
task,
|
59
59
|
signal: last_signal,
|
60
60
|
outputs: @map[task],
|
61
|
-
exec_context: circuit_options[:exec_context]
|
61
|
+
exec_context: circuit_options[:exec_context] # passed at run-time from DSL
|
62
62
|
)
|
63
63
|
end
|
64
64
|
end
|
@@ -66,7 +66,11 @@ module Trailblazer
|
|
66
66
|
|
67
67
|
# Returns the circuit's components.
|
68
68
|
def to_h
|
69
|
-
{
|
69
|
+
{
|
70
|
+
map: @map,
|
71
|
+
end_events: @stop_events,
|
72
|
+
start_task: @start_task
|
73
|
+
}
|
70
74
|
end
|
71
75
|
|
72
76
|
private
|
@@ -5,16 +5,15 @@ module Trailblazer
|
|
5
5
|
|
6
6
|
def warn(caller_location, message)
|
7
7
|
location = caller_location ? location_for(caller_location) : nil
|
8
|
-
warning
|
8
|
+
warning = [location, message].compact.join(" ")
|
9
9
|
|
10
|
-
Kernel.warn %
|
10
|
+
Kernel.warn %([Trailblazer] #{warning}\n)
|
11
11
|
end
|
12
12
|
|
13
13
|
def location_for(caller_location)
|
14
|
-
|
15
|
-
line_no = caller_location.lineno
|
14
|
+
line_no = caller_location.lineno
|
16
15
|
|
17
|
-
%
|
16
|
+
%(#{caller_location.absolute_path}:#{line_no})
|
18
17
|
end
|
19
18
|
end
|
20
19
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Activity
|
3
|
+
module Introspect
|
4
|
+
# @private
|
5
|
+
module Render
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def call(activity, **options)
|
9
|
+
nodes = Introspect.Nodes(activity)
|
10
|
+
circuit_map = activity.to_h[:circuit].to_h[:map]
|
11
|
+
|
12
|
+
content = nodes.collect do |task, node|
|
13
|
+
outgoings = circuit_map[task]
|
14
|
+
|
15
|
+
conns = outgoings.collect do |signal, target|
|
16
|
+
" {#{signal}} => #{inspect_with_matcher(target, **options)}"
|
17
|
+
end
|
18
|
+
|
19
|
+
[
|
20
|
+
inspect_with_matcher(node.task, **options),
|
21
|
+
conns.join("\n")
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
content = content.join("\n")
|
26
|
+
|
27
|
+
"\n#{content}".gsub(/0x\w+/, "0x")
|
28
|
+
end
|
29
|
+
|
30
|
+
# If Ruby had pattern matching, this function wasn't necessary.
|
31
|
+
def inspect_with_matcher(task, inspect_task: method(:inspect_task), inspect_end: method(:inspect_end))
|
32
|
+
return inspect_task.(task) unless task.is_a?(Trailblazer::Activity::End)
|
33
|
+
inspect_end.(task)
|
34
|
+
end
|
35
|
+
|
36
|
+
def inspect_task(task)
|
37
|
+
task.inspect
|
38
|
+
end
|
39
|
+
|
40
|
+
def inspect_end(task)
|
41
|
+
class_name = strip(task.class)
|
42
|
+
options = task.to_h
|
43
|
+
|
44
|
+
"#<#{class_name}/#{options[:semantic].inspect}>"
|
45
|
+
end
|
46
|
+
|
47
|
+
def strip(string)
|
48
|
+
string.to_s.sub("Trailblazer::Activity::", "")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -5,142 +5,49 @@ module Trailblazer
|
|
5
5
|
# It abstracts internals about circuits and provides a convenient API to third-parties
|
6
6
|
# such as tracing, rendering an activity, or finding particular tasks.
|
7
7
|
module Introspect
|
8
|
-
#
|
9
|
-
|
10
|
-
#
|
11
|
-
# It is much simpler and faster than the Graph interface that might get (re)moved.
|
12
|
-
def self.TaskMap(activity)
|
8
|
+
# Public entry point for {Activity} instance introspection.
|
9
|
+
def self.Nodes(activity, task: nil, **options)
|
13
10
|
schema = activity.to_h
|
14
11
|
nodes = schema[:nodes]
|
15
12
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
task = node_attributes[:task],
|
20
|
-
TaskMap::TaskAttributes(id: node_attributes[:id], task: task)
|
21
|
-
]
|
22
|
-
end
|
23
|
-
|
24
|
-
TaskMap[task_map_tuples].freeze
|
13
|
+
return Nodes.find_by_id(nodes, options[:id]) if options.key?(:id)
|
14
|
+
return nodes.fetch(task) if task
|
15
|
+
nodes
|
25
16
|
end
|
26
17
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
def self.
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
def find_by_id(id)
|
35
|
-
tuple = find { |task, attrs| attrs[:id] == id } or return
|
18
|
+
module Nodes
|
19
|
+
# @private
|
20
|
+
# @return Attributes data structure
|
21
|
+
def self.find_by_id(nodes, id)
|
22
|
+
tuple = nodes.find { |task, attrs| attrs.id == id } or return
|
36
23
|
tuple[1]
|
37
24
|
end
|
38
25
|
end
|
39
26
|
|
40
|
-
# TODO: order of step/fail/pass in Node would be cool to have
|
41
|
-
|
42
|
-
# TODO: Remove Graph. This is only useful to render the full
|
43
|
-
# circuit, which is a very specific task that could sit in `developer`,
|
44
|
-
# instead.
|
45
|
-
# Some thoughts here:
|
46
|
-
# * where do we need Schema.outputs? and where task.outputs?
|
47
|
-
#
|
48
|
-
#
|
49
|
-
# @private This API is still under construction.
|
50
|
-
class Graph
|
51
|
-
def initialize(activity)
|
52
|
-
@schema = activity.to_h or raise
|
53
|
-
@circuit = @schema[:circuit]
|
54
|
-
@map = @circuit.to_h[:map]
|
55
|
-
@configs = @schema[:nodes]
|
56
|
-
end
|
57
|
-
|
58
|
-
def find(id = nil, &block)
|
59
|
-
return find_by_id(id) unless block_given?
|
60
|
-
|
61
|
-
find_with_block(&block)
|
62
|
-
end
|
63
|
-
|
64
|
-
# TODO: convert to {#to_a}.
|
65
|
-
def collect(strategy: :circuit)
|
66
|
-
@map.keys.each_with_index.collect { |task, i| yield find_with_block { |node| node.task == task }, i }
|
67
|
-
end
|
68
|
-
|
69
|
-
def stop_events
|
70
|
-
@circuit.to_h[:end_events]
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def find_by_id(id)
|
76
|
-
node = @configs.find { |_node| _node.id == id } or return
|
77
|
-
node_for(node)
|
78
|
-
end
|
79
|
-
|
80
|
-
def find_with_block
|
81
|
-
existing = @configs.find { |node| yield Node(node.task, node.id, node.outputs, node.data) } or return
|
82
|
-
|
83
|
-
node_for(existing)
|
84
|
-
end
|
85
|
-
|
86
|
-
# Build a {Graph::Node} with outputs etc.
|
87
|
-
def node_for(node_attributes)
|
88
|
-
Node(
|
89
|
-
node_attributes.task,
|
90
|
-
node_attributes.id,
|
91
|
-
node_attributes.outputs, # [#<struct Trailblazer::Activity::Output signal=Trailblazer::Activity::Right, semantic=:success>]
|
92
|
-
outgoings_for(node_attributes),
|
93
|
-
node_attributes.data,
|
94
|
-
)
|
95
|
-
end
|
96
|
-
|
97
|
-
def Node(*args)
|
98
|
-
Node.new(*args).freeze
|
99
|
-
end
|
100
|
-
|
101
|
-
Node = Struct.new(:task, :id, :outputs, :outgoings, :data)
|
102
|
-
Outgoing = Struct.new(:output, :task)
|
103
|
-
|
104
|
-
def outgoings_for(node)
|
105
|
-
outputs = node.outputs
|
106
|
-
connections = @map[node.task]
|
107
|
-
|
108
|
-
connections.collect do |signal, target|
|
109
|
-
output = outputs.find { |out| out.signal == signal }
|
110
|
-
Outgoing.new(output, target)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def self.Graph(*args)
|
116
|
-
Graph.new(*args)
|
117
|
-
end
|
118
|
-
|
119
27
|
# @private
|
120
28
|
def self.find_path(activity, segments)
|
121
|
-
raise ArgumentError.new(%
|
29
|
+
raise ArgumentError.new(%([Trailblazer] Please pass #{activity}.to_h[:activity] into #find_path.)) unless activity.is_a?(Trailblazer::Activity)
|
122
30
|
|
123
|
-
|
124
|
-
last_graph, last_activity = nil, TaskWrap.container_activity_for(activity) # needed for empty/root path
|
31
|
+
segments = [nil, *segments]
|
125
32
|
|
126
|
-
|
127
|
-
|
128
|
-
|
33
|
+
attributes = nil
|
34
|
+
last_activity = nil
|
35
|
+
activity = TaskWrap.container_activity_for(activity) # needed for empty/root path
|
129
36
|
|
37
|
+
segments.each do |segment|
|
38
|
+
attributes = Introspect.Nodes(activity, id: segment) or return nil
|
130
39
|
last_activity = activity
|
131
|
-
|
132
|
-
|
133
|
-
activity = task
|
40
|
+
activity = attributes.task
|
134
41
|
end
|
135
42
|
|
136
|
-
return
|
43
|
+
return attributes, last_activity
|
137
44
|
end
|
138
45
|
|
139
46
|
def self.render_task(proc)
|
140
47
|
if proc.is_a?(Method)
|
141
48
|
|
142
49
|
receiver = proc.receiver
|
143
|
-
receiver = receiver.is_a?(Class) ? (receiver.name || "#<Class:0x>") : (receiver.name || "#<Module:0x>") #"#<Class:0x>"
|
50
|
+
receiver = receiver.is_a?(Class) ? (receiver.name || "#<Class:0x>") : (receiver.name || "#<Module:0x>") # "#<Class:0x>"
|
144
51
|
|
145
52
|
return "#<Method: #{receiver}.#{proc.name}>"
|
146
53
|
elsif proc.is_a?(Symbol)
|
@@ -149,6 +56,13 @@ module Trailblazer
|
|
149
56
|
|
150
57
|
proc.inspect
|
151
58
|
end
|
59
|
+
|
60
|
+
# TODO: remove with 0.1.0.
|
61
|
+
def self.Graph(*args)
|
62
|
+
Deprecate.warn caller_locations[0], %(`Trailblazer::Activity::Introspect::Graph` is deprecated. Please use `Trailblazer::Developer::Introspect.Graph`)
|
63
|
+
|
64
|
+
Trailblazer::Developer::Introspect::Graph.new(*args)
|
65
|
+
end
|
152
66
|
end # Introspect
|
153
67
|
end
|
154
68
|
end
|