trailblazer-activity-dsl-linear 0.2.6 → 0.2.7
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 +4 -4
- data/CHANGES.md +8 -0
- data/lib/trailblazer/activity/dsl/linear.rb +13 -3
- data/lib/trailblazer/activity/dsl/linear/helper.rb +124 -105
- data/lib/trailblazer/activity/dsl/linear/normalizer.rb +9 -1
- data/lib/trailblazer/activity/dsl/linear/strategy.rb +16 -12
- data/lib/trailblazer/activity/dsl/linear/version.rb +1 -1
- data/lib/trailblazer/activity/fast_track.rb +1 -0
- data/lib/trailblazer/activity/path.rb +1 -0
- data/lib/trailblazer/activity/railway.rb +1 -0
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6c778b5c85e44e0bada5fd6b073bca7c5ad859dd7e7670970ded5828253f12f
|
4
|
+
data.tar.gz: 016efb08c0817d3075ec0d504ceaf00a79cd69021b0db2a3b00445592a04e6b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a4781c4a6edb5d16078cd48be9379d862be9d13c6f62ba849b86fe872c33411541456bba91ed64ae96125b9526053976a77910ddcaae68e92227391378a7d83
|
7
|
+
data.tar.gz: 9bcd652ff1be61710625ab18fb20af9e765f49be8f02e940b6b3ebab2a43e679fb0299a9faa354501229c6f411fd76a3698db4ea29404ae15baf9b5693eb338e
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
# 0.2.7
|
2
|
+
|
3
|
+
* `Did you mean ?` suggestions on Linear::Sequence::IndexError.
|
4
|
+
* Introduce `Linear::Helper` module for strategy extensions in third-party gems.
|
5
|
+
* Convenient way to patch Subprocess itself using `patch` option.
|
6
|
+
* Allow multiple `Path()` macro per step.
|
7
|
+
* Small fix for defining instance methods as steps using circuit interface.
|
8
|
+
|
1
9
|
# 0.2.6
|
2
10
|
|
3
11
|
* Added `@fields` to `Linear::State` to save arbitrary data on the activity/strategy level.
|
@@ -37,7 +37,17 @@ class Trailblazer::Activity
|
|
37
37
|
sequence
|
38
38
|
end
|
39
39
|
|
40
|
-
class IndexError < IndexError
|
40
|
+
class IndexError < IndexError
|
41
|
+
attr_reader :step_id
|
42
|
+
|
43
|
+
def initialize(sequence, step_id)
|
44
|
+
@step_id = step_id
|
45
|
+
valid_ids = sequence.collect{ |row| row[3][:id].inspect }
|
46
|
+
|
47
|
+
message = %{#{@step_id.inspect} is not a valid step ID. Did you mean any of these ?\n#{valid_ids.join("\n")}}
|
48
|
+
super(message)
|
49
|
+
end
|
50
|
+
end
|
41
51
|
end
|
42
52
|
|
43
53
|
# Sequence
|
@@ -111,7 +121,7 @@ class Trailblazer::Activity
|
|
111
121
|
end
|
112
122
|
|
113
123
|
def find(sequence, insert_id)
|
114
|
-
index = find_index(sequence, insert_id) or raise Sequence::IndexError.new(insert_id
|
124
|
+
index = find_index(sequence, insert_id) or raise Sequence::IndexError.new(sequence, insert_id)
|
115
125
|
|
116
126
|
return index, sequence.clone # Ruby doesn't have an easy way to avoid mutating arrays :(
|
117
127
|
end
|
@@ -157,10 +167,10 @@ end
|
|
157
167
|
|
158
168
|
require "trailblazer/activity/dsl/linear/normalizer"
|
159
169
|
require "trailblazer/activity/dsl/linear/state"
|
170
|
+
require "trailblazer/activity/dsl/linear/helper"
|
160
171
|
require "trailblazer/activity/dsl/linear/strategy"
|
161
172
|
require "trailblazer/activity/dsl/linear/compiler"
|
162
173
|
require "trailblazer/activity/path"
|
163
174
|
require "trailblazer/activity/railway"
|
164
175
|
require "trailblazer/activity/fast_track"
|
165
|
-
require "trailblazer/activity/dsl/linear/helper" # FIXME
|
166
176
|
require "trailblazer/activity/dsl/linear/variable_mapping"
|
@@ -1,125 +1,144 @@
|
|
1
1
|
module Trailblazer
|
2
2
|
class Activity
|
3
|
-
module DSL
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
# Output( :failure ) #=> Output::Semantic
|
19
|
-
def Output(signal, semantic=nil)
|
20
|
-
return OutputSemantic.new(signal) if semantic.nil?
|
21
|
-
|
22
|
-
Activity.Output(signal, semantic)
|
23
|
-
end
|
24
|
-
|
25
|
-
def End(semantic)
|
26
|
-
Activity.End(semantic)
|
27
|
-
end
|
28
|
-
|
29
|
-
def end_id(_end)
|
30
|
-
"End.#{_end.to_h[:semantic]}" # TODO: use everywhere
|
31
|
-
end
|
32
|
-
|
33
|
-
def Track(color)
|
34
|
-
Track.new(color, []).freeze
|
35
|
-
end
|
36
|
-
|
37
|
-
def Id(id)
|
38
|
-
Id.new(id).freeze
|
39
|
-
end
|
40
|
-
|
41
|
-
def Path(track_color: "track_#{rand}", end_id:"path_end_#{rand}", connect_to:nil, **options, &block)
|
42
|
-
# DISCUSS: here, we use the global normalizer and don't allow injection.
|
43
|
-
state = Activity::Path::DSL::State.new(Activity::Path::DSL.OptionsForState(track_name: track_color, end_id: end_id, **options)) # TODO: test injecting {:normalizers}.
|
44
|
-
|
45
|
-
# seq = block.call(state) # state changes.
|
46
|
-
state.instance_exec(&block)
|
47
|
-
|
48
|
-
seq = state.to_h[:sequence]
|
49
|
-
|
50
|
-
_end_id =
|
51
|
-
if connect_to
|
52
|
-
end_id
|
53
|
-
else
|
54
|
-
nil
|
3
|
+
module DSL
|
4
|
+
module Linear
|
5
|
+
module Helper
|
6
|
+
# @api private
|
7
|
+
OutputSemantic = Struct.new(:value)
|
8
|
+
Id = Struct.new(:value)
|
9
|
+
Track = Struct.new(:color, :adds)
|
10
|
+
Extension = Struct.new(:callable) do
|
11
|
+
def call(*args, &block)
|
12
|
+
callable.(*args, &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.included(base)
|
17
|
+
base.extend ClassMethods
|
55
18
|
end
|
56
19
|
|
57
|
-
|
20
|
+
# Shortcut functions for the DSL.
|
21
|
+
module ClassMethods
|
22
|
+
# Output( Left, :failure )
|
23
|
+
# Output( :failure ) #=> Output::Semantic
|
24
|
+
def Output(signal, semantic=nil)
|
25
|
+
return OutputSemantic.new(signal) if semantic.nil?
|
26
|
+
|
27
|
+
Activity.Output(signal, semantic)
|
28
|
+
end
|
29
|
+
|
30
|
+
def End(semantic)
|
31
|
+
Activity.End(semantic)
|
32
|
+
end
|
33
|
+
|
34
|
+
def end_id(_end)
|
35
|
+
"End.#{_end.to_h[:semantic]}" # TODO: use everywhere
|
36
|
+
end
|
37
|
+
|
38
|
+
def Track(color)
|
39
|
+
Track.new(color, []).freeze
|
40
|
+
end
|
41
|
+
|
42
|
+
def Id(id)
|
43
|
+
Id.new(id).freeze
|
44
|
+
end
|
45
|
+
|
46
|
+
def Path(track_color: "track_#{rand}", end_id:"path_end_#{rand}", connect_to:nil, **options, &block)
|
47
|
+
# DISCUSS: here, we use the global normalizer and don't allow injection.
|
48
|
+
state = Activity::Path::DSL::State.new(Activity::Path::DSL.OptionsForState(track_name: track_color, end_id: end_id, **options)) # TODO: test injecting {:normalizers}.
|
49
|
+
|
50
|
+
# seq = block.call(state) # state changes.
|
51
|
+
state.instance_exec(&block)
|
58
52
|
|
59
|
-
|
60
|
-
output, _ = seq[-1][2][0].(seq, seq[-1]) # FIXME: the Forward() proc contains the row's Output, and the only current way to retrieve it is calling the search strategy. It should be Forward#to_h
|
53
|
+
seq = state.to_h[:sequence]
|
61
54
|
|
62
|
-
|
55
|
+
_end_id =
|
56
|
+
if connect_to
|
57
|
+
end_id
|
58
|
+
else
|
59
|
+
nil
|
60
|
+
end
|
63
61
|
|
64
|
-
|
65
|
-
row = row[0..1] + [searches] + [row[3]] # FIXME: not mutating an array is so hard: we only want to replace the "searches" element, index 2
|
62
|
+
seq = strip_start_and_ends(seq, end_id: _end_id) # don't cut off end unless {:connect_to} is set.
|
66
63
|
|
67
|
-
|
68
|
-
|
64
|
+
if connect_to
|
65
|
+
output, _ = seq[-1][2][0].(seq, seq[-1]) # FIXME: the Forward() proc contains the row's Output, and the only current way to retrieve it is calling the search strategy. It should be Forward#to_h
|
69
66
|
|
70
|
-
|
71
|
-
adds = seq.collect do |row|
|
72
|
-
{
|
73
|
-
row: row,
|
74
|
-
insert: [Insert.method(:Prepend), "End.success"]
|
75
|
-
}
|
76
|
-
end
|
67
|
+
searches = [Search.ById(output, connect_to.value)]
|
77
68
|
|
78
|
-
|
79
|
-
|
80
|
-
end
|
69
|
+
row = seq[-1]
|
70
|
+
row = row[0..1] + [searches] + [row[3]] # FIXME: not mutating an array is so hard: we only want to replace the "searches" element, index 2
|
81
71
|
|
82
|
-
|
83
|
-
|
84
|
-
patch.each do |path, patch|
|
85
|
-
activity = Patch.(activity, path, patch) # TODO: test if multiple patches works!
|
86
|
-
end
|
72
|
+
seq = seq[0..-2] + [row]
|
73
|
+
end
|
87
74
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
75
|
+
# Add the path before End.success - not sure this is bullet-proof.
|
76
|
+
adds = seq.collect do |row|
|
77
|
+
{
|
78
|
+
row: row,
|
79
|
+
insert: [Insert.method(:Prepend), "End.success"]
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
# Connect the Output() => Track(path_track)
|
84
|
+
return Track.new(track_color, adds)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Computes the {:outputs} options for {activity}.
|
88
|
+
def Subprocess(activity, patch: {})
|
89
|
+
activity = Patch.customize(activity, options: patch)
|
90
|
+
|
91
|
+
{
|
92
|
+
task: activity,
|
93
|
+
outputs: Hash[activity.to_h[:outputs].collect { |output| [output.semantic, output] }]
|
94
|
+
}
|
95
|
+
end
|
93
96
|
|
94
|
-
|
95
|
-
|
97
|
+
module Patch
|
98
|
+
module_function
|
96
99
|
|
97
|
-
|
98
|
-
|
100
|
+
def customize(activity, options:)
|
101
|
+
options = options.is_a?(Proc) ?
|
102
|
+
{ [] => options } : # hash-wrapping with empty path, for patching given activity itself
|
103
|
+
options
|
99
104
|
|
105
|
+
options.each do |path, patch|
|
106
|
+
activity = call(activity, path, patch) # TODO: test if multiple patches works!
|
107
|
+
end
|
100
108
|
|
101
|
-
|
102
|
-
|
103
|
-
segment_activity = Introspect::Graph(activity).find(task_id).task
|
104
|
-
patched_segment_activity = call(segment_activity, path, customization)
|
109
|
+
activity
|
110
|
+
end
|
105
111
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
112
|
+
def call(activity, path, customization)
|
113
|
+
task_id, *path = path
|
114
|
+
|
115
|
+
patch =
|
116
|
+
if task_id
|
117
|
+
segment_activity = Introspect::Graph(activity).find(task_id).task
|
118
|
+
patched_segment_activity = call(segment_activity, path, customization)
|
119
|
+
|
120
|
+
# Replace the patched subprocess.
|
121
|
+
-> { step Subprocess(patched_segment_activity), replace: task_id, id: task_id }
|
122
|
+
else
|
123
|
+
customization # apply the *actual* patch from the Subprocess() call.
|
124
|
+
end
|
125
|
+
|
126
|
+
patched_activity = Class.new(activity)
|
127
|
+
patched_activity.class_exec(&patch)
|
128
|
+
patched_activity
|
129
|
+
end
|
110
130
|
end
|
111
131
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
132
|
+
def normalize(options, local_keys) # TODO: test me.
|
133
|
+
locals = options.reject { |key, value| ! local_keys.include?(key) }
|
134
|
+
foreign = options.reject { |key, value| local_keys.include?(key) }
|
135
|
+
return foreign, locals
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end # Helper
|
139
|
+
|
140
|
+
include Helper # Introduce Helper constants in DSL::Linear scope
|
141
|
+
end # Linear
|
142
|
+
end # DSL
|
143
|
+
end # Activity
|
125
144
|
end
|
@@ -53,7 +53,13 @@ module Trailblazer
|
|
53
53
|
# Specific to the "step DSL": if the first argument is a callable, wrap it in a {step_interface_builder}
|
54
54
|
# since its interface expects the step interface, but the circuit will call it with circuit interface.
|
55
55
|
def normalize_step_interface((ctx, flow_options), *)
|
56
|
-
options = ctx[:options] # either a <#task> or {} from macro
|
56
|
+
options = case (step_args = ctx[:options]) # either a <#task> or {} from macro
|
57
|
+
when Hash
|
58
|
+
# extract task for interfaces like `step task: :instance_method_name`
|
59
|
+
step_args[:task].is_a?(Symbol) ? step_args[:task] : step_args
|
60
|
+
else
|
61
|
+
step_args
|
62
|
+
end
|
57
63
|
|
58
64
|
unless options.is_a?(::Hash)
|
59
65
|
# task = wrap_with_step_interface(task: options, step_interface_builder: ctx[:user_options][:step_interface_builder]) # TODO: make this optional with appropriate wiring.
|
@@ -157,6 +163,8 @@ module Trailblazer
|
|
157
163
|
_adds = [add_end(cfg, magnetic_to: end_id, id: end_id)] unless end_exists
|
158
164
|
|
159
165
|
[output_to_id(ctx, output, end_id), _adds]
|
166
|
+
else
|
167
|
+
raise cfg.inspect
|
160
168
|
end
|
161
169
|
|
162
170
|
connections = connections.merge(new_connections)
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require "forwardable"
|
2
|
-
|
3
1
|
module Trailblazer
|
4
2
|
class Activity
|
5
3
|
module DSL
|
@@ -58,23 +56,29 @@ module Trailblazer
|
|
58
56
|
|
59
57
|
private def forward_block(args, block)
|
60
58
|
options = args[1]
|
61
|
-
|
62
|
-
|
59
|
+
|
60
|
+
return args unless options.is_a?(Hash)
|
61
|
+
|
62
|
+
# FIXME: doesn't account {task: <>} and repeats logic from Normalizer.
|
63
|
+
|
64
|
+
# DISCUSS: THIS SHOULD BE DONE IN DSL.Path() which is stateful! the block forwarding should be the only thing happening here!
|
65
|
+
evaluated_options =
|
66
|
+
options.find_all { |k,v| v.is_a?(BlockProxy) }.collect do |output, proxy|
|
63
67
|
shared_options = {step_interface_builder: @state.instance_variable_get(:@normalizer_options)[:step_interface_builder]} # FIXME: how do we know what to pass on and what not?
|
64
|
-
|
68
|
+
|
69
|
+
[output, Linear.Path(**shared_options, **proxy.options, &(proxy.block || block))] # FIXME: the || sucks.
|
65
70
|
end
|
66
71
|
|
67
|
-
|
68
|
-
end
|
72
|
+
evaluated_options = Hash[evaluated_options]
|
69
73
|
|
70
|
-
|
71
|
-
|
74
|
+
return args[0], options.merge(evaluated_options)
|
75
|
+
end
|
72
76
|
|
73
|
-
def Path(options) # we can't access {
|
74
|
-
BlockProxy.new(options)
|
77
|
+
def Path(options, &block) # syntactically, we can't access the {do ... end} block here.
|
78
|
+
BlockProxy.new(options, block)
|
75
79
|
end
|
76
80
|
|
77
|
-
BlockProxy = Struct.new(:options)
|
81
|
+
BlockProxy = Struct.new(:options, :block)
|
78
82
|
|
79
83
|
private def merge!(activity)
|
80
84
|
old_seq = @state.instance_variable_get(:@sequence) # TODO: fixme
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trailblazer-activity-dsl-linear
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: trailblazer-activity
|
@@ -135,8 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
135
|
- !ruby/object:Gem::Version
|
136
136
|
version: '0'
|
137
137
|
requirements: []
|
138
|
-
|
139
|
-
rubygems_version: 2.7.3
|
138
|
+
rubygems_version: 3.0.8
|
140
139
|
signing_key:
|
141
140
|
specification_version: 4
|
142
141
|
summary: Simple DSL to define Trailblazer activities.
|