trailblazer-activity-dsl-linear 0.2.6 → 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|