trailblazer-operation 0.0.13 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +5 -3
- data/CHANGES.md +44 -0
- data/Gemfile +13 -2
- data/Rakefile +8 -6
- data/lib/trailblazer/operation.rb +86 -12
- data/lib/trailblazer/operation/deprecated_macro.rb +19 -0
- data/lib/trailblazer/operation/inject.rb +34 -0
- data/lib/trailblazer/operation/inspect.rb +80 -0
- data/lib/trailblazer/operation/public_call.rb +62 -0
- data/lib/trailblazer/operation/railway.rb +32 -0
- data/lib/trailblazer/operation/railway/fast_track.rb +13 -0
- data/lib/trailblazer/operation/railway/normalizer.rb +73 -0
- data/lib/trailblazer/operation/railway/task_builder.rb +44 -0
- data/lib/trailblazer/operation/result.rb +6 -4
- data/lib/trailblazer/operation/skill.rb +8 -24
- data/lib/trailblazer/operation/task_wrap.rb +68 -0
- data/lib/trailblazer/operation/trace.rb +49 -0
- data/lib/trailblazer/operation/variable_mapping.rb +91 -0
- data/lib/trailblazer/operation/version.rb +1 -1
- data/test/call_test.rb +27 -8
- data/test/class_dependencies_test.rb +16 -0
- data/test/docs/doormat_test.rb +189 -0
- data/test/docs/wiring_test.rb +421 -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 +50 -0
- data/test/macro_test.rb +61 -0
- data/test/operation_test.rb +94 -0
- data/test/result_test.rb +14 -8
- data/test/ruby-2.0.0/operation_test.rb +73 -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 +91 -0
- data/test/test_helper.rb +37 -0
- data/test/trace_test.rb +62 -0
- data/test/variable_mapping_test.rb +66 -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 +62 -36
- 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/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,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::Magnetic::Builder::FastTrack::FailFast end
|
6
|
+
def self.pass_fast!; Activity::Magnetic::Builder::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,73 @@
|
|
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
|
+
def self.call(task, options, unknown_options, sequence_options)
|
10
|
+
wrapped_task, options =
|
11
|
+
if task.is_a?(::Hash) # macro.
|
12
|
+
[
|
13
|
+
task[:task],
|
14
|
+
task.merge(options) # Note that the user options are merged over the macro options.
|
15
|
+
]
|
16
|
+
elsif task.is_a?(Array) # TODO remove in 2.2
|
17
|
+
Operation::DeprecatedMacro.( *task )
|
18
|
+
else # user step
|
19
|
+
[
|
20
|
+
TaskBuilder.(task),
|
21
|
+
{ id: task }.merge(options) # default :id
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
options, unknown_options = deprecate_name(options, unknown_options) # TODO remove in 2.2
|
26
|
+
|
27
|
+
raise "No :id given for #{wrapped_task}" unless options[:id]
|
28
|
+
|
29
|
+
options = defaultize(task, options) # :plus_poles
|
30
|
+
|
31
|
+
|
32
|
+
options, locals, sequence_options = override(task, options, sequence_options) # :override
|
33
|
+
|
34
|
+
return wrapped_task, options, unknown_options, sequence_options
|
35
|
+
end
|
36
|
+
|
37
|
+
# Merge user options over defaults.
|
38
|
+
def self.defaultize(task, options)
|
39
|
+
{
|
40
|
+
plus_poles: InitialPlusPoles(),
|
41
|
+
}.merge(options)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Handle the :override option which is specific to Operation.
|
45
|
+
def self.override(task, options, sequence_options)
|
46
|
+
options, locals = Activity::Magnetic::Builder.normalize(options, [:override])
|
47
|
+
sequence_options = sequence_options.merge( replace: options[:id] ) if locals[:override]
|
48
|
+
|
49
|
+
return options, locals, sequence_options
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.InitialPlusPoles
|
53
|
+
Activity::Magnetic::DSL::PlusPoles.new.merge(
|
54
|
+
Activity.Output(Activity::Right, :success) => nil,
|
55
|
+
Activity.Output(Activity::Left, :failure) => nil,
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.deprecate_name(options, unknown_options) # TODO remove in 2.2
|
60
|
+
unknown_options, deprecated_options = Activity::Magnetic::Builder.normalize(unknown_options, [:name])
|
61
|
+
|
62
|
+
options = options.merge( name: deprecated_options[:name] ) if deprecated_options[:name]
|
63
|
+
|
64
|
+
options, locals = Activity::Magnetic::Builder.normalize(options, [:name])
|
65
|
+
if locals[:name]
|
66
|
+
warn "[Trailblazer] The :name option for #step, #success and #failure has been renamed to :id."
|
67
|
+
options = options.merge(id: locals[:name])
|
68
|
+
end
|
69
|
+
return options, unknown_options
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,44 @@
|
|
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
|
+
class Task < Proc
|
9
|
+
def initialize(source_location, &block)
|
10
|
+
@source_location = source_location
|
11
|
+
super &block
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"<Railway::Task{#{@source_location}}>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
to_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# TODO: make this class replaceable so @Mensfeld gets his own call style. :trollface:
|
24
|
+
|
25
|
+
def self.call(step, on_true=Activity::Right, on_false=Activity::Left)
|
26
|
+
Task.new step, &->( (options, *args), **circuit_args ) do
|
27
|
+
# Execute the user step with TRB's kw args.
|
28
|
+
result = Trailblazer::Option::KW(step).(options, **circuit_args) # circuit_args contains :exec_context.
|
29
|
+
|
30
|
+
# Return an appropriate signal which direction to go next.
|
31
|
+
direction = binary_direction_for(result, on_true, on_false)
|
32
|
+
|
33
|
+
[ direction, [ options, *args ], **circuit_args ]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Translates the return value of the user step into a valid signal.
|
38
|
+
# Note that it passes through subclasses of {Signal}.
|
39
|
+
def self.binary_direction_for(result, on_true, on_false)
|
40
|
+
result.is_a?(Class) && result < Activity::Signal ? result : (result ? on_true : on_false)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
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?
|
@@ -1,11 +1,4 @@
|
|
1
|
-
|
2
|
-
# Dependency ("skill") management for Operation.
|
3
|
-
# Op::[]
|
4
|
-
# Op::[]=
|
5
|
-
# Writing, even with an existing name, will never mutate a container.
|
6
|
-
# Op#[]
|
7
|
-
# Op#[]=
|
8
|
-
# Op.(params, { "constructor" => competences })
|
1
|
+
# Dependencies can be defined on the operation. class level
|
9
2
|
class Trailblazer::Operation
|
10
3
|
module Skill
|
11
4
|
# The class-level skill container: Operation::[], ::[]=.
|
@@ -18,24 +11,15 @@ class Trailblazer::Operation
|
|
18
11
|
extend Forwardable
|
19
12
|
def_delegators :skills, :[], :[]=
|
20
13
|
end
|
14
|
+
end
|
21
15
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
alias :_call :call
|
16
|
+
# The use of this module is not encouraged and it is only here for backward-compatibility.
|
17
|
+
# Instead, please pass dependencies via containers, locals, or macros into the respective steps.
|
18
|
+
module ClassDependencies
|
19
|
+
def __call__( (ctx, flow_options), **circuit_options )
|
20
|
+
@skills.each { |name, value| ctx[name] ||= value } # this resembles the behavior in 2.0. we didn't say we liked it.
|
29
21
|
|
30
|
-
|
31
|
-
# two different implementations of ::call.
|
32
|
-
# FIXME: that shouldn't be here in this namespace.
|
33
|
-
module Positional
|
34
|
-
def call(params={}, options={}, *dependencies)
|
35
|
-
super(options.merge("params" => params), *dependencies)
|
36
|
-
end
|
37
|
-
end
|
22
|
+
super
|
38
23
|
end
|
39
|
-
|
40
24
|
end
|
41
25
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
module Operation::Railway
|
3
|
+
module TaskWrap
|
4
|
+
def self.included(includer)
|
5
|
+
includer.extend ClassMethods # ::__call__, ::inititalize_task_wraps!
|
6
|
+
includer.extend DSL
|
7
|
+
|
8
|
+
includer.initialize_task_wraps!
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def initialize_task_wraps!
|
13
|
+
heritage.record :initialize_task_wraps!
|
14
|
+
|
15
|
+
# The map of task_wrap per step/task. Note that it defaults to Wrap.initial_activity.
|
16
|
+
# This gets extended at compile-time for particular tasks as the steps are created via the DSL.
|
17
|
+
self["__static_task_wraps__"] = ::Hash.new(Activity::Wrap.initial_activity)
|
18
|
+
end
|
19
|
+
|
20
|
+
# __call__ prepares `flow_options` and `static_wraps` for {TaskWrap::Runner}.
|
21
|
+
def __call__(args, **circuit_args)
|
22
|
+
args, _circuit_args = TaskWrap.arguments_for_call(self, args, **circuit_args)
|
23
|
+
|
24
|
+
super( args, circuit_args.merge(_circuit_args) ) # Railway::__call__
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.arguments_for_call(operation, (options, flow_options), **circuit_args)
|
29
|
+
wrap_static = operation["__static_task_wraps__"]
|
30
|
+
|
31
|
+
circuit_args = {
|
32
|
+
runner: Activity::Wrap::Runner,
|
33
|
+
# FIXME: this sucks, why do we even need to pass an empty runtime there?
|
34
|
+
wrap_runtime: circuit_args[:wrap_runtime] || ::Hash.new([]), # FIXME:this sucks. (was:) this overwrites wrap_runtime from outside.
|
35
|
+
wrap_static: wrap_static,
|
36
|
+
}
|
37
|
+
|
38
|
+
return [ options, flow_options ], circuit_args
|
39
|
+
end
|
40
|
+
|
41
|
+
module DSL
|
42
|
+
# TODO: this override is hard to follow, we should have a pipeline circuit in DSL to add behavior.
|
43
|
+
# @private
|
44
|
+
def _task(*args)
|
45
|
+
returned = super # TODO: do this with a circuit :)
|
46
|
+
adds, (task, local_options) = returned
|
47
|
+
|
48
|
+
runner_options = local_options[:runner_options]
|
49
|
+
|
50
|
+
runner_options and apply_adds_from_runner_options!( task, runner_options )
|
51
|
+
|
52
|
+
returned
|
53
|
+
end
|
54
|
+
|
55
|
+
# Extend the static wrap for a specific task, at compile time.
|
56
|
+
def apply_adds_from_runner_options!(task, merge:raise, **ignored)
|
57
|
+
static_wrap = self["__static_task_wraps__"][task]
|
58
|
+
|
59
|
+
# macro might want to apply changes to the static task_wrap (e.g. Inject)
|
60
|
+
self["__static_task_wraps__"][task] = Activity::Magnetic::Builder.merge( static_wrap, merge )
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end # TaskWrap
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# |-- Railway::Call "insert.exec_context"
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Operation
|
3
|
+
module Trace
|
4
|
+
def self.call(operation, *args)
|
5
|
+
ctx = PublicCall.send(:options_for_public_call, *args)
|
6
|
+
|
7
|
+
# let Activity::Trace::call handle all parameters, just make sure it calls Operation.__call__
|
8
|
+
call_block = ->(operation, *args) { operation.__call__(*args) }
|
9
|
+
|
10
|
+
stack, direction, options, flow_options = Activity::Trace.(
|
11
|
+
operation,
|
12
|
+
ctx,
|
13
|
+
&call_block # instructs Trace to use __call__.
|
14
|
+
)
|
15
|
+
|
16
|
+
result = Railway::Result(direction, options)
|
17
|
+
|
18
|
+
Result.new(result, stack)
|
19
|
+
end
|
20
|
+
|
21
|
+
# `Operation::trace` is included for simple tracing of the flow.
|
22
|
+
# It simply forwards all arguments to `Trace.call`.
|
23
|
+
#
|
24
|
+
# @public
|
25
|
+
#
|
26
|
+
# Operation.trace(params, "current_user" => current_user).wtf
|
27
|
+
def trace(*args)
|
28
|
+
Trace.(self, *args)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Presentation of the traced stack via the returned result object.
|
32
|
+
# This object is wrapped around the original result in {Trace.call}.
|
33
|
+
class Result < SimpleDelegator
|
34
|
+
def initialize(result, stack)
|
35
|
+
super(result)
|
36
|
+
@stack = stack
|
37
|
+
end
|
38
|
+
|
39
|
+
def wtf
|
40
|
+
Activity::Trace::Present.tree(@stack)
|
41
|
+
end
|
42
|
+
|
43
|
+
def wtf?
|
44
|
+
puts wtf
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,91 @@
|
|
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) # FIXME: THIS SHOULD ALWAYS BE A _NEW_ Context.
|
19
|
+
# TODO: make this unnecessary.
|
20
|
+
# wrap user's hash in Context if it's not one, already (in case user used options.merge).
|
21
|
+
# DISCUSS: should we restrict user to .merge and options.Context?
|
22
|
+
input_ctx = Trailblazer.Context(input_ctx) if !input_ctx.instance_of?(Trailblazer::Context) || input_ctx==original_args[0][0]
|
23
|
+
|
24
|
+
wrap_ctx = wrap_ctx.merge( vm_original_args: original_args )
|
25
|
+
|
26
|
+
# decompose the original_args since we want to modify them.
|
27
|
+
(original_ctx, original_flow_options), original_circuit_options = original_args
|
28
|
+
|
29
|
+
# instead of the original Context, pass on the filtered `input_ctx` in the wrap.
|
30
|
+
return Trailblazer::Activity::Right, [ wrap_ctx, [[input_ctx, original_flow_options], original_circuit_options] ]
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def apply_filter((original_ctx, original_flow_options), original_circuit_options)
|
36
|
+
@filter.( original_ctx, **original_circuit_options )
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# TaskWrap step to compute the outgoing {Context} from the wrapped task.
|
41
|
+
# This allows renaming, filtering, hiding, of the options returned from the wrapped task.
|
42
|
+
class Output
|
43
|
+
def initialize(filter, strategy=CopyMutableToOriginal)
|
44
|
+
@filter = Trailblazer::Option(filter)
|
45
|
+
@strategy = strategy
|
46
|
+
end
|
47
|
+
|
48
|
+
# Runs the user filter and replaces the ctx in `wrap_ctx[:result_args]` with the filtered one.
|
49
|
+
def call( (wrap_ctx, original_args), **circuit_options )
|
50
|
+
(original_ctx, original_flow_options), original_circuit_options = original_args
|
51
|
+
|
52
|
+
returned_ctx, _ = wrap_ctx[:result_args] # this is the Context returned from `call`ing the task.
|
53
|
+
|
54
|
+
# returned_ctx is the Context object from the nested operation. In <=2.1, this might be a completely different one
|
55
|
+
# than "ours" we created in Input. We now need to compile a list of all added values. This is time-intensive and should
|
56
|
+
# be optimized by removing as many Context creations as possible (e.g. the one adding self[] stuff in Operation.__call__).
|
57
|
+
_, mutable_data = returned_ctx.decompose # DISCUSS: this is a weak assumption. What if the task returns a deeply nested Context?
|
58
|
+
|
59
|
+
# let user compute the output.
|
60
|
+
output = apply_filter(mutable_data, original_flow_options, original_circuit_options)
|
61
|
+
|
62
|
+
original_ctx = wrap_ctx[:vm_original_args][0][0]
|
63
|
+
|
64
|
+
new_ctx = @strategy.( original_ctx, output ) # here, we compute the "new" options {Context}.
|
65
|
+
|
66
|
+
wrap_ctx = wrap_ctx.merge( result_args: [new_ctx, original_flow_options] )
|
67
|
+
|
68
|
+
# and then pass on the "new" context.
|
69
|
+
return Trailblazer::Activity::Right, [ wrap_ctx, original_args ]
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# @note API not stable
|
75
|
+
def apply_filter(mutable_data, original_flow_options, original_circuit_options)
|
76
|
+
@filter.(mutable_data, **original_circuit_options)
|
77
|
+
end
|
78
|
+
|
79
|
+
# "merge" Strategy
|
80
|
+
module CopyMutableToOriginal
|
81
|
+
# @param original Context
|
82
|
+
# @param options Context The object returned from a (nested) {Activity}.
|
83
|
+
def self.call(original, mutable)
|
84
|
+
mutable.each { |k,v| original[k] = v }
|
85
|
+
|
86
|
+
original
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end # Wrap
|
91
|
+
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
|
|