trailblazer-activity 0.2.0 → 0.2.1

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