trailblazer-operation 0.10.0 → 0.11.0

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -2
  3. data/CHANGES.md +15 -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 -19
  27. data/trailblazer-operation.gemspec +3 -2
  28. metadata +50 -25
  29. data/lib/trailblazer/operation/trace.rb +0 -53
  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: 055b4f6ef1ce80319468ccbb1299dadf1ff82429179381904780a1d04bcda48f
4
- data.tar.gz: c3d25c70992d811625a162c5dce4bc5751df2665034b80d6739b4153860914ca
3
+ metadata.gz: 318d6a14d3ed10922cd2b8a2ec9d67f3d73672488c4833ddfa883ab13c382fe3
4
+ data.tar.gz: 3432c41d2f36efa9f313f2da68195dfab92b486881db581c03430955202c188b
5
5
  SHA512:
6
- metadata.gz: 4e1450dc8761312a31ebb9113a1086e4f51f501a8fbc178f597b9720b14a767e7cd4b42a833b42b2ed065b7c41cd8f9bb19c81ddd808020c4ab9f5643c0375c0
7
- data.tar.gz: 7ee732afe996545413207ea93ece417b1d5ccb74b51820048957f73c22d8ea53eb61a01c1d96531056e8bbd407e5d1c004382e36132e5dfa435468b24192981c
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,18 @@
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
+
11
+ ## 0.10.1
12
+
13
+ * Deprecate `Operation.trace` and `Result#wtf?` in favor of `Operation.wtf?`.
14
+ * Use `trailblazer-developer-0.1.0`.
15
+
1
16
  ## 0.10.0
2
17
 
3
18
  * Require `trailblazer-activity-dsl-linear-1.2.0`.
@@ -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.0"
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