trailblazer-operation 0.0.13 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +5 -3
- data/CHANGES.md +100 -0
- data/Gemfile +4 -2
- data/Rakefile +8 -6
- data/lib/trailblazer/operation.rb +80 -13
- data/lib/trailblazer/operation/callable.rb +42 -0
- data/lib/trailblazer/operation/class_dependencies.rb +25 -0
- data/lib/trailblazer/operation/deprecated_macro.rb +19 -0
- data/lib/trailblazer/operation/heritage.rb +30 -0
- data/lib/trailblazer/operation/inject.rb +36 -0
- data/lib/trailblazer/operation/inspect.rb +79 -0
- data/lib/trailblazer/operation/public_call.rb +55 -0
- data/lib/trailblazer/operation/railway.rb +32 -0
- data/lib/trailblazer/operation/railway/fast_track.rb +13 -0
- data/lib/trailblazer/operation/railway/macaroni.rb +23 -0
- data/lib/trailblazer/operation/railway/normalizer.rb +58 -0
- data/lib/trailblazer/operation/railway/task_builder.rb +37 -0
- data/lib/trailblazer/operation/result.rb +6 -4
- data/lib/trailblazer/operation/trace.rb +46 -0
- data/lib/trailblazer/operation/version.rb +1 -1
- data/test/call_test.rb +27 -8
- data/test/callable_test.rb +147 -0
- data/test/class_dependencies_test.rb +16 -0
- data/test/docs/doormat_test.rb +189 -0
- data/test/docs/macaroni_test.rb +33 -0
- data/test/docs/operation_test.rb +23 -0
- data/test/docs/wiring_test.rb +559 -0
- data/test/dry_container_test.rb +4 -0
- data/test/fast_track_test.rb +197 -0
- data/test/gemfiles/Gemfile.ruby-2.0 +1 -2
- data/test/gemfiles/Gemfile.ruby-2.0.lock +40 -0
- data/test/inheritance_test.rb +1 -1
- data/test/inspect_test.rb +43 -0
- data/test/introspect_test.rb +51 -0
- data/test/macro_test.rb +60 -0
- data/test/operation_test.rb +94 -0
- data/test/result_test.rb +14 -8
- data/test/ruby-2.0.0/operation_test.rb +61 -0
- data/test/ruby-2.0.0/step_test.rb +136 -0
- data/test/skill_test.rb +66 -48
- data/test/step_test.rb +228 -0
- data/test/task_wrap_test.rb +97 -0
- data/test/test_helper.rb +37 -0
- data/test/trace_test.rb +57 -0
- data/test/wire_test.rb +113 -0
- data/test/wiring/defaults_test.rb +197 -0
- data/test/wiring/subprocess_test.rb +70 -0
- data/trailblazer-operation.gemspec +3 -5
- metadata +68 -37
- data/lib/trailblazer/operation/1.9.3/option.rb +0 -36
- data/lib/trailblazer/operation/generic.rb +0 -12
- data/lib/trailblazer/operation/option.rb +0 -54
- data/lib/trailblazer/operation/pipetree.rb +0 -142
- data/lib/trailblazer/operation/skill.rb +0 -41
- data/lib/trailblazer/skill.rb +0 -70
- data/test/2.0.0-pipetree_test.rb +0 -100
- data/test/2.1.0-pipetree_test.rb +0 -100
- data/test/operation_skill_test.rb +0 -89
- data/test/pipetree_test.rb +0 -185
@@ -0,0 +1,55 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
module Operation::PublicCall
|
3
|
+
# This is the outer-most public `call` method that gets invoked when calling `Create.()`.
|
4
|
+
# The signature of this is `params, options, *containers`. This was a mistake, as the
|
5
|
+
# first argument could've been part of `options` hash in the first place.
|
6
|
+
#
|
7
|
+
# Create.(params, runtime_data, *containers)
|
8
|
+
# #=> Result<Context...>
|
9
|
+
#
|
10
|
+
# In workflows/Nested compositions, this method is not used anymore and it might probably
|
11
|
+
# get removed in future versions of TRB. Currently, we use Operation::__call__ as an alternative.
|
12
|
+
#
|
13
|
+
#
|
14
|
+
# @note Do not override this method as it will be removed in future versions. Also, you will break tracing.
|
15
|
+
# @return Operation::Railway::Result binary result object
|
16
|
+
def call(*args)
|
17
|
+
return call_with_circuit_interface(*args) if args.any? && args[0].is_a?(Array) # This is kind of a hack that could be well hidden if Ruby had method overloading. Goal is to simplify the call/__call__ thing as we're fading out Operation::call anyway.
|
18
|
+
call_with_public_interface(*args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def call_with_public_interface(*args)
|
22
|
+
ctx = Operation::PublicCall.options_for_public_call(*args)
|
23
|
+
|
24
|
+
# call the activity.
|
25
|
+
# This will result in invoking {::call_with_circuit_interface}.
|
26
|
+
last_signal, (options, flow_options) = Activity::TaskWrap.invoke(self, [ctx, {}], {})
|
27
|
+
|
28
|
+
# Result is successful if the activity ended with an End event derived from Railway::End::Success.
|
29
|
+
Operation::Railway::Result(last_signal, options, flow_options)
|
30
|
+
end
|
31
|
+
|
32
|
+
# This interface is used for all nested OPs (and the outer-most, too).
|
33
|
+
def call_with_circuit_interface(args, circuit_options)
|
34
|
+
@activity.(
|
35
|
+
args,
|
36
|
+
circuit_options.merge(
|
37
|
+
exec_context: new
|
38
|
+
)
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Compile a Context object to be passed into the Activity::call.
|
43
|
+
# @private
|
44
|
+
def self.options_for_public_call(options={}, *containers)
|
45
|
+
# generate the skill hash that embraces runtime options plus potential containers, the so called Runtime options.
|
46
|
+
# This wrapping is supposed to happen once in the entire system.
|
47
|
+
|
48
|
+
hash_transformer = ->(containers) { containers[0].to_hash } # FIXME: don't transform any containers into kw args.
|
49
|
+
|
50
|
+
immutable_options = Trailblazer::Context::ContainerChain.new( [options, *containers], to_hash: hash_transformer ) # Runtime options, immutable.
|
51
|
+
|
52
|
+
ctx = Trailblazer::Context(immutable_options)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
# Operations is simply a thin API to define, inherit and run circuits by passing the options object.
|
3
|
+
# It encourages the linear railway style (http://trb.to/gems/workflow/circuit.html#operation) but can
|
4
|
+
# easily be extend for more complex workflows.
|
5
|
+
class Operation
|
6
|
+
# End event: All subclasses of End:::Success are interpreted as "success".
|
7
|
+
module Railway
|
8
|
+
# @param options Context
|
9
|
+
# @param end_event The last emitted signal in a circuit is usually the end event.
|
10
|
+
def self.Result(end_event, options, *)
|
11
|
+
Result.new(end_event.kind_of?(End::Success), options, end_event)
|
12
|
+
end
|
13
|
+
|
14
|
+
# The Railway::Result knows about its binary state, the context (data), and the last event in the circuit.
|
15
|
+
class Result < Result # Operation::Result
|
16
|
+
def initialize(success, data, event)
|
17
|
+
super(success, data)
|
18
|
+
|
19
|
+
@event = event
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :event
|
23
|
+
end
|
24
|
+
|
25
|
+
module End
|
26
|
+
class Success < Activity::End; end
|
27
|
+
class Failure < Activity::End; end
|
28
|
+
end
|
29
|
+
|
30
|
+
end # Railway
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
module Operation::Railway
|
3
|
+
def self.fail! ; Activity::Left end
|
4
|
+
def self.pass! ; Activity::Right end
|
5
|
+
def self.fail_fast!; Activity::FastTrack::FailFast end
|
6
|
+
def self.pass_fast!; Activity::FastTrack::PassFast end
|
7
|
+
|
8
|
+
module End
|
9
|
+
FailFast = Class.new(Operation::Railway::End::Failure)
|
10
|
+
PassFast = Class.new(Operation::Railway::End::Success)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
module Operation::Railway
|
3
|
+
# Call the user's steps with a differing API (inspired by Maciej Mensfeld) that
|
4
|
+
# only receives keyword args. The `options` keyword is the stateful context object
|
5
|
+
#
|
6
|
+
# def my_step( params:, ** )
|
7
|
+
# def my_step( params:, options:, ** )
|
8
|
+
module Macaroni
|
9
|
+
def self.call(user_proc)
|
10
|
+
Activity::TaskBuilder::Task.new( Trailblazer::Option.build( Macaroni::Option, user_proc ), user_proc )
|
11
|
+
end
|
12
|
+
|
13
|
+
class Option < Trailblazer::Option
|
14
|
+
# The Option#call! method prepares the arguments.
|
15
|
+
def self.call!(proc, options, *)
|
16
|
+
proc.( **options.to_hash.merge( options: options ) )
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
KwSignature = Macaroni
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
module Operation::Railway
|
3
|
+
# The {Normalizer} is called for every DSL call (step/pass/fail etc.) and normalizes/defaults
|
4
|
+
# the user options, such as setting `:id`, connecting the task's outputs or wrapping the user's
|
5
|
+
# task via {TaskBuilder} in order to translate true/false to `Right` or `Left`.
|
6
|
+
#
|
7
|
+
# The Normalizer sits in the `@builder`, which receives all DSL calls from the Operation subclass.
|
8
|
+
module Normalizer
|
9
|
+
Pipeline = Activity::Magnetic::Normalizer::Pipeline.clone
|
10
|
+
|
11
|
+
Pipeline.module_eval do
|
12
|
+
# Handle the :override option which is specific to Operation.
|
13
|
+
def self.override(ctx, task:, options:, sequence_options:, **)
|
14
|
+
options, locals = Activity::Magnetic::Options.normalize(options, [:override])
|
15
|
+
sequence_options = sequence_options.merge( replace: options[:id] ) if locals[:override]
|
16
|
+
|
17
|
+
ctx[:options], ctx[:sequence_options] = options, sequence_options
|
18
|
+
end
|
19
|
+
|
20
|
+
# TODO remove in 2.2
|
21
|
+
def self.deprecate_macro_with_two_args(ctx, task:, **)
|
22
|
+
return true unless task.is_a?(Array) # TODO remove in 2.2
|
23
|
+
|
24
|
+
ctx[:options] = Operation::DeprecatedMacro.( *task )
|
25
|
+
end
|
26
|
+
|
27
|
+
# TODO remove in 2.2
|
28
|
+
def self.deprecate_name(ctx, local_options:, connection_options:, **)
|
29
|
+
connection_options, deprecated_options = Activity::Magnetic::Options.normalize(connection_options, [:name])
|
30
|
+
local_options, _deprecated_options = Activity::Magnetic::Options.normalize(local_options, [:name])
|
31
|
+
|
32
|
+
deprecated_options = deprecated_options.merge(_deprecated_options)
|
33
|
+
|
34
|
+
local_options = local_options.merge( name: deprecated_options[:name] ) if deprecated_options[:name]
|
35
|
+
|
36
|
+
local_options, locals = Activity::Magnetic::Options.normalize(local_options, [:name])
|
37
|
+
if locals[:name]
|
38
|
+
warn "[Trailblazer] The :name option for #step, #success and #failure has been renamed to :id."
|
39
|
+
local_options = local_options.merge(id: locals[:name])
|
40
|
+
end
|
41
|
+
|
42
|
+
ctx[:local_options], ctx[:connection_options] = local_options, connection_options
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.raise_on_missing_id(ctx, local_options:, **)
|
46
|
+
raise "No :id given for #{local_options[:task]}" unless local_options[:id]
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
# add more normalization tasks to the existing Magnetic::Normalizer::Pipeline
|
51
|
+
task Activity::TaskBuilder::Binary( method(:deprecate_macro_with_two_args) ), before: "split_options"
|
52
|
+
task Activity::TaskBuilder::Binary( method(:deprecate_name) )
|
53
|
+
task Activity::TaskBuilder::Binary( method(:override) )
|
54
|
+
task Activity::TaskBuilder::Binary( method(:raise_on_missing_id) )
|
55
|
+
end
|
56
|
+
end # Normalizer
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
module Operation::Railway
|
3
|
+
# every step is wrapped by this proc/decider. this is executed in the circuit as the actual task.
|
4
|
+
# Step calls step.(options, **options, flow_options)
|
5
|
+
# Output direction binary: true=>Right, false=>Left.
|
6
|
+
# Passes through all subclasses of Direction.~~~~~~~~~~~~~~~~~
|
7
|
+
module TaskBuilder
|
8
|
+
def self.call(user_proc)
|
9
|
+
Task.new( Trailblazer::Option::KW( user_proc ), user_proc )
|
10
|
+
end
|
11
|
+
|
12
|
+
# Translates the return value of the user step into a valid signal.
|
13
|
+
# Note that it passes through subclasses of {Signal}.
|
14
|
+
def self.binary_direction_for(result, on_true, on_false)
|
15
|
+
result.is_a?(Class) && result < Activity::Signal ? result : (result ? on_true : on_false)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Task
|
20
|
+
def initialize(task, user_proc)
|
21
|
+
@task = task
|
22
|
+
@user_proc = user_proc
|
23
|
+
freeze
|
24
|
+
end
|
25
|
+
|
26
|
+
def call( (options, *args), **circuit_args )
|
27
|
+
# Execute the user step with TRB's kw args.
|
28
|
+
result = @task.( options, **circuit_args ) # circuit_args contains :exec_context.
|
29
|
+
|
30
|
+
# Return an appropriate signal which direction to go next.
|
31
|
+
direction = TaskBuilder.binary_direction_for( result, Activity::Right, Activity::Left )
|
32
|
+
|
33
|
+
[ direction, [ options, *args ], **circuit_args ]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,12 +1,11 @@
|
|
1
1
|
class Trailblazer::Operation
|
2
2
|
class Result
|
3
|
+
# @param success Boolean validity of the result object
|
4
|
+
# @param data Context
|
3
5
|
def initialize(success, data)
|
4
|
-
@success, @data = success, data
|
6
|
+
@success, @data = success, data
|
5
7
|
end
|
6
8
|
|
7
|
-
extend Forwardable
|
8
|
-
def_delegators :@data, :[] # DISCUSS: make it a real delegator? see Nested.
|
9
|
-
|
10
9
|
def success?
|
11
10
|
@success
|
12
11
|
end
|
@@ -15,6 +14,9 @@ class Trailblazer::Operation
|
|
15
14
|
! success?
|
16
15
|
end
|
17
16
|
|
17
|
+
extend Forwardable
|
18
|
+
def_delegators :@data, :[] # DISCUSS: make it a real delegator? see Nested.
|
19
|
+
|
18
20
|
# DISCUSS: the two methods below are more for testing.
|
19
21
|
def inspect(*slices)
|
20
22
|
return "<Result:#{success?} #{slice(*slices).inspect} >" if slices.any?
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Operation
|
3
|
+
module Trace
|
4
|
+
# @note The problem in this method is, we have redundancy with Operation::PublicCall
|
5
|
+
def self.call(operation, *args)
|
6
|
+
ctx = PublicCall.options_for_public_call(*args) # redundant with PublicCall::call.
|
7
|
+
|
8
|
+
# Prepare the tracing-specific arguments. This is only run once for the entire circuit!
|
9
|
+
operation, *args = Trailblazer::Activity::Trace.arguments_for_call( operation, [ctx, {}], {} )
|
10
|
+
|
11
|
+
last_signal, (ctx, flow_options) = Activity::TaskWrap.invoke(operation, *args )
|
12
|
+
|
13
|
+
result = Railway::Result(last_signal, ctx) # redundant with PublicCall::call.
|
14
|
+
|
15
|
+
Result.new(result, flow_options[:stack].to_a)
|
16
|
+
end
|
17
|
+
|
18
|
+
# `Operation::trace` is included for simple tracing of the flow.
|
19
|
+
# It simply forwards all arguments to `Trace.call`.
|
20
|
+
#
|
21
|
+
# @public
|
22
|
+
#
|
23
|
+
# Operation.trace(params, "current_user" => current_user).wtf
|
24
|
+
def trace(*args)
|
25
|
+
Trace.(self, *args)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Presentation of the traced stack via the returned result object.
|
29
|
+
# This object is wrapped around the original result in {Trace.call}.
|
30
|
+
class Result < SimpleDelegator
|
31
|
+
def initialize(result, stack)
|
32
|
+
super(result)
|
33
|
+
@stack = stack
|
34
|
+
end
|
35
|
+
|
36
|
+
def wtf
|
37
|
+
Trailblazer::Activity::Trace::Present.(@stack)
|
38
|
+
end
|
39
|
+
|
40
|
+
def wtf?
|
41
|
+
puts wtf
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/test/call_test.rb
CHANGED
@@ -1,28 +1,47 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
|
-
# DISCUSS: do we need this test?
|
4
3
|
class CallTest < Minitest::Spec
|
5
4
|
describe "::call" do
|
6
5
|
class Create < Trailblazer::Operation
|
6
|
+
step ->(*) { true }
|
7
7
|
def inspect
|
8
8
|
"#{@skills.inspect}"
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
it { Create.().must_be_instance_of Trailblazer::Operation::Result }
|
12
|
+
it { Create.().must_be_instance_of Trailblazer::Operation::Railway::Result }
|
13
13
|
|
14
|
-
it { Create.({}).inspect.must_equal %{<Result:true <Skill {} {\"params\"=>{}} {\"pipetree\"=>[>operation.new]}> >} }
|
15
|
-
it { Create.(name: "Jacob").inspect.must_equal %{<Result:true <Skill {} {\"params\"=>{:name=>\"Jacob\"}} {\"pipetree\"=>[>operation.new]}> >} }
|
16
|
-
it { Create.({ name: "Jacob" }, { policy: Object }).inspect.must_equal %{<Result:true <Skill {} {:policy=>Object, \"params\"=>{:name=>\"Jacob\"}} {\"pipetree\"=>[>operation.new]}> >} }
|
14
|
+
# it { Create.({}).inspect.must_equal %{<Result:true <Skill {} {\"params\"=>{}} {\"pipetree\"=>[>operation.new]}> >} }
|
15
|
+
# it { Create.(name: "Jacob").inspect.must_equal %{<Result:true <Skill {} {\"params\"=>{:name=>\"Jacob\"}} {\"pipetree\"=>[>operation.new]}> >} }
|
16
|
+
# it { Create.({ name: "Jacob" }, { policy: Object }).inspect.must_equal %{<Result:true <Skill {} {:policy=>Object, \"params\"=>{:name=>\"Jacob\"}} {\"pipetree\"=>[>operation.new]}> >} }
|
17
17
|
|
18
18
|
#---
|
19
19
|
# success?
|
20
20
|
class Update < Trailblazer::Operation
|
21
|
-
step ->(options) { options[
|
21
|
+
step ->(options, **) { options[:result] }
|
22
|
+
end
|
23
|
+
|
24
|
+
# operation success
|
25
|
+
it do
|
26
|
+
result = Update.(result: true)
|
27
|
+
|
28
|
+
result.success?.must_equal true
|
29
|
+
|
30
|
+
result.event.must_be_instance_of Trailblazer::Operation::Railway::End::Success
|
31
|
+
result.event.must_equal Update.outputs[:success].signal
|
32
|
+
end
|
33
|
+
|
34
|
+
# operation failure
|
35
|
+
it do
|
36
|
+
result = Update.(result: false)
|
37
|
+
|
38
|
+
result.success?.must_equal false
|
39
|
+
result.failure?.must_equal true
|
40
|
+
|
41
|
+
result.event.must_be_instance_of Trailblazer::Operation::Railway::End::Failure
|
42
|
+
result.event.must_equal Update.outputs[:failure].signal
|
22
43
|
end
|
23
44
|
|
24
|
-
it { Update.(true).success?.must_equal true }
|
25
|
-
it { Update.(false).success?.must_equal false }
|
26
45
|
end
|
27
46
|
end
|
28
47
|
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class CallableHelper < Minitest::Spec
|
4
|
+
Operation = Trailblazer::Operation
|
5
|
+
Activity = Trailblazer::Activity
|
6
|
+
|
7
|
+
module Blog
|
8
|
+
Read = ->((options, *args), *) { options["Read"] = 1; [ Activity::Right, [options, *args] ] }
|
9
|
+
Next = ->((options, *args), *) { options["NextPage"] = []; [ options["return"], [options, *args] ] }
|
10
|
+
Comment = ->((options, *args), *) { options["Comment"] = 2; [ Activity::Right, [options, *args] ] }
|
11
|
+
end
|
12
|
+
|
13
|
+
module User
|
14
|
+
Relax = ->((options, *args), *) { options["Relax"]=true; [ Activity::Right, [options, *args] ] }
|
15
|
+
end
|
16
|
+
|
17
|
+
### Callable( )
|
18
|
+
###
|
19
|
+
describe "circuit with 1 level of nesting" do # TODO: test this kind of configuration in dsl_tests somewhere.
|
20
|
+
let(:blog) do
|
21
|
+
Module.new do
|
22
|
+
extend Activity::Path()
|
23
|
+
|
24
|
+
task task: Blog::Read
|
25
|
+
task task: Blog::Next, Output(Activity::Right, :done) => "End.success", Output(Activity::Left, :success) => Track(:success)
|
26
|
+
task task: Blog::Comment
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:user) do
|
31
|
+
_blog = blog
|
32
|
+
|
33
|
+
Module.new do
|
34
|
+
extend Activity::Path()
|
35
|
+
|
36
|
+
task task: _blog, _blog.outputs[:success] => Track(:success)
|
37
|
+
task task: User::Relax
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "ends before comment, on next_page" do
|
42
|
+
user.( [options = { "return" => Activity::Right }] ).must_equal(
|
43
|
+
[user.outputs[:success].signal, [{"return"=>Trailblazer::Activity::Right, "Read"=>1, "NextPage"=>[], "Relax"=>true}]]
|
44
|
+
)
|
45
|
+
|
46
|
+
options.must_equal({"return"=>Trailblazer::Activity::Right, "Read"=>1, "NextPage"=>[], "Relax"=>true})
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
### Callable( End1, End2 )
|
51
|
+
###
|
52
|
+
describe "circuit with 2 end events in the nested process" do
|
53
|
+
let(:blog) do
|
54
|
+
Module.new do
|
55
|
+
extend Activity::Path()
|
56
|
+
|
57
|
+
task task: Blog::Read
|
58
|
+
task task: Blog::Next, Output(Activity::Right, :success___) => :__success, Output(Activity::Left, :retry___) => _retry=End(:retry)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
let(:user) do
|
63
|
+
_blog = blog
|
64
|
+
|
65
|
+
Module.new do
|
66
|
+
extend Activity::Path()
|
67
|
+
|
68
|
+
task task: _blog, _blog.outputs[:success] => Track(:success), _blog.outputs[:retry] => "End.success"
|
69
|
+
task task: User::Relax
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it "runs from Callable->default to Relax" do
|
74
|
+
user.( [ options = { "return" => Activity::Right } ] ).must_equal [
|
75
|
+
user.outputs[:success].signal,
|
76
|
+
[ {"return"=>Activity::Right, "Read"=>1, "NextPage"=>[], "Relax"=>true} ]
|
77
|
+
]
|
78
|
+
|
79
|
+
options.must_equal({"return"=>Activity::Right, "Read"=>1, "NextPage"=>[], "Relax"=>true})
|
80
|
+
end
|
81
|
+
|
82
|
+
it "runs from other Callable end" do
|
83
|
+
user.( [ options = { "return" => Activity::Left } ] ).must_equal [
|
84
|
+
user.outputs[:success].signal,
|
85
|
+
[ {"return"=>Activity::Left, "Read"=>1, "NextPage"=>[]} ]
|
86
|
+
]
|
87
|
+
|
88
|
+
options.must_equal({"return"=>Activity::Left, "Read"=>1, "NextPage"=>[]})
|
89
|
+
end
|
90
|
+
|
91
|
+
#---
|
92
|
+
#- Callable( activity, start_at )
|
93
|
+
let(:with_nested_and_start_at) do
|
94
|
+
_blog = blog
|
95
|
+
|
96
|
+
Module.new do
|
97
|
+
extend Activity::Path()
|
98
|
+
|
99
|
+
task task: Operation::Callable( _blog, start_task: Blog::Next ), _blog.outputs[:success] => Track(:success)
|
100
|
+
task task: User::Relax
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it "runs Callable from alternative start" do
|
105
|
+
with_nested_and_start_at.( [options = { "return" => Activity::Right }] ).
|
106
|
+
must_equal [
|
107
|
+
with_nested_and_start_at.outputs[:success].signal,
|
108
|
+
[ {"return"=>Activity::Right, "NextPage"=>[], "Relax"=>true} ]
|
109
|
+
]
|
110
|
+
|
111
|
+
options.must_equal({"return"=>Activity::Right, "NextPage"=>[], "Relax"=>true})
|
112
|
+
end
|
113
|
+
|
114
|
+
#---
|
115
|
+
#- Callable( activity, call: :__call__ ) { ... }
|
116
|
+
describe "Callable with :call option" do
|
117
|
+
let(:process) do
|
118
|
+
class Workout
|
119
|
+
def self.__call__((options, *args), *)
|
120
|
+
options[:workout] = 9
|
121
|
+
|
122
|
+
return Activity::Right, [options, *args]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
subprocess = Operation::Callable( Workout, call: :__call__ )
|
127
|
+
|
128
|
+
Module.new do
|
129
|
+
extend Activity::Path()
|
130
|
+
|
131
|
+
task task: subprocess
|
132
|
+
task task: User::Relax
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
it "runs Callable process with __call__" do
|
137
|
+
process.( [options = { "return" => Activity::Right }] ).
|
138
|
+
must_equal [
|
139
|
+
process.outputs[:success].signal,
|
140
|
+
[{"return"=>Activity::Right, :workout=>9, "Relax"=>true}]
|
141
|
+
]
|
142
|
+
|
143
|
+
options.must_equal({"return"=>Activity::Right, :workout=>9, "Relax"=>true})
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|