trailblazer-operation 0.0.13 → 0.4.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 +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
|