trailblazer-activity 0.2.1 → 0.3.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +4 -0
  3. data/NOTES_ +36 -0
  4. data/README.md +7 -7
  5. data/Rakefile +1 -1
  6. data/lib/trailblazer/activity.rb +137 -96
  7. data/lib/trailblazer/activity/heritage.rb +30 -0
  8. data/lib/trailblazer/activity/introspection.rb +105 -0
  9. data/lib/trailblazer/activity/magnetic.rb +47 -0
  10. data/lib/trailblazer/activity/magnetic/builder.rb +161 -0
  11. data/lib/trailblazer/activity/magnetic/builder/block.rb +37 -0
  12. data/lib/trailblazer/activity/magnetic/builder/fast_track.rb +141 -0
  13. data/lib/trailblazer/activity/magnetic/builder/path.rb +98 -0
  14. data/lib/trailblazer/activity/magnetic/builder/railway.rb +123 -0
  15. data/lib/trailblazer/activity/magnetic/dsl.rb +90 -0
  16. data/lib/trailblazer/activity/magnetic/dsl/alterations.rb +44 -0
  17. data/lib/trailblazer/activity/magnetic/dsl/plus_poles.rb +59 -0
  18. data/lib/trailblazer/activity/magnetic/finalizer.rb +55 -0
  19. data/lib/trailblazer/activity/magnetic/generate.rb +62 -0
  20. data/lib/trailblazer/activity/present.rb +12 -19
  21. data/lib/trailblazer/activity/process.rb +16 -0
  22. data/lib/trailblazer/activity/schema/dependencies.rb +41 -0
  23. data/lib/trailblazer/activity/schema/sequence.rb +46 -0
  24. data/lib/trailblazer/activity/structures.rb +41 -0
  25. data/lib/trailblazer/activity/subprocess.rb +9 -1
  26. data/lib/trailblazer/activity/trace.rb +25 -16
  27. data/lib/trailblazer/activity/version.rb +1 -1
  28. data/lib/trailblazer/activity/wrap.rb +4 -13
  29. data/lib/trailblazer/circuit.rb +4 -35
  30. data/lib/trailblazer/wrap/call_task.rb +2 -2
  31. data/lib/trailblazer/wrap/runner.rb +7 -1
  32. data/lib/trailblazer/wrap/trace.rb +6 -5
  33. metadata +21 -10
  34. data/lib/trailblazer/activity/graph.rb +0 -157
  35. data/lib/trailblazer/circuit/testing.rb +0 -58
  36. data/lib/trailblazer/container_chain.rb +0 -45
  37. data/lib/trailblazer/context.rb +0 -68
  38. data/lib/trailblazer/option.rb +0 -78
  39. data/lib/trailblazer/wrap/inject.rb +0 -32
  40. data/lib/trailblazer/wrap/variable_mapping.rb +0 -92
@@ -0,0 +1,16 @@
1
+ module Trailblazer
2
+ class Activity::Process
3
+ # The executable run-time instance for an Activity.
4
+ def initialize(circuit_hash, end_events)
5
+ @default_start_event = circuit_hash.keys.first
6
+ @circuit = Circuit.new(circuit_hash, end_events)
7
+ end
8
+
9
+ def call(args, task: @default_start_event, **circuit_options)
10
+ @circuit.(
11
+ args,
12
+ circuit_options.merge( task: task ) , # this passes :runner to the {Circuit}.
13
+ )
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,41 @@
1
+ module Trailblazer
2
+ module Activity::Schema
3
+ # Helps organizing the structure of the circuit and allows to define steps that
4
+ # might be inserted in a completely different order, but it's experimental.
5
+ #
6
+ # Translates linear DSL calls that might refer to the same task several times into a linear "drawing instruction"
7
+ # that can be consumed by Schema.bla.
8
+ #
9
+ # This class is experimental.
10
+ class Dependencies
11
+ def initialize
12
+ @groups = {
13
+ start: Sequence.new,
14
+ main: Sequence.new, # normal steps
15
+ end: Sequence.new, # ends
16
+ unresolved: Sequence.new, # div
17
+ }
18
+
19
+ @order = [ :start, :main, :end, :unresolved ]
20
+ end
21
+
22
+ def add(id, seq_options, group: :main, **sequence_options)
23
+ group = @groups[group] or raise "unknown group #{group}, implement me"
24
+
25
+ group.add(id, seq_options, **sequence_options) # handles
26
+ end
27
+
28
+ def to_a
29
+ @order.collect{ |name| @groups[name].to_a }.flatten(1)
30
+ end
31
+
32
+ # private
33
+ def find(id)
34
+ @groups.find do |name, group|
35
+ index = group.send( :find_index, id )
36
+ return group, index if index
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,46 @@
1
+ module Trailblazer
2
+ module Activity::Schema
3
+ # the list of what tasks to add to the graph, for the "drawer"
4
+ # produces immutable list of: (node, magnetic_to (incoming colors), outgoing colors)
5
+ # via #to_a
6
+
7
+ #mutable, for DSL
8
+ #
9
+ # @api private
10
+ class Sequence < ::Array
11
+ Element = Struct.new(:id, :configuration)
12
+
13
+ # Insert the task into {Sequence} array by respecting options such as `:before`.
14
+ # This mutates the object per design.
15
+ #
16
+ # @param wiring [ [:success, :special_1], A, [ Output, Output ] ]
17
+ def add(id, wiring, before:nil, after:nil, replace:nil, delete:nil)
18
+ element = Element.new(id, wiring).freeze
19
+
20
+ return insert(find_index!(before), element) if before
21
+ return insert(find_index!(after)+1, element) if after
22
+ return self[find_index!(replace)] = element if replace
23
+ return delete_at(find_index!(delete)) if delete
24
+
25
+ self << element
26
+ end
27
+
28
+ def to_a
29
+ collect { |element| element.configuration }
30
+ end
31
+
32
+ private
33
+
34
+ def find_index(id)
35
+ element = find { |el| el.id == id }
36
+ index(element)
37
+ end
38
+
39
+ def find_index!(id)
40
+ find_index(id) or raise IndexError.new(id)
41
+ end
42
+
43
+ class IndexError < IndexError; end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,41 @@
1
+ module Trailblazer
2
+ class Activity
3
+ # End event is just another callable task.
4
+ # Any instance of subclass of End will halt the circuit's execution when hit.
5
+ class End
6
+ def initialize(name, options={})
7
+ @name = name
8
+ @options = options
9
+ end
10
+
11
+ def call(*args)
12
+ [ self, *args ]
13
+ end
14
+ end
15
+
16
+ class Start < End
17
+ def call(*args)
18
+ return Activity::Right, *args
19
+ end
20
+ end
21
+
22
+ # Builder for Activity::End.
23
+ def self.End(name, semantic=name)
24
+ Activity::End.new(name, semantic: semantic)
25
+ end
26
+
27
+ class Signal; end
28
+ class Right < Signal; end
29
+ class Left < Signal; end
30
+
31
+ # signal: actual signal emitted by the task
32
+ # color: the mapping, where this signal will travel to. This can be e.g. Left=>:success. The polarization when building the graph.
33
+ # "i am traveling towards :success because ::step said so!"
34
+ # semantic: the original "semantic" or role of the signal, such as :success. This usually comes from the activity hosting this output.
35
+ Output = Struct.new(:signal, :semantic)
36
+
37
+ def self.Output(signal, color)
38
+ Output.new(signal, color).freeze
39
+ end
40
+ end
41
+ end
@@ -9,6 +9,8 @@ module Trailblazer
9
9
  # Subprocess allows to have tasks with a different call interface and start event.
10
10
  # @param activity any object with an {Activity interface}
11
11
  class Subprocess
12
+ include Interface
13
+
12
14
  def initialize(activity, call: :call, **options)
13
15
  @activity = activity
14
16
  @options = options
@@ -20,7 +22,13 @@ module Trailblazer
20
22
  end
21
23
 
22
24
  # @private
23
- attr_reader :activity # we actually only need this for introspection.
25
+ def decompose
26
+ @activity.decompose # TODO: test explicitly
27
+ end
28
+
29
+ def debug
30
+ @activity.debug
31
+ end
24
32
  end
25
33
  end
26
34
  end
@@ -10,43 +10,52 @@ module Trailblazer
10
10
  module Trace
11
11
  def self.call(activity, (options), *args, &block)
12
12
  tracing_flow_options = {
13
- stack: Trace::Stack.new,
14
- # Note that we don't pass :wrap_static here, that's handled by Task.__call__.
15
- introspection: {}, # usually set that in Activity::call.
13
+ stack: Trace::Stack.new,
16
14
  }
17
15
 
18
16
  tracing_circuit_options = {
19
- runner: Wrap::Runner,
20
- wrap_runtime: ::Hash.new(Trace.wirings), # FIXME: this still overrides existing wrap_runtimes.
21
- wrap_static: ::Hash.new( Trailblazer::Activity::Wrap.initial_activity ) # FIXME
17
+ runner: Wrap::Runner,
18
+ wrap_runtime: ::Hash.new(Trace.wirings), # FIXME: this still overrides existing wrap_runtimes.
19
+ wrap_static: ::Hash.new( Trailblazer::Activity::Wrap.initial_activity ), # FIXME
20
+ introspection: compute_debug(activity), # FIXME: this is still also set in Activity::call
22
21
  }
23
22
 
24
- last_signal, (options, flow_options) = call_circuit( activity, [
25
- options,
23
+ last_signal, (options, flow_options) = call_activity( activity, [ options, tracing_flow_options ], tracing_circuit_options, &block )
26
24
  # tracing_flow_options.merge(flow_options),
27
- tracing_flow_options,
28
- ], tracing_circuit_options, &block )
29
25
 
30
26
  return flow_options[:stack].to_a, last_signal, options, flow_options
31
27
  end
32
28
 
29
+ private
30
+
33
31
  # TODO: test alterations with any wrap_circuit.
34
- def self.call_circuit(activity, *args, &block)
32
+ def self.call_activity(activity, *args, &block)
35
33
  return activity.(*args) unless block
36
34
  block.(activity, *args)
37
35
  end
38
36
 
39
- # Graph insertions for the trace tasks that capture the arguments just before calling the task,
37
+ # TODO: this is experimental.
38
+ # Go through all nested Activities and grab their `Activity.debug` field. This gets all merged into
39
+ # one big debugging hash, instead of computing it overly complex at runtime and while executing the circuit.
40
+ def self.compute_debug(activity)
41
+ arrs = Introspect.collect( activity, recursive: true ) { |task, _| task }.find_all { |task| task.is_a?(Interface) }.collect { |task| task.debug }.flatten(1).compact
42
+
43
+ arrs.inject( activity.debug ) { |memo, debug| memo.merge(debug) }
44
+ end
45
+
46
+ # Insertions for the trace tasks that capture the arguments just before calling the task,
40
47
  # and before the TaskWrap is finished.
41
48
  #
42
49
  # Note that the TaskWrap steps are implemented in Activity::Wrap::Trace.
43
50
  def self.wirings
44
- [
45
- [ :insert_before!, "task_wrap.call_task", node: [ Wrap::Trace.method(:capture_args), id: "task_wrap.capture_args" ], outgoing: [ Circuit::Right, {} ], incoming: ->(*) { true } ],
46
- [ :insert_before!, "End.default", node: [ Wrap::Trace.method(:capture_return), id: "task_wrap.capture_return" ], outgoing: [ Circuit::Right, {} ], incoming: ->(*) { true } ],
47
- ]
51
+ Activity::Magnetic::Builder::Path.plan do
52
+ task Wrap::Trace.method(:capture_args), id: "task_wrap.capture_args", before: "task_wrap.call_task"
53
+ task Wrap::Trace.method(:capture_return), id: "task_wrap.capture_return", before: "End.success", group: :end
54
+ end
48
55
  end
49
56
 
57
+ Entity = Struct.new(:task, :type, :value, :value2, :introspection)
58
+
50
59
  # Mutable/stateful per design. We want a (global) stack!
51
60
  class Stack
52
61
  def initialize
@@ -1,5 +1,5 @@
1
1
  module Trailblazer
2
2
  class Activity
3
- VERSION = "0.2.1"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
@@ -12,20 +12,11 @@ class Trailblazer::Activity
12
12
  # |-- Trace.capture_return [optional]
13
13
  # |-- Wrap::End
14
14
 
15
- # Activity = Trailblazer::Activity::Activity({ id: "task.wrap" }, end: { default: End.new(:default) }) do |act|
16
- # {
17
- # act[:Start] => { Right => Call }, # see Wrap::call_task
18
- # Call => { Right => act[:End] },
19
- # }
20
- # end # Activity
21
-
22
15
  def self.initial_activity
23
- Trailblazer::Activity.from_wirings(
24
- [
25
- [ :attach!, target: [ Trailblazer::Circuit::End.new(:default), type: :event, id: "End.default" ], edge: [ Trailblazer::Circuit::Right, {} ] ],
26
- [ :insert_before!, "End.default", node: [ Wrap.method(:call_task), id: "task_wrap.call_task" ], outgoing: [ Trailblazer::Circuit::Right, {} ], incoming: ->(*) { true } ]
27
- ]
28
- )
16
+ Magnetic::Builder::Path.plan do
17
+ # Wrap.call_task is defined in wrap/call_task.
18
+ task Wrap.method(:call_task), id: "task_wrap.call_task"
19
+ end
29
20
  end
30
21
  end
31
22
  end
@@ -6,17 +6,15 @@ module Trailblazer
6
6
  #
7
7
  # @param map [Hash] Defines the wiring.
8
8
  # @param stop_events [Array] Tasks that stop execution of the circuit.
9
- # @param name [Hash] Names for tracing, debugging and exceptions. `:id` is a reserved key for circuit name.
10
9
  #
11
10
  # result = circuit.(start_at, *args)
12
11
  #
13
12
  # @see Activity
14
13
  # @api semi-private
15
14
  class Circuit
16
- def initialize(map, stop_events, name)
15
+ def initialize(map, stop_events)
17
16
  @map = map
18
17
  @stop_events = stop_events
19
- @name = name
20
18
  end
21
19
 
22
20
  # @param args [Array] all arguments to be passed to the task's `call`
@@ -46,16 +44,15 @@ module Trailblazer
46
44
  return [ last_signal, args ] if @stop_events.include?(task) # DISCUSS: return circuit_options here?
47
45
 
48
46
  task = next_for(task, last_signal) do |next_task, in_map|
49
- task_name = @name[task] || task # TODO: this must be implemented only once, somewhere.
50
- raise IllegalInputError.new("#{@name[:id]} #{task_name}") unless in_map
51
- raise IllegalOutputSignalError.new("from #{@name[:id]}: `#{task_name}`===>[ #{last_signal.inspect} ]") unless next_task
47
+ raise IllegalInputError.new("#{task}") unless in_map
48
+ raise IllegalOutputSignalError.new("from : `#{task}`===>[ #{last_signal.inspect} ]") unless next_task
52
49
  end
53
50
  end
54
51
  end
55
52
 
56
53
  # Returns the circuit's components.
57
54
  def to_fields
58
- [ @map, @stop_events, @name]
55
+ [ @map, @stop_events ]
59
56
  end
60
57
 
61
58
  private
@@ -76,33 +73,5 @@ module Trailblazer
76
73
 
77
74
  class IllegalOutputSignalError < RuntimeError
78
75
  end
79
-
80
- # End event is just another callable task.
81
- # Any instance of subclass of End will halt the circuit's execution when hit.
82
- class End
83
- def initialize(name, options={})
84
- @name = name
85
- @options = options
86
- end
87
-
88
- def call(*args)
89
- [ self, *args ]
90
- end
91
- end
92
-
93
- class Start < End
94
- def call(*args)
95
- [ Right, *args ]
96
- end
97
- end
98
-
99
- # Builder for Circuit::End when defining the Activity's circuit.
100
- def self.End(name, options={})
101
- End.new(name, options)
102
- end
103
-
104
- class Signal; end
105
- class Right < Signal; end
106
- class Left < Signal; end
107
76
  end
108
77
  end
@@ -7,13 +7,13 @@ class Trailblazer::Activity
7
7
  task = wrap_ctx[:task]
8
8
 
9
9
  # Call the actual task we're wrapping here.
10
- puts "~~~~wrap.call: #{task}"
10
+ # puts "~~~~wrap.call: #{task}"
11
11
  result_direction, result_args = task.( *original_args ) # we lose :exec_context here.
12
12
 
13
13
  # DISCUSS: do we want original_args here to be passed on, or the "effective" result_args which are different to original_args now?
14
14
  wrap_ctx = wrap_ctx.merge( result_direction: result_direction, result_args: result_args )
15
15
 
16
- [ Trailblazer::Circuit::Right, [ wrap_ctx, original_args ], **circuit_options ]
16
+ [ Right, [ wrap_ctx, original_args ], **circuit_options ]
17
17
  end
18
18
  end # Wrap
19
19
  end
@@ -41,7 +41,13 @@ class Trailblazer::Activity
41
41
 
42
42
  # Apply runtime alterations.
43
43
  # Grab the additional wirings for the particular `task` from `wrap_runtime` (returns default otherwise).
44
- wrap_activity = Trailblazer::Activity.merge(wrap_activity, wrap_runtime[task])
44
+
45
+ # NOTE: the recompilation is absolutely not necessary at runtime and could be avoided if the runtime wrap is empty.
46
+ # TODO: make this faster.
47
+ adds = Trailblazer::Activity::Magnetic::Builder.merge(wrap_activity, wrap_runtime[task])
48
+ wrap_activity, outputs = Magnetic::Builder::Finalizer.(adds)
49
+
50
+ wrap_activity
45
51
  end
46
52
  end # Runner
47
53
  end
@@ -1,26 +1,27 @@
1
1
  class Trailblazer::Activity
2
2
  module Wrap
3
+ # TaskWrap tasks for tracing.
3
4
  module Trace
4
5
  # def self.capture_args(direction, options, flow_options, wrap_config, original_flow_options)
5
6
  def self.capture_args((wrap_config, original_args), **circuit_options)
6
- (original_options, original_flow_options, _) = original_args[0]
7
+ (original_options, original_flow_options), original_circuit_options = original_args
7
8
 
8
9
  original_flow_options[:stack].indent!
9
10
 
10
- original_flow_options[:stack] << [ wrap_config[:task], :args, nil, {}, original_flow_options[:introspection] ]
11
+ original_flow_options[:stack] << Trailblazer::Activity::Trace::Entity.new( wrap_config[:task], :args, nil, {}, original_circuit_options[:introspection] )
11
12
 
12
- [ Trailblazer::Circuit::Right, [wrap_config, original_args], **circuit_options ]
13
+ [ Trailblazer::Activity::Right, [wrap_config, original_args], **circuit_options ]
13
14
  end
14
15
 
15
16
  def self.capture_return((wrap_config, original_args), **circuit_options)
16
17
  (original_options, original_flow_options, _) = original_args[0]
17
18
 
18
- original_flow_options[:stack] << [ wrap_config[:task], :return, wrap_config[:result_direction], {} ]
19
+ original_flow_options[:stack] << Trailblazer::Activity::Trace::Entity.new( wrap_config[:task], :return, wrap_config[:result_direction], {} )
19
20
 
20
21
  original_flow_options[:stack].unindent!
21
22
 
22
23
 
23
- [ Trailblazer::Circuit::Right, [wrap_config, original_args], **circuit_options ]
24
+ [ Trailblazer::Activity::Right, [wrap_config, original_args], **circuit_options ]
24
25
  end
25
26
  end
26
27
  end
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.2.1
4
+ version: 0.3.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: 2017-10-21 00:00:00.000000000 Z
11
+ date: 2017-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hirb
@@ -93,26 +93,37 @@ files:
93
93
  - CHANGES.md
94
94
  - Gemfile
95
95
  - NOTES
96
+ - NOTES_
96
97
  - README.md
97
98
  - Rakefile
98
99
  - lib/trailblazer-activity.rb
99
100
  - lib/trailblazer/activity.rb
100
- - lib/trailblazer/activity/graph.rb
101
+ - lib/trailblazer/activity/heritage.rb
102
+ - lib/trailblazer/activity/introspection.rb
103
+ - lib/trailblazer/activity/magnetic.rb
104
+ - lib/trailblazer/activity/magnetic/builder.rb
105
+ - lib/trailblazer/activity/magnetic/builder/block.rb
106
+ - lib/trailblazer/activity/magnetic/builder/fast_track.rb
107
+ - lib/trailblazer/activity/magnetic/builder/path.rb
108
+ - lib/trailblazer/activity/magnetic/builder/railway.rb
109
+ - lib/trailblazer/activity/magnetic/dsl.rb
110
+ - lib/trailblazer/activity/magnetic/dsl/alterations.rb
111
+ - lib/trailblazer/activity/magnetic/dsl/plus_poles.rb
112
+ - lib/trailblazer/activity/magnetic/finalizer.rb
113
+ - lib/trailblazer/activity/magnetic/generate.rb
101
114
  - lib/trailblazer/activity/present.rb
115
+ - lib/trailblazer/activity/process.rb
116
+ - lib/trailblazer/activity/schema/dependencies.rb
117
+ - lib/trailblazer/activity/schema/sequence.rb
118
+ - lib/trailblazer/activity/structures.rb
102
119
  - lib/trailblazer/activity/subprocess.rb
103
120
  - lib/trailblazer/activity/trace.rb
104
121
  - lib/trailblazer/activity/version.rb
105
122
  - lib/trailblazer/activity/wrap.rb
106
123
  - lib/trailblazer/circuit.rb
107
- - lib/trailblazer/circuit/testing.rb
108
- - lib/trailblazer/container_chain.rb
109
- - lib/trailblazer/context.rb
110
- - lib/trailblazer/option.rb
111
124
  - lib/trailblazer/wrap/call_task.rb
112
- - lib/trailblazer/wrap/inject.rb
113
125
  - lib/trailblazer/wrap/runner.rb
114
126
  - lib/trailblazer/wrap/trace.rb
115
- - lib/trailblazer/wrap/variable_mapping.rb
116
127
  - trailblazer-activity.gemspec
117
128
  homepage: http://trailblazer.to/gems/workflow
118
129
  licenses:
@@ -134,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
145
  version: '0'
135
146
  requirements: []
136
147
  rubyforge_project:
137
- rubygems_version: 2.6.8
148
+ rubygems_version: 2.6.12
138
149
  signing_key:
139
150
  specification_version: 4
140
151
  summary: The main element for Trailblazer's BPMN-compliant workflows.