trailblazer-activity 0.5.2 → 0.6.0

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: 14060aa9266dec50a2538bfddacb622d1fcefeba94b92fade757e039ea8aab4f
4
- data.tar.gz: 4bcd28d5b77dc9023c43edb0d762a4ac228fa36012a275f3830f56db9e71e795
3
+ metadata.gz: 656825c80059310cf4a2d89a2214a20618bbd688c182285e8d3c24c8475b8233
4
+ data.tar.gz: 4fb358255fe07a12fcb94172a05f466ce3ac0cb66d3b9502a3622d2a4392a71f
5
5
  SHA512:
6
- metadata.gz: 95e528103b854970aa5b168ce89e8164ae0bdd64ffa21deec24fbd05aa6ca8700d2aa252f5ee3b7b12d2ffe9e592fbb6f3424a9208aa86be96b167fb7bc0c036
7
- data.tar.gz: '09e520f7372cd6d604dc5444484bc561e29e4c1462fd2298bc6d9d4d6a1571eadb37dd7150988cf94a53fe0b218a94529e0fd38e5e2fb31a57ca3ae017640dbc'
6
+ metadata.gz: 74361dffc29b0108e198a3a2bee9637b6519b2ad56b4fb9172aceffc92bee1bcb5dc5e1417dab33d9ba48340749bd5f4306bd16a2c22a1f5eb66cfea4991f04b
7
+ data.tar.gz: ad2fd6a2d6d4df1a2c8e167fd395ef05899f7389b95f8219239d4533c75632001992ff38eb240b300d1b2260544b85abd276372783f8a4c2480afc2a3e45a1a5
data/.travis.yml CHANGED
@@ -1,14 +1,8 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- # - 2.0
5
- - 2.1
6
- - 2.2
7
- - 2.3.3
8
- - 2.4.3
9
- - 2.5.0
10
- matrix:
11
- include:
12
- - rvm: jruby-head
13
- env: JRUBY_OPTS="--profile.api"
4
+ - 2.5.1
5
+ - 2.4.4
6
+ - 2.3.7
7
+ - 2.2.10
14
8
  before_install: gem install bundler
data/CHANGES.md CHANGED
@@ -1,3 +1,25 @@
1
+ # 0.6.0
2
+
3
+ * The `:task` option in `Circuit::call` is now named `:start_task` for consistency.
4
+ * Removed the `:argumenter` option for `Activity::call`. Instead, an `Activity` passes itself via the `:activity` option.
5
+ * Removed the `:extension` option. Instead, any option from the DSL that `is_a?(DSL::Extension)` will be processed in `add_task!`.
6
+ * Replace argumenters with `TaskWrap::invoke`. This simplifies the whole `call` process, and moves all initialization of args to the top.
7
+ * Added `Introspect::Graph::find`.
8
+ * Removed `Introspect::Enumerator` in favor of the `Graph` API.
9
+
10
+ # 0.5.4
11
+
12
+ * Introducing `Introspect::Enumerator` and removing `Introspect.find`. `Enumerator` contains `Enumerable` and exposes all necessary utility methods.
13
+
14
+ # 0.5.3
15
+
16
+ * In Path(), allow referencing an existing task, instead of creating an end event.
17
+ This avoids having to use two `Output() => ..` and is much cleaner.
18
+
19
+ ```ruby
20
+ Path( end_id: :find_model) do .. end
21
+ ```
22
+
1
23
  # 0.5.2
2
24
 
3
25
  * In `Path()`, we removed the `#path` method in favor of a cleaner `task` DSL method. We now use the default plus_poles `success` and `failure` everywhere for consistency. This means that a `task` has two outputs, and if you referenced `Output(:success)`, that would be only one of them. We're planning to have `pass` back which has one `success` plus_pole, only. This change makes the DSL wiring behavior much more consistent.
@@ -12,10 +12,10 @@ class Trailblazer::Activity < Module
12
12
  self[:circuit] = circuit
13
13
  self[:outputs] = outputs
14
14
 
15
- _, local_options = returned_options
15
+ _, local_options, connections, sequence_options, extension_options = returned_options
16
16
 
17
17
  # {Extension API} call all extensions.
18
- local_options[:extension].collect { |ext| ext.( self, *returned_options, original_dsl_args: [name, task, options, block] ) } if local_options[:extension]
18
+ extension_options.keys.collect { |ext| ext.( self, *returned_options, original_dsl_args: [name, task, options, block] ) }
19
19
  end
20
20
  end
21
21
  end
@@ -15,6 +15,11 @@ module Trailblazer
15
15
  # @api private
16
16
  OutputSemantic = Struct.new(:value)
17
17
  Track = Struct.new(:color)
18
+ Extension = Struct.new(:callable) do
19
+ def call(*args, &block)
20
+ callable.(*args, &block)
21
+ end
22
+ end
18
23
 
19
24
  # Shortcut functions for the DSL. These have no state.
20
25
  module Helper
@@ -5,8 +5,8 @@ module Trailblazer
5
5
  # task Callable, id: "success", before: "another"
6
6
  class DefaultNormalizer
7
7
  # Declarative::Variables
8
- def self.build(plus_poles:, extension:[], **options)
9
- return new(plus_poles: plus_poles, extension: extension), options
8
+ def self.build(plus_poles:, **options)
9
+ return new(plus_poles: plus_poles), options
10
10
  end
11
11
 
12
12
  def initialize(**default_options)
@@ -19,7 +19,7 @@ module Trailblazer
19
19
 
20
20
  local_options, sequence_options = Options.normalize( local_options, Activity::Schema::Dependencies.sequence_keywords )
21
21
 
22
- return task, local_options, {}, sequence_options
22
+ return task, local_options, {}, sequence_options, {}
23
23
  end
24
24
  end
25
25
  end
@@ -30,9 +30,9 @@ module Trailblazer
30
30
  default_outputs: @default_outputs,
31
31
  }
32
32
 
33
- signal, (ctx, ) = @pipeline.( [ctx] )
33
+ signal, (ctx, ) = @pipeline.( [ctx], {} )
34
34
 
35
- return ctx[:options][:task], ctx[:local_options], ctx[:connection_options], ctx[:sequence_options]
35
+ return ctx[:options][:task], ctx[:local_options], ctx[:connection_options], ctx[:sequence_options], ctx[:extension_options]
36
36
  end
37
37
 
38
38
  # needs the basic Normalizer
@@ -42,15 +42,18 @@ module Trailblazer
42
42
  extend Trailblazer::Activity::Path( normalizer_class: DefaultNormalizer, plus_poles: PlusPoles.new.merge( Builder::Path.default_outputs.values ) ) # FIXME: the DefaultNormalizer actually doesn't need Left.
43
43
 
44
44
  def self.split_options( ctx, task:, options:, ** )
45
- keywords = extract_dsl_keywords(options)
45
+ keywords = extract_dsl_keywords(options)
46
+ extensions = extract_extensions(options)
46
47
 
47
48
  # sort through the "original" user DSL options.
49
+ options, extension_options = Options.normalize( options, extensions ) # DISCUSS:
48
50
  options, local_options = Options.normalize( options, keywords ) # DISCUSS:
49
51
  local_options, sequence_options = Options.normalize( local_options, Activity::Schema::Dependencies.sequence_keywords )
50
52
 
51
53
  ctx[:local_options],
52
54
  ctx[:connection_options],
53
- ctx[:sequence_options] = local_options, options, sequence_options
55
+ ctx[:sequence_options],
56
+ ctx[:extension_options] = local_options, options, sequence_options, extension_options
54
57
  end
55
58
 
56
59
  # Filter out connections, e.g. `Output(:fail_fast) => :success` and return only the keywords like `:id` or `:replace`.
@@ -58,17 +61,19 @@ module Trailblazer
58
61
  options.keys - options.keys.find_all { |k| connection_classes.include?( k.class ) }
59
62
  end
60
63
 
64
+ def self.extract_extensions(options, extensions_classes = [Activity::DSL::Extension])
65
+ options.keys.find_all { |k| extensions_classes.include?( k.class ) }
66
+ end
67
+
61
68
  # FIXME; why don't we use the extensions passed into the initializer?
62
- def self.normalize_extension_option( ctx, local_options:, ** )
63
- local_options[:extension] = (local_options[:extension]||[]) + [ Activity::Introspect.method(:add_introspection), Activity::DSL.method(:record) ] # fixme: this sucks
69
+ def self.initialize_extension_option( ctx, options:, ** )
70
+ ctx[:options] = options.merge( Activity::DSL::Extension.new( Activity::DSL.method(:record) ) => true )
64
71
  end
65
72
 
66
- # Normalizes ctx[:options]
73
+ # Normalizes ctx[:options] (the user-options via the DSL) into the internal Hash format.
67
74
  def self.normalize_for_macro( ctx, task:, options:, task_builder:, ** )
68
75
  ctx[:options] =
69
76
  if task.is_a?(::Hash) # macro.
70
- options = options.merge(extension: (options[:extension]||[])+(task[:extension]||[]) ) # FIXME.
71
-
72
77
  task.merge(options) # Note that the user options are merged over the macro options.
73
78
  else # user step
74
79
  { id: task }
@@ -93,9 +98,9 @@ module Trailblazer
93
98
  .merge(local_options)
94
99
  end
95
100
 
101
+ task Activity::TaskBuilder::Binary( method(:initialize_extension_option) ), id: "initialize_extension_option"
96
102
  task Activity::TaskBuilder::Binary( method(:normalize_for_macro) ), id: "normalize_for_macro"
97
103
  task Activity::TaskBuilder::Binary( method(:split_options) ), id: "split_options"
98
- task Activity::TaskBuilder::Binary( method(:normalize_extension_option) ), id: "normalize_extension_option"
99
104
  task Activity::TaskBuilder::Binary( method(:initialize_plus_poles) ), id: "initialize_plus_poles"
100
105
  # task ->((ctx, _), **) { pp ctx; [Activity::Right, [ctx, _]] }
101
106
  end
@@ -24,11 +24,15 @@ module Trailblazer
24
24
  end
25
25
 
26
26
  # @return [Adds] list of Adds instances that can be chained or added to an existing sequence.
27
- def self.InitialAdds(track_color:raise, end_semantic:raise, start_outputs: {success: self.default_outputs[:success]}, track_end: Activity.End(end_semantic), **)
27
+ def self.InitialAdds(**options)
28
+ StartAdds(**options) + EndAdds(**options)
29
+ end
28
30
 
31
+ # TODO: make this nicer.
32
+ def self.StartAdds(track_color:, end_semantic:, start_outputs: {success: self.default_outputs[:success]}, **)
29
33
  builder_options={ track_color: track_color, end_semantic: end_semantic }
30
34
 
31
- start_adds = adds(
35
+ adds(
32
36
  Activity::Start.new(semantic: :default),
33
37
 
34
38
  TaskPolarizations(builder_options),
@@ -39,20 +43,31 @@ module Trailblazer
39
43
  magnetic_to: [],
40
44
  plus_poles: PlusPoles.initial(start_outputs), # FIXME: this is actually redundant with Normalizer
41
45
  )
46
+ end
42
47
 
43
- end_adds = adds(
44
- track_end,
48
+ # TODO: make this nicer.
49
+ def self.EndAdds(track_color:, end_semantic:, track_end: Activity.End(end_semantic), end_id: nil, **)
50
+ # an end can either be a reference to another task,
51
+ # or a "real" end event.
52
+ if end_id
53
+ [[:magnetic_to,
54
+ [ end_id, [track_color] ] ]
55
+ ]
56
+ else
57
+ builder_options={ track_color: track_color, end_semantic: end_semantic }
45
58
 
46
- EndEventPolarizations(builder_options),
59
+ adds(
60
+ track_end,
47
61
 
48
- {}, { group: :end },
62
+ EndEventPolarizations(builder_options), # only sets :magnetic_to.
49
63
 
50
- id: "End.#{track_color}",
51
- plus_poles: {},
52
- magnetic_to: nil,
53
- )
64
+ {}, { group: :end },
54
65
 
55
- start_adds + end_adds
66
+ id: "End.#{track_color}",
67
+ plus_poles: {},
68
+ magnetic_to: nil,
69
+ )
70
+ end
56
71
  end
57
72
 
58
73
  def self.TaskPolarizations(track_color:, **)
@@ -42,23 +42,23 @@ module Trailblazer
42
42
  def insert(strategy, polarizer, task, options, &block)
43
43
  normalizer = options[:normalizer] || @normalizer # DISCUSS: do this at a deeper point?
44
44
 
45
- task, local_options, connection_options, sequence_options = normalizer.(task, options)
45
+ task, local_options, connection_options, sequence_options, extension_options = normalizer.(task, options)
46
46
 
47
47
  polarizations = strategy.send(polarizer, @builder_options) # Railway.StepPolarizations( @builder_options )
48
48
 
49
- insert_element( polarizations, task, local_options, connection_options, sequence_options, &block )
49
+ insert_element( polarizations, task, local_options, connection_options, sequence_options, extension_options, &block )
50
50
  end
51
51
 
52
52
  private
53
53
 
54
54
  # Internal top-level entry point to add task(s) and connections.
55
55
  # High level interface for DSL calls like ::task or ::step.
56
- def insert_element(polarizations, task, local_options, connection_options, sequence_options, &block)
57
- adds, *returned_options = Builder.adds_for(polarizations, task, local_options, connection_options, sequence_options, &block)
56
+ def insert_element(polarizations, task, local_options, connection_options, sequence_options, extension_options, &block)
57
+ adds, *returned_options = Builder.adds_for(polarizations, task, local_options, connection_options, sequence_options, extension_options, &block)
58
58
  end
59
59
 
60
60
  # @return Adds
61
- def self.adds_for(polarizations, task, local_options, connection_options, sequence_options, &block)
61
+ def self.adds_for(polarizations, task, local_options, connection_options, sequence_options, extension_options, &block)
62
62
  # go through all wiring options such as Output()=>:color.
63
63
  polarizations_from_user_options, additional_adds = process_dsl_options(connection_options, local_options, &block)
64
64
 
@@ -66,7 +66,7 @@ module Trailblazer
66
66
 
67
67
  result = adds(task, polarizations, local_options, sequence_options, local_options)
68
68
 
69
- return result + (local_options[:adds] || []) + additional_adds, task, local_options, connection_options, sequence_options
69
+ return result + (local_options[:adds] || []) + additional_adds, task, local_options, connection_options, sequence_options, extension_options
70
70
  end
71
71
 
72
72
  def self.process_dsl_options(options, id:nil, plus_poles:, **, &block)
@@ -34,7 +34,7 @@ module Trailblazer
34
34
  def self.circuit_hash_to_process(circuit_hash)
35
35
  end_events = end_events_for(circuit_hash)
36
36
 
37
- return Circuit.new(circuit_hash, end_events), end_events
37
+ return Circuit.new(circuit_hash, end_events, start_task: circuit_hash.keys.first), end_events
38
38
  end
39
39
 
40
40
  # Find all end events that don't have outgoing connections.
@@ -55,11 +55,11 @@ module Trailblazer
55
55
  Polarization.new( output: output, color: task.color )
56
56
  ]
57
57
  else # ID: existing step
58
- new_edge = "#{id}-#{output.signal}-#{task}"
58
+ new_edge = "#{id}-#{output.signal}-#{task}" # edge from <id> to <target>
59
59
 
60
60
  [
61
61
  Polarization.new( output: output, color: new_edge ),
62
- [[ :magnetic_to, [ task, [new_edge] ] ]],
62
+ [[ :magnetic_to, [ task, [new_edge] ] ]], # mark target (`task`) as magnetic to the new edge.
63
63
  ]
64
64
  end
65
65
  end
@@ -12,7 +12,6 @@ module Trailblazer
12
12
  builder_class: Magnetic::Builder::Path, # we use the Activity-based Normalizer
13
13
  normalizer_class: Magnetic::Normalizer,
14
14
  default_outputs: Magnetic::Builder::Path.default_outputs, # binary outputs
15
- extension: [ Introspect.method(:add_introspection) ],
16
15
 
17
16
  extend: [
18
17
  # DSL.def_dsl(:task, Magnetic::Builder::Path, :PassPolarizations),
@@ -1,27 +1,52 @@
1
1
  module Trailblazer
2
- class Activity < Module # Introspection is not used at run-time except for rendering diagrams, tracing, and the like.
2
+ class Activity < Module
3
+ # Compile-time.
4
+ # Introspection is not used at run-time except for rendering diagrams, tracing, and the like.
3
5
  module Introspect
4
- # {:argumenter} API
5
- def self.arguments_for_call(activity, (options, flow_options), **circuit_options)
6
- circuit_options = circuit_options.merge( introspect: activity.debug )
7
-
8
- return activity, [ options, flow_options ], circuit_options
6
+ def self.Graph(*args)
7
+ Graph.new(*args)
9
8
  end
10
9
 
11
- # {Extension} API
12
- def self.add_introspection(activity, task, local_options, *returned_options)
13
- activity[:debug, task] = { id: local_options[:id] || task }
14
- end
10
+ # @private This API is still under construction.
11
+ class Graph
12
+ def initialize(activity)
13
+ @activity = activity
14
+ @circuit = activity.to_h[:circuit]
15
+ @adds = activity.to_h[:adds].compact # FIXME: why are there nils in Adds?
16
+ end
17
+
18
+ def find(id=nil, &block)
19
+ return find_by_id(id) unless block_given?
20
+ find_with_block(&block)
21
+ end
22
+
23
+ private
24
+
25
+ def find_by_id(id)
26
+ (_, (id, triplett)) = @adds.find { |(op, (_id, triplett))| _id == id }
27
+
28
+ Node(triplett[1], id, triplett[0])
29
+ end
30
+
31
+ def find_with_block(&block)
32
+ adds = @adds.find { |(op, (id, triplett))| yield( Node(triplett[1], id, triplett[0]) ) }
33
+ return nil unless adds
34
+
35
+ (op, (id, triplett)) = adds
15
36
 
37
+ Node(triplett[1], id, triplett[0])
38
+ end
16
39
 
17
- # @api private
18
- def self.find(activity, &block)
19
- circuit = activity.to_h[:circuit]
40
+ def Node(*args)
41
+ Node.new(*args).freeze
42
+ end
20
43
 
21
- circuit.instance_variable_get(:@map).find(&block)
44
+ Node = Struct.new(:task, :id, :magnetic_to)
22
45
  end
23
46
 
24
47
 
48
+ # FIXME: remove this
49
+ # @private This will be removed shortly.
25
50
  def self.collect(activity, options={}, &block)
26
51
  circuit = activity.to_h[:circuit]
27
52
  circuit_hash = circuit.to_h[:map]
@@ -17,7 +17,10 @@ module Trailblazer
17
17
  stack.each do |captured, *returned|
18
18
  task = captured.task
19
19
 
20
- name = (node = captured.introspection[task]) ? node[:id] : task
20
+ graph = Introspect::Graph(captured.activity)
21
+
22
+ name = (node = graph.find { |node| node[:task] == task }) ? node[:id] : task
23
+ name ||= task # FIXME: bullshit
21
24
 
22
25
  if returned.size == 1 # flat
23
26
  tree << [ level, name ]
@@ -18,8 +18,8 @@
18
18
  @options = options.merge(semantic: semantic)
19
19
  end
20
20
 
21
- def call(*args)
22
- return self, *args
21
+ def call(args, circuit_options)
22
+ return self, args, circuit_options
23
23
  end
24
24
 
25
25
  def to_h
@@ -34,8 +34,8 @@
34
34
  end
35
35
 
36
36
  class Start < End
37
- def call(*args)
38
- return Activity::Right, *args
37
+ def call(args, circuit_options)
38
+ return Activity::Right, args, circuit_options
39
39
  end
40
40
  end
41
41
 
@@ -8,7 +8,7 @@ class Trailblazer::Activity < Module
8
8
 
9
9
  # Call the actual task we're wrapping here.
10
10
  # puts "~~~~wrap.call: #{task}"
11
- return_signal, return_args = task.( *original_args ) # we lose :exec_context here.
11
+ return_signal, return_args = task.(*original_args)
12
12
 
13
13
  # DISCUSS: do we want original_args here to be passed on, or the "effective" return_args which are different to original_args now?
14
14
  wrap_ctx = wrap_ctx.merge( return_signal: return_signal, return_args: return_args )
@@ -11,7 +11,7 @@ module Trailblazer
11
11
  # {:extension API}
12
12
  def call(activity, task, local_options, *returned_options)
13
13
  # we could make the default initial_activity injectable via the DSL, the value would sit in returned_options or local_options.
14
- static_wrap = Activity::TaskWrap.wrap_static_for(task, wrap_static: activity[:wrap_static] || {})
14
+ static_wrap = Activity::TaskWrap.wrap_static_for(task, activity: activity)
15
15
 
16
16
  # # macro might want to apply changes to the static task_wrap (e.g. Inject)
17
17
  new_wrap = Activity::Path::Plan.merge( static_wrap, @extension_plan )
@@ -9,7 +9,7 @@ class Trailblazer::Activity < Module
9
9
  #
10
10
  # @api private
11
11
  # @interface Runner
12
- def self.call(task, args, **circuit_options)
12
+ def self.call(task, args, circuit_options)
13
13
  wrap_ctx = { task: task }
14
14
 
15
15
  # this activity is "wrapped around" the actual `task`.
@@ -18,9 +18,11 @@ class Trailblazer::Activity < Module
18
18
  # We save all original args passed into this Runner.call, because we want to return them later after this wrap
19
19
  # is finished.
20
20
  original_args = [ args, circuit_options ]
21
+
21
22
  # call the wrap {Activity} around the task.
22
23
  wrap_end_signal, ( wrap_ctx, _ ) = task_wrap_activity.(
23
- [ wrap_ctx, original_args ] # we omit circuit_options here on purpose, so the wrapping activity uses the default, plain Runner.
24
+ [ wrap_ctx, original_args ], # we omit circuit_options here on purpose, so the wrapping activity uses the default, plain Runner.
25
+ {}
24
26
  )
25
27
 
26
28
  # don't return the wrap's end signal, but the one from call_task.
@@ -37,7 +39,10 @@ class Trailblazer::Activity < Module
37
39
  # unnecessary computations at `call`-time since steps might not even be executed.
38
40
  # TODO: make this faster.
39
41
  def self.merge_static_with_runtime(task, wrap_runtime:, **circuit_options)
40
- wrap_activity = TaskWrap.wrap_static_for(task, circuit_options) # find static wrap for this specific task, or default wrap activity.
42
+ wrap_activity = TaskWrap.wrap_static_for(
43
+ task,
44
+ circuit_options
45
+ ) # find static wrap for this specific task, or default wrap activity.
41
46
 
42
47
  # Apply runtime alterations.
43
48
  # Grab the additional wirings for the particular `task` from `wrap_runtime` (returns default otherwise).
@@ -45,7 +50,12 @@ class Trailblazer::Activity < Module
45
50
  end
46
51
  end # Runner
47
52
 
48
- def self.wrap_static_for(task, wrap_static:, default_activity: TaskWrap.initial_activity, **)
53
+ # Retrieve the static wrap config from {activity}.
54
+ # I do not like this part too much, I'd prefer computing the {:wrap_static} at compile-time for the entire
55
+ # object graph (including nesteds) and simply pass it through to all Runner calls.
56
+ # TODO: simplify that. See above
57
+ def self.wrap_static_for(task, activity:, default_activity: TaskWrap.initial_activity, **)
58
+ wrap_static = activity[:wrap_static] || {}
49
59
  wrap_static[task] || default_activity
50
60
  end
51
61
  end
@@ -2,26 +2,42 @@ class Trailblazer::Activity < Module
2
2
  module TaskWrap
3
3
  # TaskWrap tasks for tracing.
4
4
  module Trace
5
- # def self.capture_args(direction, options, flow_options, wrap_config, original_flow_options)
6
- def self.capture_args((wrap_config, original_args), **circuit_options)
7
- (original_options, original_flow_options), original_circuit_options = original_args
5
+ module_function
8
6
 
9
- original_flow_options[:stack].indent!
7
+ # taskWrap step to capture incoming arguments of a step.
8
+ # def self.capture_args(direction, options, flow_options, wrap_config, original_flow_options)
9
+ def capture_args((wrap_config, original_args), **circuit_options)
10
10
 
11
- original_flow_options[:stack] << Trailblazer::Activity::Trace::Entity.new( wrap_config[:task], :args, nil, {}, original_circuit_options[:introspect] )
11
+ original_args = capture_for(wrap_config[:task], *original_args)
12
12
 
13
- [ Trailblazer::Activity::Right, [wrap_config, original_args], **circuit_options ]
13
+ return Trailblazer::Activity::Right, [wrap_config, original_args], circuit_options
14
14
  end
15
15
 
16
- def self.capture_return((wrap_config, original_args), **circuit_options)
16
+ # taskWrap step to capture outgoing arguments from a step.
17
+ def capture_return((wrap_config, original_args), **circuit_options)
17
18
  (original_options, original_flow_options, _) = original_args[0]
18
19
 
19
- original_flow_options[:stack] << Trailblazer::Activity::Trace::Entity.new( wrap_config[:task], :return, wrap_config[:return_signal], {} )
20
+ original_flow_options[:stack] << Trailblazer::Activity::Trace::Entity.new(
21
+ wrap_config[:task], {}, :return, wrap_config[:return_signal]
22
+ )
20
23
 
21
24
  original_flow_options[:stack].unindent!
22
25
 
23
26
 
24
- [ Trailblazer::Activity::Right, [wrap_config, original_args], **circuit_options ]
27
+ return Trailblazer::Activity::Right, [wrap_config, original_args], circuit_options
28
+ end
29
+
30
+ # It's important to understand that {flow[:stack]} is mutated by design. This is needed so
31
+ # in case of exceptions we still have a "global" trace - unfortunately Ruby doesn't allow
32
+ # us a better way.
33
+ def capture_for(task, (ctx, flow), activity:, **circuit_options)
34
+ flow[:stack].indent!
35
+
36
+ flow[:stack] << Trailblazer::Activity::Trace::Entity.new(
37
+ task, activity, :args, nil, {}
38
+ )
39
+
40
+ return [ctx, flow], circuit_options.merge(activity: activity)
25
41
  end
26
42
  end
27
43
  end
@@ -24,29 +24,17 @@ module Trailblazer
24
24
  end
25
25
 
26
26
  # Compute runtime arguments necessary to execute a taskWrap per task of the activity.
27
- def self.arguments_for_call(activity, (options, flow_options), **circuit_options)
27
+ def self.invoke(activity, args, wrap_runtime: {}, **circuit_options)
28
28
  circuit_options = circuit_options.merge(
29
29
  runner: TaskWrap::Runner,
30
- wrap_runtime: circuit_options[:wrap_runtime] || {},
31
- wrap_static: activity[:wrap_static] || {},
32
- )
33
-
34
- return activity, [ options, flow_options ], circuit_options
35
- end
30
+ wrap_runtime: wrap_runtime,
36
31
 
37
- module NonStatic
38
- def self.arguments_for_call(activity, (options, flow_options), **circuit_options)
39
- circuit_options = circuit_options.merge(
40
- runner: TaskWrap::Runner,
41
- wrap_runtime: circuit_options[:wrap_runtime] || {}, # FIXME:this sucks. (was:) this overwrites wrap_runtime from outside.
42
- wrap_static: ::Hash.new(TaskWrap.initial_activity), # add a default static wrap.
43
- )
32
+ activity: { adds: [], circuit: {} }, # for Runner
33
+ )
44
34
 
45
- return activity, [ options, flow_options ], circuit_options
46
- end
35
+ # signal, (ctx, flow), circuit_options =
36
+ Runner.(activity, args, circuit_options)
47
37
  end
48
-
49
- # better: MyClass < Activity(TaskWrap, ...)
50
- end
38
+ end # TaskWrap
51
39
  end
52
40
  end
@@ -0,0 +1,32 @@
1
+ # DISCUSS: move to trailblazer-activity-test ?
2
+
3
+ # Helpers to quickly create steps and tasks.
4
+ module Trailblazer::Activity::Testing
5
+ # Creates a module with one step method for each name.
6
+ #
7
+ # @example
8
+ # extend T.def_steps(:create, :save)
9
+ def self.def_steps(*names)
10
+ Module.new do
11
+ names.each do |name|
12
+ define_method(name) do | ctx, ** |
13
+ ctx[:seq] << name
14
+ ctx.key?(name) ? ctx[name] : true
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ # Creates a method instance with a task interface.
21
+ #
22
+ # @example
23
+ # task task: T.def_task(:create)
24
+ def self.def_task(name)
25
+ Module.new do
26
+ define_singleton_method(name) do | (ctx, flow_options), ** |
27
+ ctx[:seq] << name
28
+ return Trailblazer::Activity::Right, [ctx, flow_options]
29
+ end
30
+ end.method(name)
31
+ end
32
+ end
@@ -22,13 +22,10 @@ module Trailblazer
22
22
  return activity, [ options, flow_options.merge(tracing_flow_options) ], circuit_options.merge(tracing_circuit_options)
23
23
  end
24
24
 
25
- def self.call(activity, (options, flow_options), *args)
26
- activity, (options, flow_options), circuit_options = Trace.arguments_for_call( activity, [options, flow_options], {} ) # only run once for the entire circuit!
25
+ def self.call(activity, (options, flow_options), circuit_options={})
26
+ activity, (options, flow_options), circuit_options = Trace.arguments_for_call( activity, [options, flow_options], circuit_options ) # only run once for the entire circuit!
27
27
  last_signal, (options, flow_options) =
28
- activity.(
29
- [options, flow_options],
30
- circuit_options.merge({ argumenter: [ Introspect.method(:arguments_for_call), TaskWrap.method(:arguments_for_call) ] })
31
- )
28
+ Activity::TaskWrap.invoke(activity, [options, flow_options], circuit_options)
32
29
 
33
30
  return flow_options[:stack].to_a, last_signal, [options, flow_options]
34
31
  end
@@ -48,7 +45,7 @@ module Trailblazer
48
45
  end
49
46
  end
50
47
 
51
- Entity = Struct.new(:task, :type, :value, :value2, :introspection)
48
+ Entity = Struct.new(:task, :activity, :type, :value, :value2)
52
49
 
53
50
  # Mutable/stateful per design. We want a (global) stack!
54
51
  class Stack
@@ -1,5 +1,5 @@
1
1
  module Trailblazer
2
2
  class Activity < Module
3
- VERSION = "0.5.2"
3
+ VERSION = "0.6.0"
4
4
  end
5
5
  end
@@ -1,10 +1,4 @@
1
- require "trailblazer/activity/version"
2
-
3
1
  module Trailblazer
4
- def self.Activity(implementation=Activity::Path, options={})
5
- Activity.new(implementation, state)
6
- end
7
-
8
2
  class Activity < Module
9
3
  attr_reader :initial_state
10
4
 
@@ -51,6 +45,12 @@ module Trailblazer
51
45
  end
52
46
  end
53
47
 
48
+ # Reader and writer method for DSL objects.
49
+ # The writer {dsl[:key] = "value"} exposes immutable behavior and will replace the old
50
+ # @state with a new, modified copy.
51
+ #
52
+ # Always use the DSL::Accessor accessors to avoid leaking state to other components
53
+ # due to mutable write operations.
54
54
  module Accessor
55
55
  def []=(*args)
56
56
  @state = State::Config.send(:[]=, @state, *args)
@@ -81,12 +81,12 @@ module Trailblazer
81
81
  require "trailblazer/activity/dsl/magnetic/merge"
82
82
  include Magnetic::Merge # Activity#merge!
83
83
 
84
- def call(args, argumenter: [], **circuit_options) # DISCUSS: the argumenter logic might be moved out.
85
- _, args, circuit_options = argumenter.inject( [self, args, circuit_options] ) { |memo, argumenter| argumenter.(*memo) }
86
-
87
- self[:circuit].( args, circuit_options.merge(argumenter: argumenter) )
84
+ # @private Note that {Activity.call} is considered private until the public API is stable.
85
+ def call(args, circuit_options={})
86
+ self[:circuit].( args, circuit_options.merge(activity: self) )
88
87
  end
89
88
  end
89
+
90
90
  end # Activity
91
91
  end
92
92
 
@@ -14,7 +14,7 @@ module Trailblazer
14
14
  #
15
15
  # This is the "pipeline operator"'s implementation.
16
16
  class Circuit
17
- def initialize(map, stop_events, name: nil, start_task: map.keys.first)
17
+ def initialize(map, stop_events, start_task:, name: nil)
18
18
  @map = map
19
19
  @stop_events = stop_events
20
20
  @name = name
@@ -36,8 +36,9 @@ module Trailblazer
36
36
  # @return [last_signal, options, flow_options, *args]
37
37
  #
38
38
  # NOTE: returned circuit_options are discarded when calling the runner.
39
- def call(args, task: @start_task, runner: Run, **circuit_options)
39
+ def call(args, start_task: @start_task, runner: Run, **circuit_options)
40
40
  circuit_options = circuit_options.merge( runner: runner ).freeze # TODO: set the :runner option via arguments_for_call to save the merge?
41
+ task = start_task
41
42
 
42
43
  loop do
43
44
  last_signal, args, _discarded_circuit_options = runner.(
@@ -49,35 +50,22 @@ module Trailblazer
49
50
  # Stop execution of the circuit when we hit a stop event (< End). This could be an task's End or Suspend.
50
51
  return [ last_signal, args ] if @stop_events.include?(task) # DISCUSS: return circuit_options here?
51
52
 
52
- task = next_for(task, last_signal) do |next_task, in_map|
53
- raise IllegalInputError.new("#{task}") unless in_map
54
- raise IllegalOutputSignalError.new("<#{@name}>[#{task}][ #{last_signal.inspect} ]") unless next_task
55
- end
53
+ task = next_for(task, last_signal) or raise IllegalSignalError.new("<#{@name}>[#{task}][ #{last_signal.inspect} ]")
56
54
  end
57
55
  end
58
56
 
59
57
  # Returns the circuit's components.
60
58
  def to_h
61
- { map: @map, end_events: @stop_events }
59
+ { map: @map, end_events: @stop_events, start_task: @start_task }
62
60
  end
63
61
 
64
62
  private
65
- def next_for(last_task, emitted_signal)
66
- # p @map
67
- in_map = false
68
- cfg = @map.keys.find { |t| t == last_task } and in_map = true
69
- cfg = @map[cfg] if cfg
70
- cfg ||= {}
71
- next_task = cfg[emitted_signal]
72
- yield next_task, in_map
73
-
74
- next_task
75
- end
76
-
77
- class IllegalInputError < RuntimeError
63
+ def next_for(last_task, signal)
64
+ outputs = @map[last_task]
65
+ outputs[signal]
78
66
  end
79
67
 
80
- class IllegalOutputSignalError < RuntimeError
68
+ class IllegalSignalError < RuntimeError
81
69
  end
82
70
  end
83
71
  end
@@ -1 +1,2 @@
1
+ require "trailblazer/activity/version"
1
2
  require "trailblazer/activity"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trailblazer-activity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-03-23 00:00:00.000000000 Z
11
+ date: 2018-05-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hirb
@@ -111,7 +111,6 @@ files:
111
111
  - NOTES
112
112
  - README.md
113
113
  - Rakefile
114
- - inspeccccccccct.rb
115
114
  - lib/trailblazer-activity.rb
116
115
  - lib/trailblazer/activity.rb
117
116
  - lib/trailblazer/activity/config.rb
@@ -150,6 +149,7 @@ files:
150
149
  - lib/trailblazer/activity/task_wrap/merge.rb
151
150
  - lib/trailblazer/activity/task_wrap/runner.rb
152
151
  - lib/trailblazer/activity/task_wrap/trace.rb
152
+ - lib/trailblazer/activity/testing.rb
153
153
  - lib/trailblazer/activity/trace.rb
154
154
  - lib/trailblazer/activity/version.rb
155
155
  - lib/trailblazer/circuit.rb
data/inspeccccccccct.rb DELETED
@@ -1,19 +0,0 @@
1
-
2
- module Inspect
3
- def inspect
4
- "nice view!"
5
- end
6
- end
7
-
8
- module A
9
- extend Inspect
10
-
11
- def self.a
12
- end
13
- end
14
-
15
- # jruby 9.1.7
16
- puts A.method(:a).inspect #=> #<Method: A.a>
17
- # 2.5
18
- puts A.method(:a).inspect #=> #<Method: nice view!.a>
19
-