trailblazer-activity 0.15.1 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,106 @@
1
+ module Trailblazer
2
+ class Activity
3
+ class Schema
4
+ class Intermediate
5
+ # @private This class might get removed in 0.17.0.
6
+ module Compiler
7
+ module_function
8
+
9
+ DEFAULT_CONFIG = {wrap_static: Hash.new(Activity::TaskWrap.initial_wrap_static.freeze)} # DISCUSS: this really doesn't have to be here, but works for now and we want it in 99%.
10
+
11
+ # Compiles a {Schema} instance from an {intermediate} structure and
12
+ # the {implementation} object references.
13
+ #
14
+ # Intermediate structure, Implementation, calls extensions, passes {config} # TODO
15
+ def call(intermediate, implementation, config_merge: {})
16
+ config = DEFAULT_CONFIG
17
+ .merge(config_merge)
18
+ .freeze
19
+
20
+ circuit, outputs, nodes, config = schema_components(intermediate, implementation, config)
21
+
22
+ Schema.new(circuit, outputs, nodes, config)
23
+ end
24
+
25
+ # From the intermediate "template" and the actual implementation, compile a {Circuit} instance.
26
+ def schema_components(intermediate, implementation, config)
27
+ wiring = {}
28
+ nodes_attributes = []
29
+
30
+ intermediate.wiring.each do |task_ref, outs|
31
+ id = task_ref.id
32
+ impl_task = implementation.fetch(id)
33
+ task = impl_task.circuit_task
34
+ outputs = impl_task.outputs
35
+
36
+ wiring[task] = connections_for(outs, outputs, implementation)
37
+
38
+ nodes_attributes << [
39
+ id, # id
40
+ task, # task
41
+ task_ref[:data], # TODO: allow adding data from implementation.
42
+ outputs # {Activity::Output}s
43
+ ]
44
+
45
+ # Invoke each task's extensions (usually coming from the DSL user or some macro).
46
+ # We're expecting each {ext} to be a {TaskWrap::Extension::WrapStatic} instance.
47
+ config = invoke_extensions_for(config, impl_task, id)
48
+ end
49
+
50
+ start_id = intermediate.start_task_id
51
+
52
+ terminus_to_output = terminus_to_output(intermediate, implementation)
53
+ activity_outputs = terminus_to_output.values
54
+
55
+ circuit = Circuit.new(
56
+ wiring,
57
+ terminus_to_output.keys, # termini
58
+ start_task: implementation.fetch(start_id).circuit_task
59
+ )
60
+
61
+ return circuit,
62
+ activity_outputs,
63
+ Schema::Nodes(nodes_attributes),
64
+ config
65
+ end
66
+
67
+ # Compute the connections for {circuit_task}.
68
+ # For a terminus, this produces an empty hash.
69
+ def connections_for(intermediate_outs, task_outputs, implementation)
70
+ intermediate_outs.collect { |intermediate_out| # Intermediate::Out, it's abstract.
71
+ [
72
+ output_for_semantic(task_outputs, intermediate_out.semantic).signal,
73
+ implementation.fetch(intermediate_out.target).circuit_task # Find the implementation task, the Ruby code component for runtime.
74
+ ]
75
+ }.to_h
76
+ end
77
+
78
+ def terminus_to_output(intermediate, implementation)
79
+ terminus_to_semantic = intermediate.stop_task_ids
80
+
81
+ terminus_to_semantic.collect do |id, semantic|
82
+ terminus_task = implementation.fetch(id).circuit_task
83
+
84
+ [
85
+ terminus_task,
86
+ Activity::Output(terminus_task, semantic) # The End instance is the signal.
87
+ ]
88
+ end.to_h
89
+ end
90
+
91
+ # Run all Implementation::Task.extensions and return new {config}
92
+ def invoke_extensions_for(config, impl_task, id)
93
+ impl_task
94
+ .extensions
95
+ .inject(config) { |cfg, ext| ext.(config: cfg, id: id, task: impl_task) } # {ext} must return new config hash.
96
+ end
97
+
98
+ # In an array of {Activity::Output}, find the output matching {semantic}.
99
+ def output_for_semantic(outputs, semantic)
100
+ outputs.find { |out| out.semantic == semantic } or raise "`#{semantic}` not found in implementation"
101
+ end
102
+ end # Intermediate
103
+ end
104
+ end
105
+ end
106
+ end
@@ -1,10 +1,11 @@
1
1
  class Trailblazer::Activity
2
2
  class Schema
3
+ # @private This class might get removed in 0.17.0.
3
4
  module Implementation
4
5
  # Implementation structures
5
6
  Task = Struct.new(:circuit_task, :outputs, :extensions)
6
7
 
7
- def self.Task(task, outputs, extensions=[])
8
+ def self.Task(task, outputs, extensions = [])
8
9
  Task.new(task, outputs, extensions)
9
10
  end
10
11
  end
@@ -2,97 +2,17 @@ class Trailblazer::Activity
2
2
  class Schema
3
3
  # An {Intermediate} structure defines the *structure* of the circuit. It usually
4
4
  # comes from a DSL or a visual editor.
5
- class Intermediate < Struct.new(:wiring, :stop_task_ids, :start_task_ids)
5
+ # @private This class might get removed in 0.17.0.
6
+ class Intermediate < Struct.new(:wiring, :stop_task_ids, :start_task_id)
6
7
  TaskRef = Struct.new(:id, :data) # TODO: rename to NodeRef
7
8
  Out = Struct.new(:semantic, :target)
8
9
 
9
- def self.TaskRef(id, data={}); TaskRef.new(id, data) end
10
- def self.Out(*args); Out.new(*args) end
11
-
12
- # Compiles a {Schema} instance from an {intermediate} structure and
13
- # the {implementation} object references.
14
- #
15
- # Intermediate structure, Implementation, calls extensions, passes {config} # TODO
16
- def self.call(intermediate, implementation, config_merge: {})
17
- config_default = {wrap_static: Hash.new(TaskWrap.initial_wrap_static)} # DISCUSS: this really doesn't have to be here, but works for now and we want it in 99%.
18
- config = config_default.merge(config_merge)
19
- config.freeze
20
-
21
- circuit = circuit(intermediate, implementation)
22
- nodes = node_attributes(intermediate, implementation)
23
- outputs = outputs(intermediate.stop_task_ids, nodes)
24
- config = config(implementation, config: config)
25
-
26
- Schema.new(circuit, outputs, nodes, config)
27
- end
28
-
29
- # From the intermediate "template" and the actual implementation, compile a {Circuit} instance.
30
- def self.circuit(intermediate, implementation)
31
- end_events = intermediate.stop_task_ids
32
-
33
- wiring = Hash[
34
- intermediate.wiring.collect do |task_ref, outs|
35
- task = implementation.fetch(task_ref.id)
36
-
37
- [
38
- task.circuit_task,
39
- end_events.include?(task_ref.id) ? {} : connections_for(outs, task.outputs, implementation)
40
- ]
41
- end
42
- ]
43
-
44
- Circuit.new(
45
- wiring,
46
- intermediate.stop_task_ids.collect { |id| implementation.fetch(id).circuit_task },
47
- start_task: intermediate.start_task_ids.collect { |id| implementation.fetch(id).circuit_task }[0]
48
- )
49
- end
50
-
51
- # Compute the connections for {circuit_task}.
52
- def self.connections_for(outs, task_outputs, implementation)
53
- Hash[
54
- outs.collect { |required_out| # Intermediate::Out, it's abstract.
55
- [
56
- for_semantic(task_outputs, required_out.semantic).signal,
57
- implementation.fetch(required_out.target).circuit_task
58
- ]
59
- }.compact
60
- ]
10
+ def self.TaskRef(id, data = {})
11
+ TaskRef.new(id, data)
61
12
  end
62
13
 
63
- def self.node_attributes(intermediate, implementation)
64
- intermediate.wiring.collect do |task_ref, outputs|
65
- id = task_ref.id
66
- implementation_task = implementation.fetch(id)
67
- data = task_ref[:data] # TODO: allow adding data from implementation.
68
-
69
- NodeAttributes.new(id, implementation_task.outputs, implementation_task.circuit_task, data)
70
- end
71
- end
72
-
73
- # intermediate/implementation independent.
74
- def self.outputs(stop_task_ids, nodes_attributes)
75
- stop_task_ids.collect do |id|
76
- # Grab the {outputs} of the stop nodes.
77
- nodes_attributes.find { |node_attrs| id == node_attrs.id }.outputs
78
- end.flatten(1)
79
- end
80
-
81
- # Invoke each task's extensions (usually coming from the DSL user or some macro).
82
- # We're expecting each {ext} to be a {TaskWrap::Extension::WrapStatic} instance.
83
- def self.config(implementation, config:)
84
- implementation.each do |id, task|
85
- task.extensions.each { |ext| config = ext.(config: config, id: id, task: task) } # DISCUSS: ext must return new {Config}.
86
- end
87
-
88
- config
89
- end
90
-
91
-
92
-
93
- # Apply to any array.
94
- private_class_method def self.for_semantic(outputs, semantic)
95
- outputs.find { |out| out.semantic == semantic } or raise "`#{semantic}` not found"
14
+ def self.Out(*args)
15
+ Out.new(*args)
96
16
  end
97
17
  end # Intermediate
98
18
  end
@@ -1,15 +1,38 @@
1
1
  module Trailblazer
2
2
  class Activity
3
- # In NodeAttributes we store data from Intermediate and Implementing compile-time.
4
- # This would be lost otherwise.
5
- NodeAttributes = Struct.new(:id, :outputs, :task, :data) # TODO: rename to Schema::Task::Attributes.
6
-
3
+ # The idea with {:config} is to have a generic runtime store for feature fields
4
+ # like {:wrap_static} but also for flags, e.g. `each: true` from the Each() macro.
7
5
  class Schema < Struct.new(:circuit, :outputs, :nodes, :config)
8
6
  # {:nodes} is passed directly from {compile_activity}. We need to store this data here.
9
7
 
10
8
  # @!method to_h()
11
9
  # Returns a hash containing the schema's components.
12
10
 
11
+ class Nodes < Hash
12
+ # In Attributes we store data from Intermediate and Implementing compile-time.
13
+ # This would be lost otherwise.
14
+ Attributes = Struct.new(:id, :task, :data, :outputs)
15
+ end
16
+
17
+ # Builder for {Schema::Nodes} datastructure.
18
+ #
19
+ # A {Nodes} instance is a hash of Attributes, keyed by task. It turned out that
20
+ # 90% of introspect lookups, we search for attributes for a particular *task*, not ID.
21
+ # That's why in 0.16.0 we changed this structure.5
22
+ #
23
+ # Nodes{#<task> => #<Nodes::Attributes id= task= data= outputs=>}
24
+ #
25
+ # @private Please use {Introspect.Nodes} for querying nodes.
26
+ def self.Nodes(nodes)
27
+ Nodes[
28
+ nodes.collect do |attrs|
29
+ [
30
+ attrs[1], # task
31
+ Nodes::Attributes.new(*attrs).freeze
32
+ ]
33
+ end
34
+ ].freeze
35
+ end
13
36
  end # Schema
14
37
  end
15
38
  end
@@ -12,8 +12,8 @@ module Trailblazer
12
12
  @options = options.merge(semantic: semantic)
13
13
  end
14
14
 
15
- def call(args, **circuit_options)
16
- return self, args, **circuit_options
15
+ def call(args, **)
16
+ return self, args
17
17
  end
18
18
 
19
19
  def to_h
@@ -21,21 +21,23 @@ module Trailblazer
21
21
  end
22
22
 
23
23
  def to_s
24
- %{#<#{self.class.name} #{@options.collect { |k, v| "#{k}=#{v.inspect}" }.join(" ")}>}
24
+ %(#<#{self.class.name} #{@options.collect { |k, v| "#{k}=#{v.inspect}" }.join(" ")}>)
25
25
  end
26
26
 
27
27
  alias inspect to_s
28
28
  end
29
29
 
30
30
  class Start < End
31
- def call(args, **circuit_options)
32
- return Activity::Right, args, **circuit_options
31
+ def call(args, **)
32
+ return Activity::Right, args
33
33
  end
34
34
  end
35
35
 
36
- class Signal; end
36
+ class Signal; end
37
+
37
38
  class Right < Signal; end
38
- class Left < Signal; end
39
+
40
+ class Left < Signal; end
39
41
 
40
42
  # signal: actual signal emitted by the task
41
43
  # color: the mapping, where this signal will travel to. This can be e.g. Left=>:success. The polarization when building the graph.
@@ -13,7 +13,10 @@ class Trailblazer::Activity
13
13
  return_signal, return_args = task.(original_arguments, **original_circuit_options)
14
14
 
15
15
  # DISCUSS: do we want original_args here to be passed on, or the "effective" return_args which are different to original_args now?
16
- wrap_ctx = wrap_ctx.merge( return_signal: return_signal, return_args: return_args )
16
+ wrap_ctx = wrap_ctx.merge(
17
+ return_signal: return_signal,
18
+ return_args: return_args
19
+ )
17
20
 
18
21
  return wrap_ctx, original_args
19
22
  end
@@ -9,7 +9,7 @@ module Trailblazer
9
9
  # |-- Call (call actual task) id: "task_wrap.call_task"
10
10
  # |-- Trace.capture_return [optional]
11
11
  # |-- Wrap::End
12
- module TaskWrap
12
+ module TaskWrap
13
13
  # inserts must be
14
14
  # An {Extension} can be used for {:wrap_runtime}. It expects a collection of
15
15
  # "friendly interface" arrays.
@@ -86,14 +86,20 @@ Please update to the new TaskWrap.Extension() API."
86
86
  end
87
87
 
88
88
  def call(config:, task:, **)
89
- # Add the extension's task(s) to the activity's {wrap_static} taskWrap
90
- # by using the immutable {Config} interface.
91
- before_pipe = Config.get(config, :wrap_static, task.circuit_task)
89
+ # DISCUSS: does it make sense to test the immutable behavior here? What would be the worst outcome?
90
+ task = task.circuit_task # Receives {Schema::Implementation::Task}. DISCUSS: why?
92
91
 
93
- Config.set(config, :wrap_static, task.circuit_task, @extension.(before_pipe))
92
+ # Add the extension's task(s) to the activity's {:wrap_static} taskWrap
93
+ # which is stored in the {:config} field.
94
+ wrap_static = config[:wrap_static] # the activity's {wrap_static}.
95
+ task_wrap = wrap_static[task] # the "original" taskWrap for {task}.
96
+
97
+ # Overwrite the original task_wrap:
98
+ wrap_static = wrap_static.merge(task => @extension.(task_wrap))
99
+
100
+ config.merge(wrap_static: wrap_static) # Return new config hash. This needs to be immutable code!
94
101
  end
95
102
  end # WrapStatic
96
-
97
103
  end # Extension
98
104
  end # TaskWrap
99
105
  end
@@ -29,7 +29,7 @@ class Trailblazer::Activity
29
29
  insert_before: :Prepend,
30
30
  insert_after: :Append,
31
31
  append: :Append,
32
- prepend: :Prepend,
32
+ prepend: :Prepend
33
33
  }.fetch(name)
34
34
 
35
35
  warn "[Trailblazer] Using `Trailblazer::Activity::TaskWrap::Pipeline.method(:#{name})` is deprecated.
@@ -10,7 +10,7 @@ class Trailblazer::Activity
10
10
  # @api private
11
11
  # @interface Runner
12
12
  def self.call(task, args, **circuit_options)
13
- wrap_ctx = { task: task }
13
+ wrap_ctx = {task: task}
14
14
 
15
15
  # this pipeline is "wrapped around" the actual `task`.
16
16
  task_wrap_pipeline = merge_static_with_runtime(task, **circuit_options) || raise
@@ -28,14 +28,13 @@ class Trailblazer::Activity
28
28
  return wrap_ctx[:return_signal], wrap_ctx[:return_args]
29
29
  end
30
30
 
31
-
32
31
  # Compute the task's wrap by applying alterations both static and from runtime.
33
32
  #
34
33
  # NOTE: this is for performance reasons: we could have only one hash containing everything but that'd mean
35
34
  # unnecessary computations at `call`-time since steps might not even be executed.
36
35
  # TODO: make this faster.
37
- private_class_method def self.merge_static_with_runtime(task, wrap_runtime:, **circuit_options)
38
- static_wrap = TaskWrap.wrap_static_for(task, **circuit_options) # find static wrap for this specific task [, or default wrap activity].
36
+ private_class_method def self.merge_static_with_runtime(task, wrap_runtime:, activity:, **circuit_options)
37
+ static_wrap = TaskWrap.wrap_static_for(task, activity) # find static wrap for this specific task [, or default wrap activity].
39
38
 
40
39
  # Apply runtime alterations.
41
40
  # Grab the additional wirings for the particular `task` from `wrap_runtime`.
@@ -44,9 +43,11 @@ class Trailblazer::Activity
44
43
  end # Runner
45
44
 
46
45
  # Retrieve the static wrap config from {activity}.
47
- def self.wrap_static_for(task, activity:, **)
48
- wrap_static = activity[:wrap_static]
49
- wrap_static[task] or raise "#{task}"
46
+ # @private
47
+ def self.wrap_static_for(task, activity)
48
+ activity.to_h
49
+ .fetch(:config)
50
+ .fetch(:wrap_static)[task] # the {wrap_static} for {task}.
50
51
  end
51
52
  end
52
53
  end
@@ -21,7 +21,7 @@ module Trailblazer
21
21
  circuit_options = circuit_options.merge(
22
22
  runner: TaskWrap::Runner,
23
23
  wrap_runtime: wrap_runtime,
24
- activity: container_activity, # for Runner. Ideally we'd have a list of all static_wraps here (even nested).
24
+ activity: container_activity # for Runner. Ideally we'd have a list of all static_wraps here (even nested).
25
25
  )
26
26
 
27
27
  # signal, (ctx, flow), circuit_options =
@@ -43,10 +43,13 @@ module Trailblazer
43
43
  #
44
44
  # DISCUSS: we could cache that on Strategy/Operation level.
45
45
  # merging the **config hash is 1/4 slower than before.
46
- def container_activity_for(activity, wrap_static: initial_wrap_static, **config)
46
+ def container_activity_for(activity, wrap_static: initial_wrap_static, id: nil, **config)
47
47
  {
48
- wrap_static: {activity => wrap_static},
49
- **config
48
+ config: {
49
+ wrap_static: {activity => wrap_static},
50
+ **config
51
+ },
52
+ nodes: Schema.Nodes([[id, activity]])
50
53
  }
51
54
  end
52
55
 
@@ -54,4 +57,3 @@ module Trailblazer
54
57
  end # TaskWrap
55
58
  end
56
59
  end
57
-
@@ -10,8 +10,9 @@ module Trailblazer
10
10
  def self.def_steps(*names)
11
11
  Module.new do
12
12
  module_function
13
+
13
14
  names.each do |name|
14
- define_method(name) do | ctx, ** |
15
+ define_method(name) do |ctx, **|
15
16
  ctx[:seq] << name
16
17
  ctx.key?(name) ? ctx[name] : true
17
18
  end
@@ -24,21 +25,15 @@ module Trailblazer
24
25
  # @example
25
26
  # task task: T.def_task(:create)
26
27
  def self.def_task(name)
27
- Module.new do
28
- define_singleton_method(name) do | (ctx, flow_options), ** |
29
- ctx[:seq] << name
30
- signal = ctx.key?(name) ? ctx[name] : Activity::Right
31
-
32
- return signal, [ctx, flow_options]
33
- end
34
- end.method(name)
28
+ def_tasks(name).method(name)
35
29
  end
36
30
 
37
31
  def self.def_tasks(*names)
38
32
  Module.new do
39
33
  module_function
34
+
40
35
  names.each do |name|
41
- define_method(name) do | (ctx, flow_options), ** |
36
+ define_method(name) do |(ctx, flow_options), **|
42
37
  ctx[:seq] << name
43
38
  signal = ctx.key?(name) ? ctx[name] : Activity::Right
44
39
 
@@ -49,136 +44,62 @@ module Trailblazer
49
44
  end
50
45
 
51
46
  module Assertions
52
- Activity = Trailblazer::Activity
53
- Inter = Trailblazer::Activity::Schema::Intermediate
54
- Schema = Trailblazer::Activity::Schema
55
- TaskWrap = Trailblazer::Activity::TaskWrap
56
-
57
- # `:seq` is always passed into ctx.
58
- # @param :seq String What the {:seq} variable in the result ctx looks like. (expected seq)
59
- # @param :expected_ctx_variables Variables that are added during the call by the asserted activity.
60
- def assert_call(activity, terminus: :success, seq: "[]", expected_ctx_variables: {}, **ctx_variables)
61
- # Call without taskWrap!
62
- signal, (ctx, _) = activity.([{seq: [], **ctx_variables}, _flow_options = {}]) # simply call the activity with the input you want to assert.
63
-
64
- assert_call_for(signal, ctx, terminus: terminus, seq: seq, **expected_ctx_variables, **ctx_variables)
65
- end
66
-
67
- # Use {TaskWrap.invoke} to call the activity.
68
- def assert_invoke(activity, terminus: :success, seq: "[]", circuit_options: {}, expected_ctx_variables: {}, **ctx_variables)
69
- signal, (ctx, _flow_options) = TaskWrap.invoke(
70
- activity,
71
- [
72
- {seq: [], **ctx_variables},
73
- {} # flow_options
74
- ],
75
- **circuit_options
76
- )
77
-
78
- assert_call_for(signal, ctx, terminus: terminus, seq: seq, **ctx_variables, **expected_ctx_variables) # DISCUSS: ordering of variables?
79
- end
80
-
81
- def assert_call_for(signal, ctx, terminus: :success, seq: "[]", **ctx_variables)
82
- assert_equal signal.to_h[:semantic], terminus, "assert_call expected #{terminus} terminus, not #{signal}. Use assert_call(activity, terminus: #{signal.to_h[:semantic].inspect})"
83
-
84
- assert_equal ctx.inspect, {seq: "%%%"}.merge(ctx_variables).inspect.sub('"%%%"', seq)
85
-
86
- return ctx
87
- end
88
-
89
- module Implementing
90
- extend Activity::Testing.def_tasks(:a, :b, :c, :d, :f, :g)
47
+ # `:seq` is always passed into ctx.
48
+ # @param :seq String What the {:seq} variable in the result ctx looks like. (expected seq)
49
+ # @param :expected_ctx_variables Variables that are added during the call by the asserted activity.
50
+ def assert_call(activity, terminus: :success, seq: "[]", expected_ctx_variables: {}, **ctx_variables)
51
+ # Call without taskWrap!
52
+ signal, (ctx, _) = activity.([{seq: [], **ctx_variables}, _flow_options = {}]) # simply call the activity with the input you want to assert.
53
+
54
+ assert_call_for(signal, ctx, terminus: terminus, seq: seq, **expected_ctx_variables, **ctx_variables)
55
+ end
91
56
 
92
- Start = Activity::Start.new(semantic: :default)
93
- Failure = Activity::End(:failure)
94
- Success = Activity::End(:success)
95
- end
57
+ # Use {TaskWrap.invoke} to call the activity.
58
+ def assert_invoke(activity, terminus: :success, seq: "[]", circuit_options: {}, expected_ctx_variables: {}, **ctx_variables)
59
+ signal, (ctx, _flow_options) = Activity::TaskWrap.invoke(
60
+ activity,
61
+ [
62
+ {seq: [], **ctx_variables},
63
+ {} # flow_options
64
+ ],
65
+ **circuit_options
66
+ )
67
+
68
+ assert_call_for(signal, ctx, terminus: terminus, seq: seq, **ctx_variables, **expected_ctx_variables) # DISCUSS: ordering of variables?
69
+ end
96
70
 
97
- # TODO: Remove this once all its references are removed
98
- def implementing
99
- Implementing
100
- end
71
+ def assert_call_for(signal, ctx, terminus: :success, seq: "[]", **ctx_variables)
72
+ assert_equal signal.to_h[:semantic], terminus, "assert_call expected #{terminus} terminus, not #{signal}. Use assert_call(activity, terminus: #{signal.to_h[:semantic].inspect})"
101
73
 
102
- def flat_activity(implementing: Implementing)
103
- return @_flat_activity if defined?(@_flat_activity)
104
-
105
- intermediate = Inter.new(
106
- {
107
- Inter::TaskRef("Start.default") => [Inter::Out(:success, :B)],
108
- Inter::TaskRef(:B, additional: true) => [Inter::Out(:success, :C), Inter::Out(:failure, "End.failure")],
109
- Inter::TaskRef(:C) => [Inter::Out(:success, "End.success")],
110
- Inter::TaskRef("End.success", stop_event: true) => [Inter::Out(:success, nil)],
111
- Inter::TaskRef("End.failure", stop_event: true) => [Inter::Out(:failure, nil)],
112
- },
113
- ["End.success", "End.failure"],
114
- ["Start.default"], # start
115
- )
116
-
117
- implementation = {
118
- "Start.default" => Schema::Implementation::Task(st = Implementing::Start, [Activity::Output(Activity::Right, :success)], []),
119
- :B => Schema::Implementation::Task(b = implementing.method(:b), [Activity::Output(Activity::Right, :success), Activity::Output(Activity::Left, :failure)], []),
120
- :C => Schema::Implementation::Task(c = implementing.method(:c), [Activity::Output(Activity::Right, :success)], []),
121
- "End.success" => Schema::Implementation::Task(_es = Implementing::Success, [Activity::Output(Implementing::Success, :success)], []), # DISCUSS: End has one Output, signal is itself?
122
- "End.failure" => Schema::Implementation::Task(Implementing::Failure, [Activity::Output(Implementing::Failure, :failure)], []), # DISCUSS: End has one Output, signal is itself?
123
- }
124
-
125
- schema = Inter.(intermediate, implementation)
126
-
127
- @_flat_activity = Activity.new(schema)
128
- end
74
+ assert_equal ctx.inspect, {seq: "%%%"}.merge(ctx_variables).inspect.sub('"%%%"', seq)
129
75
 
130
- # FIXME: why is this in Testing? We don't need this anywhere but in this gem.
131
- def nested_activity(flat_activity: bc, d_id: :D)
132
- intermediate = Inter.new(
133
- {
134
- Inter::TaskRef("Start.default") => [Inter::Out(:success, :B)],
135
- Inter::TaskRef(:B, more: true) => [Inter::Out(:success, d_id)],
136
- Inter::TaskRef(d_id) => [Inter::Out(:success, :E)],
137
- Inter::TaskRef(:E) => [Inter::Out(:success, "End.success")],
138
- Inter::TaskRef("End.success", stop_event: true) => [Inter::Out(:success, nil)]
139
- },
140
- ["End.success"],
141
- ["Start.default"] # start
142
- )
143
-
144
- implementation = {
145
- "Start.default" => Schema::Implementation::Task(st = Implementing::Start, [Activity::Output(Activity::Right, :success)], []),
146
- :B => Schema::Implementation::Task(b = Implementing.method(:b), [Activity::Output(Activity::Right, :success)], []),
147
- d_id => Schema::Implementation::Task(flat_activity, [Activity::Output(Implementing::Success, :success)], []),
148
- :E => Schema::Implementation::Task(e = Implementing.method(:f), [Activity::Output(Activity::Right, :success)], []),
149
- "End.success" => Schema::Implementation::Task(_es = Implementing::Success, [Activity::Output(Implementing::Success, :success)], []), # DISCUSS: End has one Output, signal is itself?
150
- }
151
-
152
- schema = Inter.(intermediate, implementation)
153
-
154
- Activity.new(schema)
155
- end
76
+ return ctx
77
+ end
156
78
 
157
- alias_method :bc, :flat_activity
158
- alias_method :bde, :nested_activity
79
+ # Tests {:circuit} and {:outputs} fields so far.
80
+ def assert_process_for(process, *args)
81
+ semantics, circuit = args[0..-2], args[-1]
159
82
 
160
- # Tests {:circuit} and {:outputs} fields so far.
161
- def assert_process_for(process, *args)
162
- semantics, circuit = args[0..-2], args[-1]
83
+ inspects = semantics.collect { |semantic| %(#<struct Trailblazer::Activity::Output signal=#<Trailblazer::Activity::End semantic=#{semantic.inspect}>, semantic=#{semantic.inspect}>) }
163
84
 
164
- inspects = semantics.collect { |semantic| %{#<struct Trailblazer::Activity::Output signal=#<Trailblazer::Activity::End semantic=#{semantic.inspect}>, semantic=#{semantic.inspect}>} }
85
+ assert_equal %([#{inspects.join(", ")}]), process.to_h[:outputs].inspect
165
86
 
166
- assert_equal %{[#{inspects.join(", ")}]}, process.to_h[:outputs].inspect
87
+ assert_circuit(process, circuit)
167
88
 
168
- assert_circuit(process, circuit)
89
+ process
90
+ end
169
91
 
170
- process
171
- end
92
+ alias_method :assert_process, :assert_process_for
172
93
 
173
- def assert_circuit(schema, circuit)
174
- cct = Cct(schema)
94
+ def assert_circuit(schema, circuit)
95
+ cct = Cct(schema)
175
96
 
176
- cct = cct.gsub("#<Trailblazer::Activity::TaskBuilder::Task user_proc=", "<*")
177
- assert_equal %{#{circuit}}, cct
178
- end
97
+ cct = cct.gsub("#<Trailblazer::Activity::TaskBuilder::Task user_proc=", "<*")
98
+ assert_equal circuit.to_s, cct
99
+ end
179
100
 
180
101
  def Cct(activity)
181
- Trailblazer::Developer::Render::Circuit.(activity, inspect_task: Trailblazer::Activity::Testing.method(:render_task))
102
+ Activity::Introspect::Render.(activity, inspect_task: Trailblazer::Activity::Testing.method(:render_task))
182
103
  end
183
104
  end
184
105
 
@@ -1,7 +1,7 @@
1
1
  module Trailblazer
2
2
  module Version
3
3
  module Activity
4
- VERSION = '0.15.1'
4
+ VERSION = "0.16.0"
5
5
  end
6
6
  end
7
7
  end