trailblazer 2.0.7 → 2.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![TRB Newsletter](https://img.shields.io/badge/TRB-newsletter-lightgrey.svg)](http://trailblazer.to/newsletter/)
|
7
7
|
[![Gem Version](https://badge.fury.io/rb/trailblazer.svg)](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
|
-
|