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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -2
  3. data/CHANGES.md +10 -0
  4. data/lib/trailblazer/operation/class_dependencies.rb +1 -1
  5. data/lib/trailblazer/operation/public_call.rb +24 -14
  6. data/lib/trailblazer/operation/railway.rb +13 -7
  7. data/lib/trailblazer/operation/version.rb +1 -1
  8. data/lib/trailblazer/operation/wtf.rb +11 -0
  9. data/lib/trailblazer/operation.rb +2 -2
  10. data/test/call_test.rb +32 -10
  11. data/test/class_dependencies_test.rb +8 -4
  12. data/test/docs/autogenerated/activity_basics_test.rb +72 -0
  13. data/test/docs/autogenerated/composable_variable_mapping_test.rb +880 -0
  14. data/test/docs/autogenerated/fast_track_layout_test.rb +76 -0
  15. data/test/docs/autogenerated/mechanics_test.rb +382 -0
  16. data/test/docs/autogenerated/sequence_options_test.rb +202 -0
  17. data/test/docs/autogenerated/subprocess_test.rb +257 -0
  18. data/test/docs/autogenerated/wiring_api_test.rb +435 -0
  19. data/test/docs/developer_test.rb +27 -0
  20. data/test/docs/public_call_monkeypatching_test.rb +96 -0
  21. data/test/docs/result_test.rb +38 -0
  22. data/test/docs/step_dsl_test.rb +93 -0
  23. data/test/operation_test.rb +53 -13
  24. data/test/result_test.rb +1 -1
  25. data/test/test_helper.rb +17 -5
  26. data/test/trace_test.rb +3 -43
  27. data/trailblazer-operation.gemspec +2 -1
  28. metadata +42 -23
  29. data/lib/trailblazer/operation/trace.rb +0 -67
  30. data/test/callable_test.rb +0 -147
  31. data/test/docs/doormat_test.rb +0 -190
  32. data/test/docs/macaroni_test.rb +0 -31
  33. data/test/skill_test.rb +0 -66
  34. data/test/wire_test.rb +0 -113
  35. data/test/wiring/defaults_test.rb +0 -193
  36. data/test/wiring/subprocess_test.rb +0 -70
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 00f1e4d05147c0c9434d2a72017c7ea452cd06a5bf5e90985f7f809f5246ac9b
4
- data.tar.gz: 6508179d1158e4719f90e7e6a219676b47780468d99f58b6c4178f423415f6ca
3
+ metadata.gz: 318d6a14d3ed10922cd2b8a2ec9d67f3d73672488c4833ddfa883ab13c382fe3
4
+ data.tar.gz: 3432c41d2f36efa9f313f2da68195dfab92b486881db581c03430955202c188b
5
5
  SHA512:
6
- metadata.gz: bc578ee53cf1f1ecbef264943841734cb250902c329f7866eec533553e720c58ac1d5f8edf3bf84405a138aa37f25a7abf01153458ce3d177833fe0e51f83801
7
- data.tar.gz: b1972a63eddccb14fb5814c0e973543757feb56d62a0400be18da49ef8c0ca3ddda40aaef8d28983177783a48aa2345b8d86f4eaf52a43a098638956914ef60c
6
+ metadata.gz: 9d53cc20604c0e11912e4822ce0ab6cc5f05da4a7f7a4cc688da2d350bf859a2e7863f36324bd2c1015ec3ed38118f9ad0e6dcd424229552c0ad6853f760f0af
7
+ data.tar.gz: e897df0be7f3530c8f8f826ace7db4e0255ce055bda7cbdae995596d91cad2bdd19cbd21f6b2645507ef4a4b7dd71896c94ca78ab4d75960f8ef23bff2a10ba1
@@ -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@v3
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
- ctx_with_fields = Trailblazer::Context(fields, ctx, flow_options[:context_options]) # TODO: redundant to options_for_public_call.
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
- call_with_public_interface(options, flow_options, **circuit_options)
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
- # @private
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
- # call the activity.
39
- # This will result in invoking {::call_with_circuit_interface}.
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
- exec_context: new,
44
- # wrap_static: initial_wrap_static,
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
- op = wrap_ctx[:task]
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 = op.call_with_circuit_interface(original_arguments, **original_circuit_options)
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 end_event The last emitted signal in a circuit is usually the end event.
14
- def self.Result(end_event, options, *)
15
- Result.new(end_event.kind_of?(End::Success), options, end_event)
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 the last event in the circuit.
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, event)
21
+ def initialize(success, data, terminus)
21
22
  super(success, data)
22
23
 
23
- @event = event
24
+ @terminus = terminus
24
25
  end
25
26
 
26
- attr_reader :event
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
@@ -1,7 +1,7 @@
1
1
  module Trailblazer
2
2
  module Version
3
3
  module Operation
4
- VERSION = "0.10.1"
4
+ VERSION = "0.11.0"
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,11 @@
1
+ require "trailblazer/developer"
2
+
3
+ module Trailblazer
4
+ class Operation
5
+ module Wtf
6
+ def wtf?(options)
7
+ call_with_public_interface(options, {}, invoke_class: Developer::Wtf)
8
+ end
9
+ end
10
+ end
11
+ 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/trace"
54
- extend Trace # ::trace
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(merge: merge)]
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.trace(seq: [])
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
- result.wtf?
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: {}}).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}>> >}
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}).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}>> >}
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.must_equal %{<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}>>>> >}
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