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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74aa92e841b1cec015946ecabf0a7dde756df34adf30820db522223be3452870
4
- data.tar.gz: 438194ab7ef0f1ad3f17a63e6306d426ae8e8a3544f33e78bb9e8292af7c7408
3
+ metadata.gz: f6c778b5c85e44e0bada5fd6b073bca7c5ad859dd7e7670970ded5828253f12f
4
+ data.tar.gz: 016efb08c0817d3075ec0d504ceaf00a79cd69021b0db2a3b00445592a04e6b9
5
5
  SHA512:
6
- metadata.gz: 946cddafc751a2b2c9c385911080871a3f7b43231814cf2b351728cc0ae9eaf1297e30e1ed604d6d150ce4c5add9fc74820660e53a61e9b9528691ae805c15d4
7
- data.tar.gz: f8218e47483f166fb9bbc3f036b86a8bde0aced4c1e34a8c3be1558d94eb54c98c7159b432d6e1c3533df5f92991cd7184cb6403806f8bb0ed8c661ffb42b42e
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; end
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.inspect)
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::Linear # TODO: rename!
4
- # @api private
5
- OutputSemantic = Struct.new(:value)
6
- Id = Struct.new(:value)
7
- Track = Struct.new(:color, :adds)
8
- Extension = Struct.new(:callable) do
9
- def call(*args, &block)
10
- callable.(*args, &block)
11
- end
12
- end
13
-
14
- # Shortcut functions for the DSL.
15
- module_function
16
-
17
- # Output( Left, :failure )
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
- seq = strip_start_and_ends(seq, end_id: _end_id) # don't cut off end unless {:connect_to} is set.
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
- if connect_to
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
- searches = [Search.ById(output, connect_to.value)]
55
+ _end_id =
56
+ if connect_to
57
+ end_id
58
+ else
59
+ nil
60
+ end
63
61
 
64
- row = seq[-1]
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
- seq = seq[0..-2] + [row]
68
- end
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
- # Add the path before End.success - not sure this is bullet-proof.
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
- # Connect the Output() => Track(path_track)
79
- return Track.new(track_color, adds)
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
- # Computes the {:outputs} options for {activity}.
83
- def Subprocess(activity, patch: {})
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
- task: activity,
90
- outputs: Hash[activity.to_h[:outputs].collect { |output| [output.semantic, output] }]
91
- }
92
- end
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
- module Patch
95
- module_function
97
+ module Patch
98
+ module_function
96
99
 
97
- def call(activity, path, customization)
98
- task_id, *path = path
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
- patch =
102
- if task_id
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
- # Replace the patched subprocess.
107
- -> { step Subprocess(patched_segment_activity), replace: task_id, id: task_id }
108
- else
109
- customization # apply the *actual* patch from the Subprocess() call.
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
- patched_activity = Class.new(activity)
113
- patched_activity.class_exec(&patch)
114
- patched_activity
115
- end
116
- end
117
-
118
- def normalize(options, local_keys) # TODO: test me.
119
- locals = options.reject { |key, value| ! local_keys.include?(key) }
120
- foreign = options.reject { |key, value| local_keys.include?(key) }
121
- return foreign, locals
122
- end
123
- end
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
- if options.is_a?(Hash) # FIXME: doesn't account {task: <>} and repeats logic from Normalizer.
62
- output, proxy = (options.find { |k,v| v.is_a?(BlockProxy) } or return args)
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
- return args[0], options.merge(output => Linear.Path(**shared_options, **proxy.options, &block))
68
+
69
+ [output, Linear.Path(**shared_options, **proxy.options, &(proxy.block || block))] # FIXME: the || sucks.
65
70
  end
66
71
 
67
- args
68
- end
72
+ evaluated_options = Hash[evaluated_options]
69
73
 
70
- extend Forwardable
71
- def_delegators Linear, :Output, :End, :Track, :Id, :Subprocess
74
+ return args[0], options.merge(evaluated_options)
75
+ end
72
76
 
73
- def Path(options) # we can't access {block} here, syntactically.
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
@@ -3,7 +3,7 @@ module Trailblazer
3
3
  module Activity
4
4
  module DSL
5
5
  module Linear
6
- VERSION = "0.2.6"
6
+ VERSION = "0.2.7"
7
7
  end
8
8
  end
9
9
  end
@@ -154,6 +154,7 @@ module Trailblazer
154
154
  end
155
155
  end
156
156
 
157
+ include Activity::DSL::Linear::Helper
157
158
  extend Activity::DSL::Linear::Strategy
158
159
 
159
160
  initialize!(Railway::DSL::State.new(DSL.OptionsForState()))
@@ -177,6 +177,7 @@ module Trailblazer
177
177
 
178
178
  end # DSL
179
179
 
180
+ include DSL::Linear::Helper
180
181
  extend DSL::Linear::Strategy
181
182
 
182
183
  initialize!(Path::DSL::State.new(DSL.OptionsForState()))
@@ -157,6 +157,7 @@ module Trailblazer
157
157
  end
158
158
  end
159
159
 
160
+ include DSL::Linear::Helper
160
161
  extend DSL::Linear::Strategy
161
162
 
162
163
  initialize!(Railway::DSL::State.new(DSL.OptionsForState()))
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.6
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-01-29 00:00:00.000000000 Z
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
- rubyforge_project:
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.