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 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.