trailblazer 2.0.7 → 2.1.0.beta1
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 +4 -4
- data/CHANGES.md +35 -1
- data/Gemfile +6 -12
- data/README.md +3 -1
- data/Rakefile +6 -17
- data/lib/trailblazer.rb +7 -4
- data/lib/trailblazer/deprecation/call.rb +46 -0
- data/lib/trailblazer/deprecation/context.rb +43 -0
- data/lib/trailblazer/operation/contract.rb +40 -9
- data/lib/trailblazer/operation/deprecations.rb +21 -0
- data/lib/trailblazer/operation/guard.rb +5 -5
- data/lib/trailblazer/operation/model.rb +15 -10
- data/lib/trailblazer/operation/nested.rb +56 -85
- data/lib/trailblazer/operation/persist.rb +4 -2
- data/lib/trailblazer/operation/policy.rb +16 -7
- data/lib/trailblazer/operation/pundit.rb +3 -3
- data/lib/trailblazer/operation/representer.rb +5 -0
- data/lib/trailblazer/operation/rescue.rb +12 -9
- data/lib/trailblazer/operation/validate.rb +36 -29
- data/lib/trailblazer/operation/wrap.rb +49 -11
- data/lib/trailblazer/task.rb +20 -0
- data/lib/trailblazer/version.rb +1 -1
- data/test/benchmark.rb +63 -0
- data/test/deprecation/call_test.rb +42 -0
- data/test/deprecation/context_test.rb +19 -0
- data/test/docs/contract_test.rb +73 -53
- data/test/docs/dry_test.rb +2 -2
- data/test/docs/fast_test.rb +133 -13
- data/test/docs/guard_test.rb +28 -35
- data/test/docs/macro_test.rb +1 -1
- data/test/docs/model_test.rb +13 -13
- data/test/docs/nested_test.rb +54 -122
- data/test/docs/operation_test.rb +42 -43
- data/test/docs/pundit_test.rb +16 -16
- data/test/docs/representer_test.rb +18 -18
- data/test/docs/rescue_test.rb +29 -29
- data/test/docs/trace_test.rb +82 -0
- data/test/docs/wrap_test.rb +59 -26
- data/test/module_test.rb +75 -75
- data/test/nested_test.rb +293 -0
- data/test/operation/contract_test.rb +23 -153
- data/test/operation/dsl/contract_test.rb +9 -9
- data/test/operation/dsl/representer_test.rb +169 -169
- data/test/operation/model_test.rb +15 -21
- data/test/operation/persist_test.rb +18 -11
- data/test/operation/pundit_test.rb +25 -23
- data/test/operation/representer_test.rb +254 -254
- data/test/test_helper.rb +5 -2
- data/test/variables_test.rb +158 -0
- data/trailblazer.gemspec +1 -1
- data/untitled +33 -0
- metadata +25 -27
- data/lib/trailblazer/operation/callback.rb +0 -35
- data/lib/trailblazer/operation/procedural/contract.rb +0 -15
- data/lib/trailblazer/operation/procedural/validate.rb +0 -22
- data/test/operation/callback_test.rb +0 -70
- data/test/operation/dsl/callback_test.rb +0 -106
- data/test/operation/params_test.rb +0 -36
- data/test/operation/pipedream_test.rb +0 -59
- data/test/operation/pipetree_test.rb +0 -104
- data/test/operation/present_test.rb +0 -24
- data/test/operation/resolver_test.rb +0 -47
- data/test/operation_test.rb +0 -143
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c82134611daa3efe9309eebb4a283585b4095428
|
4
|
+
data.tar.gz: cc121cdb03c9ea4da12b9b2713150e50d18ebac9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 992b2af33d7b5de098b59c2eb9a75b6dd4854d0a580613b6938c6ae44f5ec2d048f4686cfecb23c4daf5673c78b24aa90286b50e5df24bc856134155f7e9d7f4
|
7
|
+
data.tar.gz: 96912060658256a931dec0a8951c61fb3bc1eb62521a4730a2458258f2e1e7c145adad129820c19149d578213c45c81673587f1f1afe16bec9c1a189ad32c914
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,38 @@
|
|
1
|
+
# 2.1
|
2
|
+
|
3
|
+
* Macros now always have to provide an `:id`. This was a bit fuzzy in 2.0.
|
4
|
+
|
5
|
+
* Nested
|
6
|
+
if Nested( Edit ), outputs will automatically be connected, see editor.
|
7
|
+
* Wrap
|
8
|
+
dropped the `pipe` option. This is now `options, flow_options, *`
|
9
|
+
`false` is now automatically connected to End.failure.
|
10
|
+
|
11
|
+
* `operation.new` step removed.
|
12
|
+
* Undocumented step behavior removed. You can't write to `self` anymore.
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
step :process
|
16
|
+
def process(*)
|
17
|
+
self["x"] = true
|
18
|
+
end
|
19
|
+
```
|
20
|
+
|
21
|
+
Always write to `options`.
|
22
|
+
|
23
|
+
* self[] removed
|
24
|
+
* Fixed `Guard` where procs could receive one argument, only. Guards follow the step interface: `Policy::Guard( ->(options, **) { .. } )
|
25
|
+
* Removed `Operation::Callback` which was a poor idea and luckily no one was using it.
|
26
|
+
|
27
|
+
TODO:
|
28
|
+
document Task API and define step API
|
29
|
+
deprecate step->(options) ?
|
30
|
+
injectable, per-operation step arguments strategy?
|
31
|
+
|
32
|
+
# 2.1.0.beta1
|
33
|
+
|
34
|
+
* Add `deprecation/call` and `deprecation/context` that help with the new `call` API and symbols for `options` keys.
|
35
|
+
|
1
36
|
# 2.0.7
|
2
37
|
|
3
38
|
* Allow to use any method with the Model macro, e.g.
|
@@ -8,7 +43,6 @@
|
|
8
43
|
|
9
44
|
will now invoke `Comment[ params[:id] ]`, which makes using Sequel a breeze.
|
10
45
|
|
11
|
-
|
12
46
|
# 2.0.6
|
13
47
|
|
14
48
|
* Fix what we broke in 2.0.5, where `Wrap` would always use the current operation subclass and not the empty `Trailblazer::Operation`. Thanks to @mensfeld.
|
data/Gemfile
CHANGED
@@ -3,29 +3,23 @@ source 'https://rubygems.org'
|
|
3
3
|
# Specify your gem's dependencies in trailblazer.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
# gem "representable", path: "../representable"
|
7
|
-
# gem "disposable", path: "../disposable"
|
8
|
-
# gem "reform", github: "apotonick/reform"
|
9
6
|
gem "reform-rails"
|
10
|
-
gem "activesupport"
|
7
|
+
gem "activesupport"#, "~> 4.2.0"
|
11
8
|
|
12
|
-
gem "roar", github: "apotonick/roar"
|
13
9
|
# gem "reform", "~> 2.0.0"
|
14
10
|
gem "reform"#, path: "../reform"
|
15
|
-
# gem "roar", path: "../roar"
|
16
11
|
gem "multi_json"
|
17
12
|
|
18
13
|
gem "dry-auto_inject"
|
19
14
|
gem "dry-matcher"
|
20
15
|
gem "dry-validation"
|
21
16
|
|
22
|
-
|
23
|
-
# gem "trailblazer-operation", path: "../operation"
|
24
|
-
# gem "pipetree", path: "../pipetree"
|
17
|
+
gem "trailblazer-operation", path: "../operation"
|
25
18
|
# gem "trailblazer-operation", github: "trailblazer/trailblazer-operation"
|
26
|
-
# gem "pipetree", github: "apotonick/pipetree"
|
27
|
-
|
28
19
|
|
29
20
|
gem "minitest-line"
|
30
21
|
|
31
|
-
|
22
|
+
gem "trailblazer-activity", path: "../trailblazer-circuit"
|
23
|
+
# gem "trailblazer-activity", github: "trailblazer/trailblazer-activity"
|
24
|
+
|
25
|
+
# gem "trailblazer-context", path: "../trailblazer-context"
|
data/README.md
CHANGED
@@ -6,7 +6,9 @@ _Trailblazer provides new high-level abstractions for Ruby frameworks. It gently
|
|
6
6
|
[](http://trailblazer.to/newsletter/)
|
7
7
|
[](http://badge.fury.io/rb/trailblazer)
|
8
8
|
|
9
|
-
**This document discusses Trailblazer 2.
|
9
|
+
**This document discusses Trailblazer 2.1.** An overview about the additions are [on our website](http://trailblazer.to/blog/2017-12-trailblazer-2-1-what-you-need-to-know.html).
|
10
|
+
|
11
|
+
The [1.x documentation is here](http://trailblazer.to/gems/operation/1.1/).
|
10
12
|
|
11
13
|
## Trailblazer In A Nutshell
|
12
14
|
|
data/Rakefile
CHANGED
@@ -5,23 +5,12 @@ task :default => [:test]
|
|
5
5
|
|
6
6
|
Rake::TestTask.new(:test) do |test|
|
7
7
|
test.libs << 'test'
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
test/operation/model_test.rb
|
12
|
-
test/operation/contract_test.rb
|
13
|
-
test/operation/persist_test.rb
|
14
|
-
test/operation/callback_test.rb
|
15
|
-
test/operation/resolver_test.rb
|
16
|
-
test/operation/dsl/contract_test.rb
|
17
|
-
|
18
|
-
test/docs/*_test.rb
|
19
|
-
}]
|
20
|
-
|
21
|
-
if RUBY_VERSION == "1.9.3"
|
22
|
-
test_files = test_files - %w{test/docs/dry_test.rb test/docs/auto_inject_test.rb}
|
23
|
-
end
|
8
|
+
test.test_files = FileList['test/**/*_test.rb'] - FileList["test/deprecation/*_test.rb"]
|
9
|
+
test.verbose = true
|
10
|
+
end
|
24
11
|
|
25
|
-
|
12
|
+
Rake::TestTask.new(:testdep) do |test|
|
13
|
+
test.libs << 'test'
|
14
|
+
test.test_files = FileList["test/deprecation/*_test.rb"]
|
26
15
|
test.verbose = true
|
27
16
|
end
|
data/lib/trailblazer.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
-
require "trailblazer/
|
2
|
-
require "trailblazer/operation/pipetree"
|
1
|
+
require "trailblazer/version"
|
3
2
|
|
3
|
+
require "trailblazer/operation"
|
4
4
|
require "trailblazer/dsl"
|
5
|
-
|
5
|
+
|
6
|
+
require "trailblazer/task"
|
7
|
+
|
8
|
+
require "trailblazer/operation/deprecations"
|
6
9
|
|
7
10
|
require "trailblazer/operation/model"
|
8
11
|
require "trailblazer/operation/contract"
|
@@ -12,7 +15,7 @@ require "trailblazer/operation/policy"
|
|
12
15
|
require "trailblazer/operation/pundit"
|
13
16
|
require "trailblazer/operation/guard"
|
14
17
|
require "trailblazer/operation/persist"
|
15
|
-
# require "trailblazer/operation/callback"
|
16
18
|
require "trailblazer/operation/nested"
|
17
19
|
require "trailblazer/operation/wrap"
|
18
20
|
require "trailblazer/operation/rescue"
|
21
|
+
require "trailblazer/operation/inject"
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
module Deprecation
|
3
|
+
module Operation
|
4
|
+
# This is super hacky. Fix you call calls everywhere (shouldn't take too long) and never load this file, again.
|
5
|
+
module Call
|
6
|
+
def self.options_for_call(params, *containers)
|
7
|
+
if containers == []
|
8
|
+
if params.is_a?(Hash) # this means we assume everything is cool. Create.( {...} )
|
9
|
+
|
10
|
+
else # this means someone did Create.( #<WeirdParamsObject> )
|
11
|
+
deprecate_positional_params(params, *containers)
|
12
|
+
return { params: params }, *containers
|
13
|
+
end
|
14
|
+
else # Create.( params, "current_user" => ... )
|
15
|
+
options, containers = containers[0], (containers[1..-1] || [])
|
16
|
+
if options.is_a?(Hash) # old API
|
17
|
+
warn "[Trailblazer] Please don't pass the `params` object as a positional argument into `Operation.()`, use the `:params` key and one hash for all: `Operation.( params: my_params, current_user: ... )` ."
|
18
|
+
return options.merge( params: params ), *containers
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
return params, *containers
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.deprecate_positional_params(params, *containers)
|
26
|
+
warn "[Trailblazer] Please don't pass the `params` object as a positional argument into `Operation.()`, use the `:params` key: `Operation.( params: my_params )` ."
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Trailblazer::Operation.module_eval do
|
34
|
+
# this sucks:
|
35
|
+
def self.call(options={}, *containers)
|
36
|
+
options, *containers = Trailblazer::Deprecation::Operation::Call.options_for_call(options, *containers)
|
37
|
+
|
38
|
+
ctx = Trailblazer::Operation::PublicCall.options_for_public_call(options, *containers)
|
39
|
+
|
40
|
+
# call the activity.
|
41
|
+
last_signal, (options, flow_options) = __call__( [ctx, {}] ) # Railway::call # DISCUSS: this could be ::call_with_context.
|
42
|
+
|
43
|
+
# Result is successful if the activity ended with an End event derived from Railway::End::Success.
|
44
|
+
Trailblazer::Operation::Railway::Result(last_signal, options, flow_options)
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
module Deprecation
|
3
|
+
class ContextWithIndifferentAccess < Trailblazer::Context
|
4
|
+
def [](key)
|
5
|
+
return super unless Trailblazer::Operation::PublicCall.deprecatable?(key)
|
6
|
+
key, _ = Trailblazer::Operation::PublicCall.deprecate_string(key, nil)
|
7
|
+
super(key)
|
8
|
+
end
|
9
|
+
|
10
|
+
def []=(key, value)
|
11
|
+
return super unless Trailblazer::Operation::PublicCall.deprecatable?(key)
|
12
|
+
key, _ = Trailblazer::Operation::PublicCall.deprecate_string(key, nil)
|
13
|
+
super(key, value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Trailblazer::Operation::PublicCall.module_eval do
|
20
|
+
def self.options_for_public_call(options={}, *containers)
|
21
|
+
hash_transformer = ->(containers) { containers[0].to_hash } # FIXME: don't transform any containers into kw args.
|
22
|
+
|
23
|
+
options = deprecate_strings(options)
|
24
|
+
|
25
|
+
immutable_options = Trailblazer::Context::ContainerChain.new( [options, *containers], to_hash: hash_transformer ) # Runtime options, immutable.
|
26
|
+
|
27
|
+
Trailblazer::Deprecation::ContextWithIndifferentAccess.new(immutable_options, {})
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.deprecatable?(key)
|
31
|
+
key.is_a?(String) && key.split(".").size == 1
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.deprecate_strings(options)
|
35
|
+
ary = options.collect { |k,v| deprecatable?(k) ? deprecate_string(k, v) : [k,v] }
|
36
|
+
Hash[ary]
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.deprecate_string(key, value)
|
40
|
+
warn "[Trailblazer] Using a string key for non-namespaced keys is deprecated. Please use `:#{key}` instead of `#{key.inspect}`."
|
41
|
+
[ key.to_sym, value ]
|
42
|
+
end
|
43
|
+
end
|
@@ -8,27 +8,58 @@
|
|
8
8
|
# Needs #[], #[]= skill dependency.
|
9
9
|
class Trailblazer::Operation
|
10
10
|
module Contract
|
11
|
-
def self.Build(name:"default", constant:nil, builder: nil)
|
12
|
-
step = ->(
|
11
|
+
def self.Build(name: "default", constant: nil, builder: nil)
|
12
|
+
step = ->((options, flow_options), **circuit_options) { Build.(options, circuit_options, name: name, constant: constant, builder: builder) }
|
13
13
|
|
14
|
-
|
14
|
+
task = Trailblazer::Activity::Task::Binary( step )
|
15
|
+
|
16
|
+
{ task: task, id: "contract.build" }
|
15
17
|
end
|
16
18
|
|
17
19
|
module Build
|
18
|
-
#
|
19
|
-
def self.
|
20
|
+
# Build contract at runtime.
|
21
|
+
def self.call(options, circuit_options, name: "default", constant: nil, builder: nil)
|
20
22
|
# TODO: we could probably clean this up a bit at some point.
|
21
|
-
contract_class = constant || options["contract.#{name}.class"]
|
22
|
-
model = options[
|
23
|
+
contract_class = constant || options["contract.#{name}.class"] # DISCUSS: Injection possible here?
|
24
|
+
model = options[:model]
|
23
25
|
name = "contract.#{name}"
|
24
26
|
|
25
|
-
|
27
|
+
options[name] =
|
28
|
+
if builder
|
29
|
+
call_builder( options, circuit_options, builder: builder, constant: contract_class, name: name )
|
30
|
+
else
|
31
|
+
contract_class.new(model)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.call_builder(options, circuit_options, builder:raise, constant:raise, name:raise)
|
36
|
+
# builder_options = Trailblazer::Context( options, constant: constant, name: name ) # options.merge( .. )
|
37
|
+
|
38
|
+
# Trailblazer::Option::KW(builder).(builder_options, circuit_options)
|
39
|
+
|
40
|
+
|
41
|
+
|
26
42
|
|
27
|
-
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
# FIXME: almost identical with Option::KW.
|
48
|
+
# FIXME: see Nested::Options::Dynamic, the same shit
|
49
|
+
tmp_options = options.to_hash.merge(
|
50
|
+
constant: constant,
|
51
|
+
name: name
|
52
|
+
)
|
53
|
+
|
54
|
+
Trailblazer::Option(builder).( options, tmp_options, circuit_options )
|
28
55
|
end
|
29
56
|
end
|
30
57
|
|
31
58
|
module DSL
|
59
|
+
def self.extended(extender)
|
60
|
+
extender.extend(ClassDependencies)
|
61
|
+
warn "[Trailblazer] Using `contract do...end` is deprecated. Please use a form class and the Builder( constant: <Form> ) option."
|
62
|
+
end
|
32
63
|
# This is the class level DSL method.
|
33
64
|
# Op.contract #=> returns contract class
|
34
65
|
# Op.contract do .. end # defines contract
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Operation
|
3
|
+
class DeprecatedOptions < Option
|
4
|
+
def self.call!(proc, direction, options, flow_options, *args)
|
5
|
+
if proc.is_a?(Proc) && proc.arity == 1
|
6
|
+
deprecate(proc)
|
7
|
+
proc.(options)
|
8
|
+
elsif proc.method(:call).arity == 1
|
9
|
+
deprecate(proc)
|
10
|
+
proc.(options)
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.deprecate(proc)
|
17
|
+
warn "[Trailblazer] Please use the step API `def my_step!(options, **)` for your step: #{proc}"
|
18
|
+
end
|
19
|
+
end # DeprecatedOptions
|
20
|
+
end
|
21
|
+
end
|
@@ -1,17 +1,17 @@
|
|
1
|
-
require "trailblazer/operation/policy"
|
2
|
-
|
3
1
|
class Trailblazer::Operation
|
4
2
|
module Policy
|
5
3
|
def self.Guard(proc, name: :default, &block)
|
6
|
-
Policy.step(Guard.build(proc), name: name)
|
4
|
+
Policy.step( Guard.build(proc), name: name )
|
7
5
|
end
|
8
6
|
|
9
7
|
module Guard
|
10
8
|
def self.build(callable)
|
11
|
-
|
9
|
+
option = Trailblazer::Option::KW(callable)
|
12
10
|
|
13
11
|
# this gets wrapped in a Operation::Result object.
|
14
|
-
->(
|
12
|
+
->( (options, *), circuit_args ) do
|
13
|
+
Result.new( !!option.(options, circuit_args), {} )
|
14
|
+
end
|
15
15
|
end
|
16
16
|
end # Guard
|
17
17
|
end
|
@@ -1,23 +1,28 @@
|
|
1
1
|
class Trailblazer::Operation
|
2
2
|
def self.Model(model_class, action=nil)
|
3
|
-
step =
|
3
|
+
# step = Pipetree::Step.new(step, "model.class" => model_class, "model.action" => action)
|
4
4
|
|
5
|
-
|
5
|
+
task = Railway::TaskBuilder.( Model.new )
|
6
6
|
|
7
|
-
|
7
|
+
runner_options = {
|
8
|
+
merge: Wrap::Inject::Defaults(
|
9
|
+
"model.class" => model_class,
|
10
|
+
"model.action" => action
|
11
|
+
)
|
12
|
+
}
|
13
|
+
|
14
|
+
{ task: task, id: "model.build", runner_options: runner_options }
|
8
15
|
end
|
9
16
|
|
10
|
-
|
11
|
-
def
|
17
|
+
class Model
|
18
|
+
def call(options, params:, **)
|
12
19
|
builder = Model::Builder.new
|
13
20
|
|
14
|
-
|
15
|
-
options["model"] = model = builder.(options, options["params"])
|
21
|
+
options[:model] = model = builder.(options, params)
|
16
22
|
|
17
|
-
|
23
|
+
options["result.model"] = result = Result.new(!model.nil?, {})
|
18
24
|
|
19
|
-
|
20
|
-
end
|
25
|
+
result.success?
|
21
26
|
end
|
22
27
|
|
23
28
|
class Builder
|
@@ -1,113 +1,84 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# WARNING: this is experimental API, but it will end up with something like that.
|
9
|
-
module Element
|
10
|
-
# DISCUSS: add builders here.
|
11
|
-
def initialize(wrapped=nil)
|
12
|
-
@wrapped = wrapped
|
13
|
-
end
|
1
|
+
# per default, everything we pass into a circuit is immutable. it's the ops/act's job to allow writing (via a Context)
|
2
|
+
module Trailblazer
|
3
|
+
class Operation
|
4
|
+
def self.Nested(callable, input:nil, output:nil, id: "Nested(#{callable})")
|
5
|
+
task_wrap_wirings = []
|
6
|
+
task, operation = Nested.build(callable, input, output)
|
14
7
|
|
15
|
-
|
16
|
-
def initialize(wrapped)
|
17
|
-
@wrapped = Option::KW.(wrapped)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
8
|
+
# @needs operation#outputs
|
21
9
|
|
22
|
-
|
23
|
-
|
24
|
-
# superinternal API is not entirely decided, yet.
|
25
|
-
# @api private
|
26
|
-
def self.for(step, input, output, is_nestable_object=method(:nestable_object?)) # DISCUSS: use builders here?
|
27
|
-
invoker = Caller::Dynamic.new(step)
|
28
|
-
invoker = Caller.new(step) if is_nestable_object.(step)
|
10
|
+
# TODO: move this to the generic step DSL
|
11
|
+
task_wrap_extensions = []
|
29
12
|
|
30
|
-
|
31
|
-
|
13
|
+
if input || output
|
14
|
+
default_input_filter = ->(options, *) { ctx = options }
|
15
|
+
default_output_filter = ->(options, *) { options }
|
32
16
|
|
33
|
-
|
34
|
-
|
17
|
+
input ||= default_input_filter
|
18
|
+
output ||= default_output_filter
|
35
19
|
|
36
|
-
|
37
|
-
|
38
|
-
result = invoker.(operation, options, options_for_nested.(operation, options)) # TODO: what about containers?
|
20
|
+
input_filter = Activity::Wrap::Input.new(input)
|
21
|
+
output_filter = Activity::Wrap::Output.new(output)
|
39
22
|
|
40
|
-
|
41
|
-
|
42
|
-
|
23
|
+
task_wrap_extensions = Activity::Magnetic::Builder::Path.plan do
|
24
|
+
task input_filter, id: ".input", before: "task_wrap.call_task"
|
25
|
+
task output_filter, id: ".output", before: "End.success", group: :end # DISCUSS: position
|
26
|
+
end
|
43
27
|
end
|
44
|
-
|
28
|
+
# Default {Output} copies the mutable data from the nested activity into the original.
|
45
29
|
|
46
|
-
|
47
|
-
# interestingly, with < we get a weird nil exception. bug in Ruby?
|
48
|
-
object.is_a?(Class) && object <= Trailblazer::Operation
|
30
|
+
{ task: task, id: id, runner_options: { merge: task_wrap_extensions }, plus_poles: Activity::Magnetic::DSL::PlusPoles.from_outputs(operation.outputs) }
|
49
31
|
end
|
50
32
|
|
51
|
-
#
|
52
|
-
|
53
|
-
|
33
|
+
# @private
|
34
|
+
module Nested
|
35
|
+
def self.build(nested_operation, input, output) # DISCUSS: use builders here?
|
36
|
+
return dynamic = Dynamic.new(nested_operation), dynamic unless nestable_object?(nested_operation)
|
54
37
|
|
55
|
-
|
56
|
-
|
38
|
+
# The returned {Nested} instance is a valid circuit element and will be `call`ed in the circuit.
|
39
|
+
# It simply returns the nested activity's `signal,options,flow_options` return set.
|
40
|
+
# The actual wiring - where to go with that - is done by the step DSL.
|
41
|
+
return Trailblazer::Activity::Subprocess(nested_operation, call: :__call__), nested_operation
|
57
42
|
end
|
58
43
|
|
59
|
-
|
60
|
-
|
61
|
-
operation._call(options)
|
44
|
+
def self.nestable_object?(object)
|
45
|
+
object.is_a?( Trailblazer::Activity::Interface )
|
62
46
|
end
|
63
47
|
|
64
|
-
def
|
65
|
-
|
66
|
-
class Dynamic < Caller
|
67
|
-
include Element::Dynamic
|
68
|
-
|
69
|
-
def nested(input, options)
|
70
|
-
@wrapped.(input, options)
|
71
|
-
end
|
48
|
+
def self.operation_class
|
49
|
+
Operation
|
72
50
|
end
|
73
|
-
end
|
74
|
-
|
75
|
-
class Options
|
76
|
-
include Element
|
77
51
|
|
78
|
-
|
79
|
-
def call(input, options)
|
80
|
-
options.to_runtime_data[0]
|
81
|
-
end
|
52
|
+
private
|
82
53
|
|
54
|
+
# For dynamic `Nested`s that do not expose an {Activity} interface.
|
55
|
+
# Since we do not know its outputs, we have to map them to :success and :failure, only.
|
56
|
+
#
|
57
|
+
# This is what {Nested} in 2.0 used to do, where the outcome could only be true/false (or success/failure).
|
83
58
|
class Dynamic
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
59
|
+
def initialize(wrapped)
|
60
|
+
@wrapped = Trailblazer::Option::KW(wrapped)
|
61
|
+
@outputs = {
|
62
|
+
:success => Activity::Output( Railway::End::Success.new(:success), :success ),
|
63
|
+
:failure => Activity::Output( Railway::End::Failure.new(:failure), :failure ),
|
64
|
+
}
|
88
65
|
end
|
89
|
-
end
|
90
66
|
|
91
|
-
|
92
|
-
include Element
|
67
|
+
attr_reader :outputs
|
93
68
|
|
94
|
-
def call(
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
def mutable_data_for(result)
|
99
|
-
result.instance_variable_get(:@data).to_mutable_data
|
100
|
-
end
|
69
|
+
def call( (options, flow_options), **circuit_options )
|
70
|
+
activity = @wrapped.(options, circuit_options) # evaluate the option to get the actual "object" to call.
|
101
71
|
|
102
|
-
|
103
|
-
include Element::Dynamic
|
72
|
+
signal, args = activity.__call__( [options, flow_options], **circuit_options )
|
104
73
|
|
105
|
-
|
106
|
-
|
107
|
-
|
74
|
+
# Translate the genuine nested signal to the generic Dynamic end (success/failure, only).
|
75
|
+
# Note that here we lose information about what specific event was emitted.
|
76
|
+
[
|
77
|
+
signal.kind_of?(Railway::End::Success) ? @outputs[:success].signal : @outputs[:failure].signal,
|
78
|
+
args
|
79
|
+
]
|
108
80
|
end
|
109
81
|
end
|
110
82
|
end
|
111
|
-
end
|
83
|
+
end # Operation
|
112
84
|
end
|
113
|
-
|