trailblazer-activity 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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.