trailblazer-operation 0.10.1 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +2 -2
- data/CHANGES.md +10 -0
- data/lib/trailblazer/operation/class_dependencies.rb +1 -1
- data/lib/trailblazer/operation/public_call.rb +24 -14
- data/lib/trailblazer/operation/railway.rb +13 -7
- data/lib/trailblazer/operation/version.rb +1 -1
- data/lib/trailblazer/operation/wtf.rb +11 -0
- data/lib/trailblazer/operation.rb +2 -2
- data/test/call_test.rb +32 -10
- data/test/class_dependencies_test.rb +8 -4
- data/test/docs/autogenerated/activity_basics_test.rb +72 -0
- data/test/docs/autogenerated/composable_variable_mapping_test.rb +880 -0
- data/test/docs/autogenerated/fast_track_layout_test.rb +76 -0
- data/test/docs/autogenerated/mechanics_test.rb +382 -0
- data/test/docs/autogenerated/sequence_options_test.rb +202 -0
- data/test/docs/autogenerated/subprocess_test.rb +257 -0
- data/test/docs/autogenerated/wiring_api_test.rb +435 -0
- data/test/docs/developer_test.rb +27 -0
- data/test/docs/public_call_monkeypatching_test.rb +96 -0
- data/test/docs/result_test.rb +38 -0
- data/test/docs/step_dsl_test.rb +93 -0
- data/test/operation_test.rb +53 -13
- data/test/result_test.rb +1 -1
- data/test/test_helper.rb +17 -5
- data/test/trace_test.rb +3 -43
- data/trailblazer-operation.gemspec +2 -1
- metadata +42 -23
- data/lib/trailblazer/operation/trace.rb +0 -67
- data/test/callable_test.rb +0 -147
- data/test/docs/doormat_test.rb +0 -190
- data/test/docs/macaroni_test.rb +0 -31
- data/test/skill_test.rb +0 -66
- data/test/wire_test.rb +0 -113
- data/test/wiring/defaults_test.rb +0 -193
- data/test/wiring/subprocess_test.rb +0 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 318d6a14d3ed10922cd2b8a2ec9d67f3d73672488c4833ddfa883ab13c382fe3
|
4
|
+
data.tar.gz: 3432c41d2f36efa9f313f2da68195dfab92b486881db581c03430955202c188b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d53cc20604c0e11912e4822ce0ab6cc5f05da4a7f7a4cc688da2d350bf859a2e7863f36324bd2c1015ec3ed38118f9ad0e6dcd424229552c0ad6853f760f0af
|
7
|
+
data.tar.gz: e897df0be7f3530c8f8f826ace7db4e0255ce055bda7cbdae995596d91cad2bdd19cbd21f6b2645507ef4a4b7dd71896c94ca78ab4d75960f8ef23bff2a10ba1
|
data/.github/workflows/ci.yml
CHANGED
@@ -5,10 +5,10 @@ jobs:
|
|
5
5
|
strategy:
|
6
6
|
fail-fast: false
|
7
7
|
matrix:
|
8
|
-
ruby: [2.5, 2.6, 2.7, '3.0', '3.1', '3.2', 'jruby']
|
8
|
+
ruby: [2.5, 2.6, 2.7, '3.0', '3.1', '3.2', "3.3", 'jruby']
|
9
9
|
runs-on: ubuntu-latest
|
10
10
|
steps:
|
11
|
-
- uses: actions/checkout@
|
11
|
+
- uses: actions/checkout@v4
|
12
12
|
- uses: ruby/setup-ruby@v1
|
13
13
|
with:
|
14
14
|
ruby-version: ${{ matrix.ruby }}
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## 0.11.0
|
2
|
+
|
3
|
+
* Introduce `Operation.call_with_public_interface_from_call` which merges `ctx` and `circuit_options`
|
4
|
+
accordingly, so the overrider of `Operation.call_with_public_interface` gets correct args.
|
5
|
+
* Removing `Operation.trace`, you've been warned! Use `Operation.wtf?`. This also removes `Result#wtf`.
|
6
|
+
See [https://github.com/trailblazer/trailblazer-operation/blob/3f821c7d576e7ccccf580fbd8c9305501fdc5d2c/test/trace_test.rb#L22](this sample test case)
|
7
|
+
if you need a more low-level interface to tracing.
|
8
|
+
* No need to pass `:exec_context` in `#call_with_public_interface`. This is done in `Strategy.call`.
|
9
|
+
* Rename `Result#event` to `Result#terminus` for consistency. Deprecate `Result#event`.
|
10
|
+
|
1
11
|
## 0.10.1
|
2
12
|
|
3
13
|
* Deprecate `Operation.trace` and `Result#wtf?` in favor of `Operation.wtf?`.
|
@@ -25,7 +25,7 @@ class Trailblazer::Operation
|
|
25
25
|
end
|
26
26
|
|
27
27
|
private def context_for_fields(fields, (ctx, flow_options), **)
|
28
|
-
|
28
|
+
_ctx_with_fields = Trailblazer::Context(fields, ctx, flow_options[:context_options]) # TODO: redundant to options_for_public_call.
|
29
29
|
end
|
30
30
|
|
31
31
|
def call_with_circuit_interface((ctx, flow_options), **circuit_options)
|
@@ -16,7 +16,16 @@ module Trailblazer
|
|
16
16
|
def call(options = {}, flow_options = {}, **circuit_options)
|
17
17
|
return call_with_circuit_interface(options, **circuit_options) if options.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 thing as we're fading out Operation::public_call anyway.
|
18
18
|
|
19
|
-
|
19
|
+
call_with_public_interface_from_call(options, flow_options, **circuit_options)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @private Please do not override this method as it might get removed.
|
23
|
+
def call_with_public_interface_from_call(options, flow_options, **circuit_options)
|
24
|
+
# normalize options.
|
25
|
+
options = options
|
26
|
+
.merge(circuit_options) # when using Op.call(params:, ...), {circuit_options} will always be ctx variables.
|
27
|
+
|
28
|
+
call_with_public_interface(options, flow_options)
|
20
29
|
end
|
21
30
|
|
22
31
|
# Default {@activity} call interface which doesn't accept {circuit_options}
|
@@ -25,24 +34,19 @@ module Trailblazer
|
|
25
34
|
#
|
26
35
|
# @return [Operation::Railway::Result]
|
27
36
|
#
|
28
|
-
# @
|
37
|
+
# @semi-public It's OK to override this method.
|
29
38
|
def call_with_public_interface(options, flow_options, invoke_class: Activity::TaskWrap, **circuit_options)
|
30
39
|
flow_options = flow_options_for_public_call(flow_options)
|
31
|
-
|
32
|
-
# In Ruby < 3, calling Op.(params: {}, "current_user" => user) results in both {circuit_options} and {options} containing variables.
|
33
|
-
# In Ruby 3.0, **circuit_options is always empty.
|
34
|
-
options = circuit_options.any? ? circuit_options.merge(options) : options
|
35
|
-
|
36
40
|
ctx = options_for_public_call(options, flow_options)
|
37
41
|
|
38
|
-
#
|
39
|
-
|
42
|
+
# Call the activity as it if was a step in an endpoint.
|
43
|
+
|
44
|
+
# This will result in invoking {self.call_with_circuit_interface}.
|
40
45
|
signal, (ctx, flow_options) = invoke_class.invoke(
|
41
46
|
self,
|
42
47
|
[ctx, flow_options],
|
43
|
-
|
44
|
-
#
|
45
|
-
container_activity: Activity::TaskWrap.container_activity_for(self, wrap_static: initial_wrap_static)
|
48
|
+
container_activity: Activity::TaskWrap.container_activity_for(self, wrap_static: initial_wrap_static), # we cannot make this static because of {self} unless we override {#inherited}.
|
49
|
+
**circuit_options # this will always be an empty hash if coming from #{call_with_public_interface_from_call}.
|
46
50
|
)
|
47
51
|
|
48
52
|
# Result is successful if the activity ended with an End event derived from Railway::End::Success.
|
@@ -83,15 +87,21 @@ module Trailblazer
|
|
83
87
|
call_with_public_interface(options, flow_options, {invoke_class: Activity::TaskWrap})
|
84
88
|
end
|
85
89
|
|
90
|
+
# This TaskWrap step replaces the default {call_task} step for this very operation.
|
91
|
+
# Instead of invoking the operation using {Operation.call}, it does {Operation.call_with_circuit_interface},
|
92
|
+
# so we don't invoke {Operation.call} twice.
|
93
|
+
#
|
94
|
+
# I don't really like this "hack", but it's the only way until we get method overloading.
|
95
|
+
#
|
86
96
|
# @private
|
87
97
|
def self.call_task(wrap_ctx, original_args) # DISCUSS: copied from {TaskWrap.call_task}.
|
88
|
-
|
98
|
+
operation = wrap_ctx[:task]
|
89
99
|
|
90
100
|
original_arguments, original_circuit_options = original_args
|
91
101
|
|
92
102
|
# Call the actual task we're wrapping here.
|
93
103
|
# puts "~~~~wrap.call: #{task}"
|
94
|
-
return_signal, return_args =
|
104
|
+
return_signal, return_args = operation.call_with_circuit_interface(original_arguments, **original_circuit_options)
|
95
105
|
|
96
106
|
# DISCUSS: do we want original_args here to be passed on, or the "effective" return_args which are different to original_args now?
|
97
107
|
wrap_ctx = wrap_ctx.merge(return_signal: return_signal, return_args: return_args)
|
@@ -10,20 +10,26 @@ module Trailblazer
|
|
10
10
|
def self.fail_fast!; Activity::FastTrack::FailFast end
|
11
11
|
def self.pass_fast!; Activity::FastTrack::PassFast end
|
12
12
|
# @param options Context
|
13
|
-
# @param
|
14
|
-
def self.Result(
|
15
|
-
Result.new(
|
13
|
+
# @param terminus The last emitted signal in a circuit is the end event/terminus.
|
14
|
+
def self.Result(terminus, options, *)
|
15
|
+
Result.new(terminus.kind_of?(End::Success), options, terminus)
|
16
16
|
end
|
17
17
|
|
18
|
-
# The Railway::Result knows about its binary state, the context (data), and
|
18
|
+
# The Railway::Result knows about its binary state, the context (data), and
|
19
|
+
# the reached terminus of the circuit.
|
19
20
|
class Result < Result # Operation::Result
|
20
|
-
def initialize(success, data,
|
21
|
+
def initialize(success, data, terminus)
|
21
22
|
super(success, data)
|
22
23
|
|
23
|
-
@
|
24
|
+
@terminus = terminus
|
24
25
|
end
|
25
26
|
|
26
|
-
|
27
|
+
def event
|
28
|
+
Activity::Deprecate.warn caller_locations[0], %(Using `Result#event` is deprecated, please use `Result#terminus`)
|
29
|
+
terminus
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :terminus
|
27
33
|
end
|
28
34
|
|
29
35
|
module End
|
@@ -50,8 +50,8 @@ module Trailblazer
|
|
50
50
|
require "trailblazer/operation/public_call" # TODO: Remove in 3.0.
|
51
51
|
extend PublicCall # ::call(params: .., current_user: ..)
|
52
52
|
|
53
|
-
require "trailblazer/operation/
|
54
|
-
extend
|
53
|
+
require "trailblazer/operation/wtf"
|
54
|
+
extend Wtf # ::trace
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
data/test/call_test.rb
CHANGED
@@ -41,21 +41,22 @@ class CallTest < Minitest::Spec
|
|
41
41
|
result.event.must_equal Update.to_h[:outputs].find { |output| output.semantic == :failure }.signal
|
42
42
|
end
|
43
43
|
|
44
|
+
def self.add_1(wrap_ctx, original_args)
|
45
|
+
ctx, = original_args[0]
|
46
|
+
ctx[:seq] << 1
|
47
|
+
return wrap_ctx, original_args # yay to mutable state. not.
|
48
|
+
end
|
49
|
+
|
44
50
|
it "invokes with the taskWrap" do
|
45
51
|
operation = Class.new(Trailblazer::Operation) do
|
46
52
|
include Trailblazer::Activity::Testing.def_steps(:a)
|
47
53
|
|
48
|
-
def self.add_1(wrap_ctx, original_args)
|
49
|
-
ctx, = original_args[0]
|
50
|
-
ctx[:seq] << 1
|
51
|
-
return wrap_ctx, original_args # yay to mutable state. not.
|
52
|
-
end
|
53
|
-
|
54
54
|
merge = [
|
55
|
-
[Trailblazer::Activity::TaskWrap::Pipeline.method(:insert_before), "task_wrap.call_task", ["user.add_1", method(:add_1)]]
|
56
55
|
]
|
57
56
|
|
58
|
-
step :a, extensions: [Trailblazer::Activity::TaskWrap::Extension(
|
57
|
+
step :a, extensions: [Trailblazer::Activity::TaskWrap::Extension.WrapStatic(
|
58
|
+
[CallTest.method(:add_1), prepend: "task_wrap.call_task", id: "user.add_1"]
|
59
|
+
)]
|
59
60
|
end
|
60
61
|
|
61
62
|
# normal operation invocation
|
@@ -64,10 +65,31 @@ class CallTest < Minitest::Spec
|
|
64
65
|
result.inspect(:seq).must_equal %{<Result:true [[1, :a]] >}
|
65
66
|
|
66
67
|
# with tracing
|
67
|
-
result = operation.
|
68
|
+
result = operation.wtf?(seq: [])
|
68
69
|
|
69
70
|
result.inspect(:seq).must_equal %{<Result:true [[1, :a]] >}
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
it "calls with the taskWrap defined for operation using circuit interface" do
|
75
|
+
operation = Class.new(Trailblazer::Operation) do
|
76
|
+
include Trailblazer::Activity::Testing.def_steps(:a)
|
77
|
+
|
78
|
+
step :a
|
79
|
+
end
|
80
|
+
|
81
|
+
my_extension = Trailblazer::Activity::TaskWrap::Extension(
|
82
|
+
[CallTest.method(:add_1), id: "my.add_1", append: "task_wrap.call_task"]
|
83
|
+
)
|
84
|
+
|
85
|
+
# circuit interface invocation using call
|
86
|
+
signal, (ctx, _) = operation.call(
|
87
|
+
[{seq: []}, {}],
|
88
|
+
wrap_runtime: Hash.new(my_extension),
|
89
|
+
runner: Trailblazer::Activity::TaskWrap::Runner
|
90
|
+
)
|
70
91
|
|
71
|
-
|
92
|
+
assert_equal signal.to_h[:semantic], :success
|
93
|
+
assert_equal ctx[:seq], [1, :a, 1, 1]
|
72
94
|
end
|
73
95
|
end
|
@@ -28,10 +28,14 @@ class ClassDependenciesTest < Minitest::Spec
|
|
28
28
|
end
|
29
29
|
|
30
30
|
# "model.class" gets injected automatically just before {Index}.
|
31
|
-
Home.({params: {}})
|
31
|
+
result = Home.({params: {}})
|
32
|
+
assert_result result, {:"model.class"=>Module, :params=>{}, :a=>Module}
|
33
|
+
# .inspect.must_equal %{<Result:true #<Trailblazer::Context::Container wrapped_options={\"model.class\"=>Module} mutable_options=#<Trailblazer::Context::Container wrapped_options={:params=>{}} mutable_options={\"a\"=>Module}>> >}
|
32
34
|
|
33
35
|
# "model.class" gets injected by user and overrides class dependencies.
|
34
|
-
Home.({params: {}, "model.class" => Symbol})
|
36
|
+
result = Home.({params: {}, "model.class" => Symbol})
|
37
|
+
assert_result result, {:"model.class"=>Symbol, :params=>{}, :a=>Symbol }
|
38
|
+
# .inspect.must_equal %{<Result:true #<Trailblazer::Context::Container wrapped_options={\"model.class\"=>Module} mutable_options=#<Trailblazer::Context::Container wrapped_options={:params=>{}, \"model.class\"=>Symbol} mutable_options={\"a\"=>Symbol}>> >}
|
35
39
|
|
36
40
|
|
37
41
|
class Dashboard < Trailblazer::Operation
|
@@ -43,8 +47,8 @@ class ClassDependenciesTest < Minitest::Spec
|
|
43
47
|
end
|
44
48
|
|
45
49
|
# TODO: "model.class" gets injected automatically in {Dashboard} and overrides the {Index} input.
|
46
|
-
Dashboard.({params: {}}).inspect
|
47
|
-
|
50
|
+
assert_equal Dashboard.({params: {}}).inspect,
|
51
|
+
%(<Result:true #<Trailblazer::Context::Container wrapped_options=#{{"model.class" => Module}} mutable_options=#<Trailblazer::Context::Container wrapped_options=#{{"model.class" => Float}} mutable_options=#<Trailblazer::Context::Container wrapped_options=#{{"model.class" => Float}} mutable_options=#<Trailblazer::Context::Container wrapped_options=#{{:params => {}}} mutable_options=#{{:Dashboard => Float, "a" => Float}}>>>> >)
|
48
52
|
end
|
49
53
|
|
50
54
|
describe "inheritance" do
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# THIS FILE IS AUTOGENERATED FROM trailblazer-activity-dsl-linear/test/docs/activity_basics_test.rb
|
2
|
+
require "test_helper"
|
3
|
+
|
4
|
+
module X
|
5
|
+
class DocsActivityTest < Minitest::Spec
|
6
|
+
it "basic activity" do
|
7
|
+
Memo = Struct.new(:options) do
|
8
|
+
def save
|
9
|
+
true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
#:memo-create
|
14
|
+
module Memo::Operation
|
15
|
+
class Create < Trailblazer::Operation
|
16
|
+
step :validate
|
17
|
+
#~body
|
18
|
+
step :save
|
19
|
+
left :handle_errors
|
20
|
+
step :notify
|
21
|
+
#~meths
|
22
|
+
include T.def_steps(:validate, :save, :handle_errors, :notify)
|
23
|
+
|
24
|
+
#:save
|
25
|
+
def save(ctx, params:, **)
|
26
|
+
memo = Memo.new(params[:memo])
|
27
|
+
memo.save
|
28
|
+
|
29
|
+
ctx[:model] = memo # you can write to the {ctx}.
|
30
|
+
end
|
31
|
+
#:save end
|
32
|
+
|
33
|
+
def notify(ctx, **)
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
#~body end
|
38
|
+
def validate(ctx, params:, **) # home-made validation
|
39
|
+
params.key?(:memo) &&
|
40
|
+
params[:memo].key?(:text) &&
|
41
|
+
params[:memo][:text].size > 9
|
42
|
+
# return value matters!
|
43
|
+
end
|
44
|
+
#~meths end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
#:memo-create end
|
48
|
+
|
49
|
+
#:memo-call
|
50
|
+
result = Memo::Operation::Create.(
|
51
|
+
params: {memo: {text: "Do not forget!"}}
|
52
|
+
)
|
53
|
+
|
54
|
+
result.success? # => true
|
55
|
+
puts result.terminus.to_h[:semantic] #=> :success
|
56
|
+
#:memo-call end
|
57
|
+
|
58
|
+
#:memo-call-model
|
59
|
+
result = Memo::Operation::Create.(
|
60
|
+
params: {memo: {text: "Do not forget!"}}
|
61
|
+
)
|
62
|
+
|
63
|
+
#~ctx_to_result
|
64
|
+
puts result[:model] #=> #<Memo id: 1 text: "Do not forget!">
|
65
|
+
#:memo-call-model end
|
66
|
+
#~ctx_to_result end
|
67
|
+
|
68
|
+
assert_equal result.terminus.inspect, %(#<Trailblazer::Activity::Railway::End::Success semantic=:success>)
|
69
|
+
#~ignore end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|