trailblazer-activity 0.2.0 → 0.2.1

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
  SHA1:
3
- metadata.gz: cda2a49909b44be7195fed23b15bb82f6d4d4ac4
4
- data.tar.gz: 3d17e44f5c91ca1a61797d1f3d5f8d515362c2ad
3
+ metadata.gz: 81fa1b9e57d3f63ee5ee4b5caadea44250fcdbad
4
+ data.tar.gz: ba3e8fc06d51e146702ac7b6eb6765fe3af6e7ee
5
5
  SHA512:
6
- metadata.gz: 70ca9ad73d4993f6295ee2dfc40b527c46fc25fc7e3384ec6dd7f57f726db5c7599affda8cfec180430020cb8f6a4340892921008bd0a1b3e54f4fa362e3dcc8
7
- data.tar.gz: 13400c97fecca6f83d0282205af04c7f60f7136b4f5f6a7175ed5035277a12e9bf9a22044102577fcca26187fa6badf5185246a3dba61c87e1eab5f06b98ac57
6
+ metadata.gz: 62291d8bbb37376be5e1c6548c35d7df00a7edf654697ac26ed786d55a2bdf49de6130ad0bcfa3a221ff1dbf30935f9ae6d7f2fbe3d143bd4ae42c52d106b110
7
+ data.tar.gz: 3503a6d13258343169a5eddbcc99fa63a169f493dcde5e42962e03775b97c9f09bb9ed68aaa9891ee13a262539dda4051ac4b7e5895b3f01a93f3b4acdd08184
data/CHANGES.md CHANGED
@@ -1,3 +1,8 @@
1
+ # 0.2.1
2
+
3
+ * Restructure all `Wrap`-specific tasks.
4
+ * Remove `Hash::Immutable`, we will use the `hamster` gem instead.
5
+
1
6
  # 0.2.0
2
7
 
3
8
  * The `Activity#call` API is now
@@ -11,9 +11,16 @@ module Trailblazer
11
11
  require "trailblazer/activity/version"
12
12
  require "trailblazer/activity/graph"
13
13
  require "trailblazer/activity/subprocess"
14
+
15
+ require "trailblazer/activity/wrap"
16
+ require "trailblazer/wrap/variable_mapping"
17
+ require "trailblazer/wrap/call_task"
18
+ require "trailblazer/wrap/trace"
19
+ require "trailblazer/wrap/inject"
20
+ require "trailblazer/wrap/runner"
21
+
14
22
  require "trailblazer/activity/trace"
15
23
  require "trailblazer/activity/present"
16
- require "trailblazer/activity/wrap"
17
24
 
18
25
  # Only way to build an Activity.
19
26
  def self.from_wirings(wirings, &block)
@@ -125,8 +132,5 @@ module Trailblazer
125
132
  @graph.find_all { |node| node[:_wrapped] == task }.first
126
133
  end
127
134
  end
128
-
129
- module Interface
130
- end
131
135
  end
132
136
  end
@@ -36,36 +36,17 @@ module Trailblazer
36
36
  block.(activity, *args)
37
37
  end
38
38
 
39
- # Default tracing tasks to be plugged into the wrap circuit.
39
+ # Graph insertions for the trace tasks that capture the arguments just before calling the task,
40
+ # and before the TaskWrap is finished.
41
+ #
42
+ # Note that the TaskWrap steps are implemented in Activity::Wrap::Trace.
40
43
  def self.wirings
41
44
  [
42
- [ :insert_before!, "task_wrap.call_task", node: [ Trace.method(:capture_args), id: "task_wrap.capture_args" ], outgoing: [ Circuit::Right, {} ], incoming: ->(*) { true } ],
43
- [ :insert_before!, "End.default", node: [ Trace.method(:capture_return), id: "task_wrap.capture_return" ], outgoing: [ Circuit::Right, {} ], incoming: ->(*) { true } ],
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 } ],
44
47
  ]
45
48
  end
46
49
 
47
- # def self.capture_args(direction, options, flow_options, wrap_config, original_flow_options)
48
- def self.capture_args((wrap_config, original_args), **circuit_options)
49
- (original_options, original_flow_options, _) = original_args[0]
50
-
51
- original_flow_options[:stack].indent!
52
-
53
- original_flow_options[:stack] << [ wrap_config[:task], :args, nil, {}, original_flow_options[:introspection] ]
54
-
55
- [ Circuit::Right, [wrap_config, original_args ], **circuit_options ]
56
- end
57
-
58
- def self.capture_return((wrap_config, original_args), **circuit_options)
59
- (original_options, original_flow_options, _) = original_args[0]
60
-
61
- original_flow_options[:stack] << [ wrap_config[:task], :return, wrap_config[:result_direction], {} ]
62
-
63
- original_flow_options[:stack].unindent!
64
-
65
-
66
- [ Circuit::Right, [wrap_config, original_args ], **circuit_options ]
67
- end
68
-
69
50
  # Mutable/stateful per design. We want a (global) stack!
70
51
  class Stack
71
52
  def initialize
@@ -1,5 +1,5 @@
1
1
  module Trailblazer
2
2
  class Activity
3
- VERSION = "0.2.0"
3
+ VERSION = "0.2.1"
4
4
  end
5
5
  end
@@ -1,68 +1,5 @@
1
1
  class Trailblazer::Activity
2
2
  module Wrap
3
- # The runner is passed into Activity#call( runner: Runner ) and is called for every task in the circuit.
4
- # Its primary job is to actually `call` the task.
5
- #
6
- # Here, we extend this, and wrap the task `call` into its own pipeline, so we can add external behavior per task.
7
- module Runner
8
- # Runner signature: call( task, direction, options, static_wraps )
9
- #
10
- # @api private
11
- # @interface Runner
12
- def self.call(task, (options, *args), wrap_runtime: raise, wrap_static: raise, **circuit_options)
13
- wrap_ctx = { task: task }
14
-
15
- # this activity is "wrapped around" the actual `task`.
16
- task_wrap_activity = apply_wirings(task, wrap_static, wrap_runtime)
17
-
18
- # We save all original args passed into this Runner.call, because we want to return them later after this wrap
19
- # is finished.
20
- original_args = [ [options, *args], circuit_options.merge( wrap_runtime: wrap_runtime, wrap_static: wrap_static ) ]
21
-
22
- # call the wrap for the task.
23
- wrap_end_signal, ( wrap_ctx, _ ) = task_wrap_activity.(
24
- [ wrap_ctx, original_args ] # we omit circuit_options here on purpose, so the wrapping activity uses the plain Runner.
25
- )
26
-
27
- # don't return the wrap's end signal, but the one from call_task.
28
- # return all original_args for the next "real" task in the circuit (this includes circuit_options).
29
-
30
- # raise if wrap_ctx[:result_args][2] != static_wraps
31
-
32
- # TODO: make circuit ignore all returned but the first
33
- [ wrap_ctx[:result_direction], wrap_ctx[:result_args] ]
34
- end
35
-
36
- private
37
-
38
- # Compute the task's wrap by applying alterations both static and from runtime.
39
- #
40
- # NOTE: this is for performance reasons: we could have only one hash containing everything but that'd mean
41
- # unnecessary computations at `call`-time since steps might not even be executed.
42
- def self.apply_wirings(task, wrap_static, wrap_runtime)
43
- wrap_activity = wrap_static[task] # find static wrap for this specific task, or default wrap activity.
44
-
45
- # Apply runtime alterations.
46
- # Grab the additional wirings for the particular `task` from `wrap_runtime` (returns default otherwise).
47
- wrap_activity = Trailblazer::Activity.merge(wrap_activity, wrap_runtime[task])
48
- end
49
- end # Runner
50
-
51
- # The call_task method implements one default step `Call` in the Wrap::Activity circuit.
52
- # It calls the actual, wrapped task.
53
- def self.call_task((wrap_ctx, original_args), **circuit_options)
54
- task = wrap_ctx[:task]
55
-
56
- # Call the actual task we're wrapping here.
57
- puts "~~~~wrap.call: #{task}"
58
- wrap_ctx[:result_direction], wrap_ctx[:result_args] = task.( *original_args )
59
-
60
- # DISCUSS: do we want original_args here to be passed on, or the "effective" result_args which are different to original_args now?
61
- [ Trailblazer::Circuit::Right, [ wrap_ctx, original_args ], **circuit_options ]
62
- end
63
-
64
- Call = method(:call_task)
65
-
66
3
  # Wrap::Activity is the actual circuit that implements the Task wrap. This circuit is
67
4
  # also known as `task_wrap`.
68
5
  #
@@ -86,7 +23,7 @@ class Trailblazer::Activity
86
23
  Trailblazer::Activity.from_wirings(
87
24
  [
88
25
  [ :attach!, target: [ Trailblazer::Circuit::End.new(:default), type: :event, id: "End.default" ], edge: [ Trailblazer::Circuit::Right, {} ] ],
89
- [ :insert_before!, "End.default", node: [ Call, id: "task_wrap.call_task" ], outgoing: [ Trailblazer::Circuit::Right, {} ], incoming: ->(*) { true } ]
26
+ [ :insert_before!, "End.default", node: [ Wrap.method(:call_task), id: "task_wrap.call_task" ], outgoing: [ Trailblazer::Circuit::Right, {} ], incoming: ->(*) { true } ]
90
27
  ]
91
28
  )
92
29
  end
@@ -60,38 +60,6 @@ module Trailblazer
60
60
  end
61
61
  end
62
62
  end
63
-
64
- # FIXME
65
- # TODO: rename Context::Hash::Immutable
66
- class Immutable
67
- def initialize(hash)
68
- @hash = hash
69
- end
70
-
71
- def [](key)
72
- @hash[key]
73
- end
74
-
75
- def to_hash # DISCUSS: where do we call this?
76
- @hash.to_hash # FIXME: should we do this?
77
- end
78
-
79
- def key?(key)
80
- @hash.key?(key)
81
- end
82
-
83
- def merge(hash)
84
- @hash.merge(hash)
85
- end
86
-
87
- def keys
88
- @hash.keys
89
- end
90
-
91
- # DISCUSS: raise in #[]=
92
- # each
93
- # TODO: Skill could inherit
94
- end
95
63
  end
96
64
 
97
65
  def self.Context(wrapped_options, mutable_options={})
@@ -0,0 +1,19 @@
1
+ class Trailblazer::Activity
2
+ module Wrap
3
+ # TaskWrap step that calls the actual wrapped task and passes all `original_args` to it.
4
+ #
5
+ # It writes to wrap_ctx[:result_direction], wrap_ctx[:result_args]
6
+ def self.call_task((wrap_ctx, original_args), **circuit_options)
7
+ task = wrap_ctx[:task]
8
+
9
+ # Call the actual task we're wrapping here.
10
+ puts "~~~~wrap.call: #{task}"
11
+ result_direction, result_args = task.( *original_args ) # we lose :exec_context here.
12
+
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
+ wrap_ctx = wrap_ctx.merge( result_direction: result_direction, result_args: result_args )
15
+
16
+ [ Trailblazer::Circuit::Right, [ wrap_ctx, original_args ], **circuit_options ]
17
+ end
18
+ end # Wrap
19
+ end
@@ -0,0 +1,32 @@
1
+ class Trailblazer::Activity
2
+ module Wrap
3
+ module Inject
4
+ # Returns an Alteration wirings that, when applied, inserts the {ReverseMergeDefaults} task
5
+ # before the {Wrap::Call} task. This is meant for macros and steps that accept a dependency
6
+ # injection but need a default parameter to be set if not injected.
7
+ # @returns Alteration
8
+ def self.Defaults(default_dependencies)
9
+ [
10
+ [ :insert_before!, "task_wrap.call_task", node: [ ReverseMergeDefaults.new( default_dependencies ), id: "ReverseMergeDefaults#{default_dependencies}" ], incoming: Proc.new{ true }, outgoing: [ Trailblazer::Circuit::Right, {} ] ]
11
+ ]
12
+ end
13
+
14
+ # @api private
15
+ # @returns Task
16
+ # @param Hash list of key/value that should be set if not already assigned/set before (or injected from the outside).
17
+ class ReverseMergeDefaults
18
+ def initialize(defaults)
19
+ @defaults = defaults
20
+ end
21
+
22
+ def call((wrap_ctx, original_args), **circuit_options)
23
+ ctx = original_args[0][0]
24
+
25
+ @defaults.each { |k, v| ctx[k] ||= v }
26
+
27
+ [ Trailblazer::Circuit::Right, [wrap_ctx, original_args] ]
28
+ end
29
+ end
30
+ end # Inject
31
+ end
32
+ end
@@ -0,0 +1,48 @@
1
+ class Trailblazer::Activity
2
+ module Wrap
3
+ # The runner is passed into Activity#call( runner: Runner ) and is called for every task in the circuit.
4
+ # It runs the TaskWrap per task.
5
+ #
6
+ # (wrap_ctx, original_args), **wrap_circuit_options
7
+ module Runner
8
+ # Runner signature: call( task, direction, options, static_wraps )
9
+ #
10
+ # @api private
11
+ # @interface Runner
12
+ def self.call(task, args, wrap_runtime: raise, wrap_static: raise, **circuit_options)
13
+ wrap_ctx = { task: task }
14
+
15
+ # this activity is "wrapped around" the actual `task`.
16
+ task_wrap_activity = apply_wirings(task, wrap_static, wrap_runtime)
17
+
18
+ # We save all original args passed into this Runner.call, because we want to return them later after this wrap
19
+ # is finished.
20
+ original_args = [ args, circuit_options.merge( wrap_runtime: wrap_runtime, wrap_static: wrap_static ) ]
21
+
22
+ # call the wrap {Activity} around the task.
23
+ wrap_end_signal, ( wrap_ctx, _ ) = task_wrap_activity.(
24
+ [ wrap_ctx, original_args ] # we omit circuit_options here on purpose, so the wrapping activity uses the default, plain Runner.
25
+ )
26
+
27
+ # don't return the wrap's end signal, but the one from call_task.
28
+ # return all original_args for the next "real" task in the circuit (this includes circuit_options).
29
+
30
+ [ wrap_ctx[:result_direction], wrap_ctx[:result_args] ]
31
+ end
32
+
33
+ private
34
+
35
+ # Compute the task's wrap by applying alterations both static and from runtime.
36
+ #
37
+ # NOTE: this is for performance reasons: we could have only one hash containing everything but that'd mean
38
+ # unnecessary computations at `call`-time since steps might not even be executed.
39
+ def self.apply_wirings(task, wrap_static, wrap_runtime)
40
+ wrap_activity = wrap_static[task] # find static wrap for this specific task, or default wrap activity.
41
+
42
+ # Apply runtime alterations.
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])
45
+ end
46
+ end # Runner
47
+ end
48
+ end
@@ -0,0 +1,27 @@
1
+ class Trailblazer::Activity
2
+ module Wrap
3
+ module Trace
4
+ # def self.capture_args(direction, options, flow_options, wrap_config, original_flow_options)
5
+ def self.capture_args((wrap_config, original_args), **circuit_options)
6
+ (original_options, original_flow_options, _) = original_args[0]
7
+
8
+ original_flow_options[:stack].indent!
9
+
10
+ original_flow_options[:stack] << [ wrap_config[:task], :args, nil, {}, original_flow_options[:introspection] ]
11
+
12
+ [ Trailblazer::Circuit::Right, [wrap_config, original_args], **circuit_options ]
13
+ end
14
+
15
+ def self.capture_return((wrap_config, original_args), **circuit_options)
16
+ (original_options, original_flow_options, _) = original_args[0]
17
+
18
+ original_flow_options[:stack] << [ wrap_config[:task], :return, wrap_config[:result_direction], {} ]
19
+
20
+ original_flow_options[:stack].unindent!
21
+
22
+
23
+ [ Trailblazer::Circuit::Right, [wrap_config, original_args], **circuit_options ]
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,92 @@
1
+ class Trailblazer::Activity
2
+ module Wrap
3
+ # TaskWrap step to compute the incoming {Context} for the wrapped task.
4
+ # This allows renaming, filtering, hiding, of the options passed into the wrapped task.
5
+ #
6
+ # Both Input and Output are typically to be added before and after task_wrap.call_task.
7
+ #
8
+ # @note Assumption: we always have :input _and_ :output, where :input produces a Context and :output decomposes it.
9
+ class Input
10
+ def initialize(filter)
11
+ @filter = Trailblazer::Option(filter)
12
+ end
13
+
14
+ # `original_args` are the actual args passed to the wrapped task: [ [options, ..], circuit_options ]
15
+ #
16
+ def call((wrap_ctx, original_args), **circuit_options)
17
+ # let user compute new ctx for the wrapped task.
18
+ input_ctx = apply_filter(*original_args)
19
+
20
+ # TODO: make this unnecessary.
21
+ # wrap user's hash in Context if it's not one, already (in case user used options.merge).
22
+ # DISCUSS: should we restrict user to .merge and options.Context?
23
+ input_ctx = Trailblazer.Context({}, input_ctx) unless input_ctx.instance_of?(Trailblazer::Context)
24
+
25
+ wrap_ctx = wrap_ctx.merge( vm_original_args: original_args )
26
+
27
+ # decompose the original_args since we want to modify them.
28
+ (original_ctx, original_flow_options), original_circuit_options = original_args
29
+
30
+ # instead of the original Context, pass on the filtered `input_ctx` in the wrap.
31
+ return Trailblazer::Circuit::Right, [ wrap_ctx, [[input_ctx, original_flow_options], original_circuit_options] ]
32
+ end
33
+
34
+ private
35
+
36
+ def apply_filter((original_ctx, original_flow_options), original_circuit_options)
37
+ @filter.( original_ctx, **original_circuit_options )
38
+ end
39
+ end
40
+
41
+ # TaskWrap step to compute the outgoing {Context} from the wrapped task.
42
+ # This allows renaming, filtering, hiding, of the options returned from the wrapped task.
43
+ class Output
44
+ def initialize(filter, strategy=CopyMutableToOriginal)
45
+ @filter = Trailblazer::Option(filter)
46
+ @strategy = strategy
47
+ end
48
+
49
+ # Runs the user filter and replaces the ctx in `wrap_ctx[:result_args]` with the filtered one.
50
+ def call((wrap_ctx, original_args), **circuit_options)
51
+ (original_ctx, original_flow_options), original_circuit_options = original_args
52
+
53
+ returned_ctx, _ = wrap_ctx[:result_args] # this is the context returned from `call`ing the task.
54
+
55
+ # returned_ctx is the Context object from the nested operation. In <=2.1, this might be a completely different one
56
+ # than "ours" we created in Input. We now need to compile a list of all added values. This is time-intensive and should
57
+ # be optimized by removing as many Context creations as possible (e.g. the one adding self[] stuff in Operation.__call__).
58
+ _, mutable_data = returned_ctx.decompose # FIXME: this is a weak assumption. What if the task returns a deeply nested Context?
59
+
60
+ # let user compute the output.
61
+ output = apply_filter(mutable_data, original_flow_options, original_circuit_options)
62
+
63
+ original_ctx = wrap_ctx[:vm_original_args][0][0]
64
+
65
+ new_ctx = @strategy.( original_ctx, output ) # here, we compute the "new" options {Context}.
66
+
67
+ wrap_ctx = wrap_ctx.merge( result_args: [new_ctx, original_flow_options] )
68
+
69
+ # and then pass on the "new" context.
70
+ return Trailblazer::Circuit::Right, [ wrap_ctx, original_args ]
71
+ end
72
+
73
+ private
74
+
75
+ # @note API not stable
76
+ def apply_filter(mutable_data, original_flow_options, original_circuit_options)
77
+ @filter.(mutable_data, **original_circuit_options)
78
+ end
79
+
80
+ # "merge" Strategy
81
+ class CopyMutableToOriginal
82
+ # @param original Context
83
+ # @param options Context The object returned from a (nested) {Activity}.
84
+ def self.call(original, mutable)
85
+ mutable.each { |k,v| original[k] = v }
86
+
87
+ original
88
+ end
89
+ end
90
+ end
91
+ end # Wrap
92
+ 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.0
4
+ version: 0.2.1
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-11 00:00:00.000000000 Z
11
+ date: 2017-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hirb
@@ -108,6 +108,11 @@ files:
108
108
  - lib/trailblazer/container_chain.rb
109
109
  - lib/trailblazer/context.rb
110
110
  - lib/trailblazer/option.rb
111
+ - lib/trailblazer/wrap/call_task.rb
112
+ - lib/trailblazer/wrap/inject.rb
113
+ - lib/trailblazer/wrap/runner.rb
114
+ - lib/trailblazer/wrap/trace.rb
115
+ - lib/trailblazer/wrap/variable_mapping.rb
111
116
  - trailblazer-activity.gemspec
112
117
  homepage: http://trailblazer.to/gems/workflow
113
118
  licenses: