trailblazer-macro 2.1.4 → 2.1.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4da758ccf1b9e9ff6fa413c687fe09c6df11449b84a775c65280a73d1b80f72
4
- data.tar.gz: 23bd67cc32f045487ca761e15227fe562e87b7cde3616e7335a8ee3126c51217
3
+ metadata.gz: 639ab06bc834f5258e404d9c87e1e53db434884225e6f6c222afcb9df9597154
4
+ data.tar.gz: b5f08c21b83529cd8a12fed2e6ef3e638a9db546b2d57d651f3a9f9adec8ff3c
5
5
  SHA512:
6
- metadata.gz: 4671b91a21144afc728dba8397282a49f6517d183349179a357da87e57a223cb4f5cb81f3d29f0bda0e3013db7581367e02a6de28a19bfbc7b83dd286a3f0d9a
7
- data.tar.gz: 339a312f0c7f7bee0afe5f74b06d3694ff826daff2e5bbfe4190a3238252cd92feb36ab14cf2429814eaf5b18e50abdac18bee594b7229c37fc64df8856e18b8
6
+ metadata.gz: ebf4969c76b10be6b3e842376ebbb6bb5fd73bd7a3c8fe1f6c61ab1efb9e8d945b3bb13404fcf6cdbead3590deb5cf9aeaf0150a32209d14107e2019981732ed
7
+ data.tar.gz: 5d485adedac0da50b68f3528ddaf80329f9d33a06cd6768e7a7c1d430c4f7ff05bf5daafbf14eb174fd821bbb5d1108150bbb278fbc7eacb960c26fd8f058cca
@@ -0,0 +1,17 @@
1
+ name: CI
2
+ on: [push, pull_request]
3
+ jobs:
4
+ test:
5
+ strategy:
6
+ fail-fast: false
7
+ matrix:
8
+ # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0'
9
+ ruby: [2.5, 2.6, 2.7, '3.0', head, jruby, jruby-head]
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v2
13
+ - uses: ruby/setup-ruby@v1
14
+ with:
15
+ ruby-version: ${{ matrix.ruby }}
16
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
17
+ - run: bundle exec rake
data/CHANGES.md CHANGED
@@ -1,3 +1,25 @@
1
+ # 2.1.9
2
+
3
+ * Allow omitting `:params` when calling the operation by defaulting it to `{}` in `Model()`.
4
+ * Use `dsl-linear` 0.5.0 and above, which allows removing `Inject()` uses.
5
+
6
+ # 2.1.8
7
+
8
+ Yanked due to inconsistency.
9
+
10
+ # 2.1.7
11
+
12
+ * Improve `Nested()` warning message.
13
+ * Fix exception in `Rescue()` macro when it's handler is a `Module`.
14
+
15
+ # 2.1.6
16
+
17
+ * Allow connecting ends of the dynamically selected activity for `Nested()` using `:auto_wire`.
18
+
19
+ # 2.1.5
20
+
21
+ * Support for Ruby 3.0.
22
+
1
23
  # 2.1.4
2
24
 
3
25
  * Upgrade DSL version to fix step's circuit interface eating passed arguments
data/Gemfile CHANGED
@@ -5,11 +5,11 @@ gemspec
5
5
 
6
6
  # gem "trailblazer", github: "trailblazer/trailblazer"
7
7
  # gem "trailblazer-activity", path: "../trailblazer-activity"
8
- # gem "trailblazer-activity-dsl-linear", github: "trailblazer/trailblazer-activity-dsl-linear", branch: 'master'
9
8
  # gem "trailblazer-activity-dsl-linear", path: "../trailblazer-activity-dsl-linear"
10
- # gem "trailblazer-operation", github: "trailblazer/trailblazer-operation", branch: 'linear'
11
- # gem "trailblazer-activity"#, github: "trailblazer/trailblazer-activity"
12
9
  # gem "trailblazer-macro-contract", git: "https://github.com/trailblazer/trailblazer-macro-contract"
10
+ # gem "trailblazer-context", path: "../trailblazer-context"
11
+ # gem "trailblazer-operation", path: "../trailblazer-operation"
12
+
13
13
 
14
14
  gem "minitest-line"
15
15
  gem "rubocop", require: false
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  All common Macro's for Trailblazer::Operation, will come here
3
3
 
4
4
  ## TODO
5
- Describe the following Macro's:
5
+ Describe the following Macros:
6
6
  - Nested
7
7
  - Rescue
8
8
  - Wrap
@@ -6,11 +6,10 @@ module Trailblazer::Macro
6
6
 
7
7
  module Guard
8
8
  def self.build(callable)
9
- option = Trailblazer::Option::KW(callable)
9
+ option = Trailblazer::Option(callable)
10
10
 
11
- # this gets wrapped in a Operation::Result object.
12
- ->((options, *), circuit_args) do
13
- Trailblazer::Operation::Result.new(!!option.call(options, circuit_args), {})
11
+ ->((ctx, *), **circuit_args) do
12
+ Trailblazer::Operation::Result.new(!!option.call(ctx, keyword_arguments: ctx.to_hash, **circuit_args), {})
14
13
  end
15
14
  end
16
15
  end
@@ -2,38 +2,39 @@ module Trailblazer::Macro
2
2
 
3
3
  Linear = Trailblazer::Activity::DSL::Linear
4
4
 
5
- def self.Model(model_class, action = nil, find_by_key = nil, id: 'model.build', not_found_terminus: false)
5
+ def self.Model(model_class = nil, action = :new, find_by_key = :id, id: 'model.build', not_found_terminus: false)
6
6
  task = Trailblazer::Activity::TaskBuilder::Binary(Model.new)
7
7
 
8
- injection = Trailblazer::Activity::TaskWrap::Inject::Defaults::Extension(
9
- :"model.class" => model_class,
10
- :"model.action" => action,
11
- :"model.find_by_key" => find_by_key
12
- )
8
+ injections = { # defaulting as per `:inject` API.
9
+ :"model.class" => ->(*) { model_class },
10
+ :"model.action" => ->(*) { action },
11
+ :"model.find_by_key" => ->(*) { find_by_key },
12
+ }
13
+
14
+ options = {task: task, id: id, inject: [:params, injections]} # pass-through {:params} if it's in ctx.
13
15
 
14
- options = { task: task, id: id, extensions: [injection] }
15
16
  options = options.merge(Linear::Output(:failure) => Linear::End(:not_found)) if not_found_terminus
16
17
 
17
18
  options
18
19
  end
19
20
 
20
21
  class Model
21
- def call(options, params: nil, **)
22
+ def call(ctx, params: {}, **)
22
23
  builder = Model::Builder.new
23
- options[:model] = model = builder.call(options, params)
24
- options[:"result.model"] = result = Trailblazer::Operation::Result.new(!model.nil?, {})
24
+ ctx[:model] = model = builder.call(ctx, params)
25
+ ctx[:"result.model"] = result = Trailblazer::Operation::Result.new(!model.nil?, {})
25
26
 
26
27
  result.success?
27
28
  end
28
29
 
29
30
  class Builder
30
- def call(options, params)
31
- action = options[:"model.action"] || :new
32
- model_class = options[:"model.class"]
33
- find_by_key = options[:"model.find_by_key"] || :id
31
+ def call(ctx, params)
32
+ action = ctx[:"model.action"]
33
+ model_class = ctx[:"model.class"]
34
+ find_by_key = ctx[:"model.find_by_key"]
34
35
  action = :pass_through unless %i[new find_by].include?(action)
35
36
 
36
- send("#{action}!", model_class, params, options[:"model.action"], find_by_key)
37
+ send("#{action}!", model_class, params, ctx[:"model.action"], find_by_key)
37
38
  end
38
39
 
39
40
  def new!(model_class, params, *)
@@ -2,14 +2,18 @@
2
2
  module Trailblazer
3
3
  module Macro
4
4
  # {Nested} macro.
5
- def self.Nested(callable, id: "Nested(#{callable})")
5
+ def self.Nested(callable, id: "Nested(#{callable})", auto_wire: [])
6
6
  if callable.is_a?(Class) && callable < Nested.operation_class
7
- warn %{[Trailblazer] Using the `Nested()` macro with operations and activities is deprecated. Replace `Nested(Create)` with `Subprocess(Create)`.}
7
+ caller_location = caller_locations(2, 1)[0]
8
+ warn "[Trailblazer]#{caller_location.absolute_path}: " \
9
+ "Using the `Nested()` macro with operations and activities is deprecated. " \
10
+ "Replace `Nested(#{callable})` with `Subprocess(#{callable})`."
11
+
8
12
  return Nested.operation_class.Subprocess(callable)
9
13
  end
10
14
 
11
15
  # dynamic
12
- task = Nested::Dynamic.new(callable)
16
+ task = Nested::Dynamic.new(callable, auto_wire: auto_wire)
13
17
 
14
18
  merge = [
15
19
  [Activity::TaskWrap::Pipeline.method(:insert_before), "task_wrap.call_task", ["Nested.compute_nested_activity", task.method(:compute_nested_activity)]],
@@ -33,17 +37,24 @@ module Trailblazer
33
37
  end
34
38
 
35
39
  # For dynamic `Nested`s that do not expose an {Activity} interface.
36
- # Since we do not know its outputs, we have to map them to :success and :failure, only.
37
40
  #
38
- # This is what {Nested} in 2.0 used to do, where the outcome could only be true/false (or success/failure).
41
+ # Dynamic doesn't automatically connect outputs of runtime {Activity}
42
+ # at compile time (as we don't know which activity will be nested, obviously).
43
+ # So by default, it only connects good old success/failure ends. But it is also
44
+ # possible to connect all the ends of all possible dynamic activities
45
+ # by passing their list to {:auto_wire} option.
46
+ #
47
+ # step Nested(:compute_nested, auto_wire: [Create, Update])
39
48
  class Dynamic
40
- def initialize(nested_activity_decider)
41
- @nested_activity_decider = Option::KW(nested_activity_decider)
49
+ STATIC_OUTPUTS = {
50
+ :success => Activity::Output(Activity::Railway::End::Success.new(semantic: :success), :success),
51
+ :failure => Activity::Output(Activity::Railway::End::Failure.new(semantic: :failure), :failure),
52
+ }
42
53
 
43
- @outputs = {
44
- :success => Activity::Output(Activity::Railway::End::Success.new(semantic: :success), :success),
45
- :failure => Activity::Output(Activity::Railway::End::Failure.new(semantic: :failure), :failure)
46
- }
54
+ def initialize(nested_activity_decider, auto_wire: [])
55
+ @nested_activity_decider = Trailblazer::Option(nested_activity_decider)
56
+ @known_activities = Array(auto_wire)
57
+ @outputs = compute_task_outputs
47
58
  end
48
59
 
49
60
  attr_reader :outputs
@@ -53,7 +64,7 @@ module Trailblazer
53
64
  (ctx, _), original_circuit_options = original_args
54
65
 
55
66
  # TODO: evaluate the option to get the actual "object" to call.
56
- activity = @nested_activity_decider.(ctx, original_circuit_options)
67
+ activity = @nested_activity_decider.(ctx, keyword_arguments: ctx.to_hash, **original_circuit_options)
57
68
 
58
69
  # Overwrite :task so task_wrap.call_task will call this activity.
59
70
  # This is a trick so we don't have to repeat logic from #call_task here.
@@ -63,13 +74,28 @@ module Trailblazer
63
74
  end
64
75
 
65
76
  def compute_return_signal(wrap_ctx, original_args)
66
- # Translate the genuine nested signal to the generic Dynamic end (success/failure, only).
67
- # Note that here we lose information about what specific event was emitted.
68
- wrap_ctx[:return_signal] = wrap_ctx[:return_signal].kind_of?(Activity::Railway::End::Success) ?
69
- @outputs[:success].signal : @outputs[:failure].signal
77
+ # NOOP when @known_activities are present as all possible signals have been registered already.
78
+ if @known_activities.empty?
79
+ # Translate the genuine nested signal to the generic Dynamic end (success/failure, only).
80
+ # Note that here we lose information about what specific event was emitted.
81
+ wrap_ctx[:return_signal] = wrap_ctx[:return_signal].kind_of?(Activity::Railway::End::Success) ?
82
+ @outputs[:success].signal : @outputs[:failure].signal
83
+ end
70
84
 
71
85
  return wrap_ctx, original_args
72
86
  end
87
+
88
+ private def compute_task_outputs
89
+ # If :auto_wire is empty, we map outputs to :success and :failure only, for backward compatibility.
90
+ # This is what {Nested} in 2.0 used to do, where the outcome could only be true/false (or success/failure).
91
+ return STATIC_OUTPUTS if @known_activities.empty?
92
+
93
+ # Merge activity#outputs from all given auto_wirable activities to wire up for this dynamic task.
94
+ @known_activities.map do |activity|
95
+ # TODO: Replace this when it's helper gets added.
96
+ Hash[activity.to_h[:outputs].collect{ |output| [output.semantic, output] }]
97
+ end.inject(:merge)
98
+ end
73
99
  end
74
100
  end
75
101
  end
@@ -8,35 +8,36 @@ module Trailblazer::Macro
8
8
  @path = path
9
9
  end
10
10
 
11
- # incoming low-level {Task API}.
11
+ # incoming low-level {circuit interface}.
12
12
  # outgoing Task::Binary API.
13
- def call((options, flow_options), **circuit_options)
14
- condition = options[@path] # this allows dependency injection.
15
- result = condition.([options, flow_options], **circuit_options)
13
+ #
14
+ # Retrieve the injectable {condition}, execute it and interpret its {Result} object.
15
+ def call((ctx, flow_options), **circuit_options)
16
+ condition = ctx[@path] # this allows dependency injection.
17
+ result = condition.([ctx, flow_options], **circuit_options)
16
18
 
17
- options[:"policy.#{@name}"] = result[:policy] # assign the policy as a skill.
18
- options[:"result.policy.#{@name}"] = result
19
+ ctx[:"policy.#{@name}"] = result[:policy] # assign the policy as a ctx variable.
20
+ ctx[:"result.policy.#{@name}"] = result
19
21
 
20
22
  # flow control
21
- signal = result.success? ? Trailblazer::Activity::Right : Trailblazer::Activity::Left # since we & this, it's only executed OnRight and the return boolean decides the direction, input is passed straight through.
23
+ signal = result.success? ? Trailblazer::Activity::Right : Trailblazer::Activity::Left
22
24
 
23
- return signal, [options, flow_options]
25
+ return signal, [ctx, flow_options]
24
26
  end
25
27
  end
26
28
 
27
- # Adds the `yield` result to the pipe and treats it like a
29
+ # Adds the `yield` result to the Railway and treats it like a
28
30
  # policy-compatible object at runtime.
29
- def self.step(condition, options, &block)
30
- name = options[:name]
31
+ def self.step(condition, name: nil, &block)
31
32
  path = :"policy.#{name}.eval"
32
-
33
33
  task = Eval.new(name: name, path: path)
34
34
 
35
- injection = Trailblazer::Activity::TaskWrap::Inject::Defaults::Extension(
36
- path => condition
37
- )
35
+ injections = {
36
+ # :"policy.default.eval"
37
+ path => ->(*) { condition }
38
+ }
38
39
 
39
- {task: task, id: path, extensions: [injection]}
40
+ {task: task, id: path, inject: [injections]}
40
41
  end
41
42
  end
42
43
  end
@@ -28,11 +28,8 @@ module Trailblazer
28
28
  # TODO: remove me in 2.2.
29
29
  module Rescue
30
30
  def self.deprecate_positional_handler_signature(handler)
31
- return handler if handler.is_a?(Symbol) # can't do nutting about this.
32
-
33
- arity = handler.is_a?(Class) ? handler.method(:call).arity : handler.arity
34
-
35
- return handler if arity != 2 # means (exception, (ctx, flow_options), *, &block), "new style"
31
+ return handler if handler.is_a?(Symbol) # can't do nothing about this.
32
+ return handler if handler.method(:call).arity != 2 # means (exception, (ctx, flow_options), *, &block), "new style"
36
33
 
37
34
  ->(exception, (ctx, flow_options), **circuit_options, &block) do
38
35
  warn "[Trailblazer] Rescue handlers have a new signature: (exception, *, &block)"
@@ -1,7 +1,7 @@
1
1
  module Trailblazer
2
2
  module Version
3
3
  module Macro
4
- VERSION = "2.1.4"
4
+ VERSION = "2.1.9"
5
5
  end
6
6
  end
7
7
  end
@@ -48,7 +48,7 @@ module Trailblazer
48
48
 
49
49
  def call((ctx, flow_options), **circuit_options)
50
50
  block_calling_wrapped = -> {
51
- call_wrapped_activity([ctx, flow_options], circuit_options)
51
+ call_wrapped_activity([ctx, flow_options], **circuit_options)
52
52
  }
53
53
 
54
54
  # call the user's Wrap {} block in the operation.
@@ -108,6 +108,27 @@ class DocsGuardNamedTest < Minitest::Spec
108
108
  }
109
109
  end
110
110
 
111
+ #---
112
+ # dependency injection
113
+ class DocsGuardInjectionTest < Minitest::Spec
114
+ #:di-op
115
+ class Create < Trailblazer::Operation
116
+ step Policy::Guard( ->(options, current_user:, **) { current_user == Module } )
117
+ end
118
+ #:di-op end
119
+
120
+ it { Create.(:current_user => Module).inspect("").must_equal %{<Result:true [nil] >} }
121
+ it {
122
+ result =
123
+ #:di-call
124
+ Create.(
125
+ :current_user => Module,
126
+ :"policy.default.eval" => Trailblazer::Operation::Policy::Guard.build(->(options, **) { false })
127
+ )
128
+ #:di-call end
129
+ result.inspect("").must_equal %{<Result:false [nil] >} }
130
+ end
131
+
111
132
  #---
112
133
  # missing current_user throws exception
113
134
  class DocsGuardMissingKeywordTest < Minitest::Spec
@@ -16,11 +16,22 @@ class DocsModelTest < Minitest::Spec
16
16
 
17
17
  #:op
18
18
  class Create < Trailblazer::Operation
19
- step Model( Song, :new )
19
+ step Model(Song, :new)
20
20
  # ..
21
21
  end
22
22
  #:op end
23
23
 
24
+
25
+ it "defaults {:params} to empty hash when not passed" do
26
+ result = Create.({})
27
+ assert_equal true, result.success?
28
+ assert_equal %{#<struct DocsModelTest::Song id=nil, title=nil>}, result[:model].inspect
29
+
30
+ result = Update.({})
31
+ assert_equal false, result.success?
32
+ assert_equal "nil", result[:model].inspect
33
+ end
34
+
24
35
  it do
25
36
  #:create
26
37
  result = Create.(params: {})
@@ -121,4 +132,43 @@ class DocsModelTest < Minitest::Spec
121
132
  result.success?.must_equal true
122
133
  result[:model].inspect.must_equal %{#<struct DocsModelTest::Song id=100, title=nil>}
123
134
  end
135
+
136
+ it "allows injecting {:model.class} and friends" do
137
+ class Hit < Song
138
+ end
139
+
140
+ #:di-model-class
141
+ result = Create.(params: {}, :"model.class" => Hit)
142
+ #:di-model-class end
143
+
144
+ result[:model].inspect.must_equal %{#<struct DocsModelTest::Hit id=nil, title=nil>}
145
+
146
+ # inject all variables
147
+ #:di-all
148
+ result = Create.(
149
+ params: {title: "Olympia"}, # some random variable.
150
+ "model.class": Hit,
151
+ "model.action": :find_by,
152
+ "model.find_by_key": :title
153
+ )
154
+ #:di-all end
155
+
156
+ result[:model].inspect.must_equal %{#<struct DocsModelTest::Hit id=2, title="Olympia">}
157
+
158
+ # use empty Model() and inject {model.class} and {model.action}
159
+ module A
160
+ #:op-model-empty
161
+ class Create < Trailblazer::Operation
162
+ step Model()
163
+ # ..
164
+ end
165
+ #:op-model-empty end
166
+ end # A
167
+
168
+ result = A::Create.(params: {}, :"model.class" => Hit)
169
+
170
+ result[:model].inspect.must_equal %{#<struct DocsModelTest::Hit id=nil, title=nil>}
171
+
172
+
173
+ end
124
174
  end
@@ -22,6 +22,11 @@ class NestedInput < Minitest::Spec
22
22
  include T.def_steps(:validate)
23
23
  end
24
24
 
25
+ class JsonValidate < Validate
26
+ step :json
27
+ include T.def_steps(:json)
28
+ end
29
+
25
30
  it "Nested(Edit), without any options" do
26
31
  module A
27
32
 
@@ -79,11 +84,6 @@ class NestedInput < Minitest::Spec
79
84
  #~meths end
80
85
  end
81
86
  #:nested-dynamic end
82
-
83
- class JsonValidate < Validate
84
- step :json
85
- include T.def_steps(:json)
86
- end
87
87
  # `edit` and `update` can be called from Nested()
88
88
 
89
89
  # edit/success
@@ -119,11 +119,6 @@ class NestedInput < Minitest::Spec
119
119
  end
120
120
  #:nested-dynamic end
121
121
 
122
- class JsonValidate < Validate
123
- step :json
124
- include T.def_steps(:json)
125
- end
126
-
127
122
  # `edit` and `update` can be called from Nested()
128
123
  end
129
124
 
@@ -161,29 +156,34 @@ class NestedInput < Minitest::Spec
161
156
  end
162
157
  end
163
158
 
164
- let(:compute_edit) {
165
- ->(ctx, what:, **) { what }
166
- }
167
-
168
- it "Nested(:method), :pass_fast => :fail_fast doesn't work with standard wiring" do
169
- skip "we need to allow adding :outputs"
159
+ it "Nested(:method, auto_wire: *activities) with :pass_fast => End()" do
160
+ module E
161
+ class JsonValidate < Trailblazer::Operation
162
+ step :validate, Output(:success) => End(:json_validate)
163
+ include T.def_steps(:validate)
164
+ end
170
165
 
171
- compute_edit = self.compute_edit
166
+ #:nested-with-auto-wire
167
+ class Create < Trailblazer::Operation
168
+ step :create
169
+ step Nested(:compute_nested, auto_wire: [Validate, JsonValidate]),
170
+ Output(:json_validate) => End(:jsoned)
172
171
 
173
- pass_fast = Class.new(Trailblazer::Operation) do
174
- step :p, pass_fast: true
175
- include T.def_steps(:p)
176
- end
172
+ #~meths
173
+ def compute_nested(ctx, what:, **)
174
+ what
175
+ end
177
176
 
178
- create = Class.new(Trailblazer::Operation) do
179
- step :a
180
- step Nested(compute_edit, auto_wire: [pass_fast]), Output(:pass_fast) => Track(:fail_fast)
181
- step :b
182
- include T.def_steps(:a, :b)
183
- end
177
+ include T.def_steps(:create)
178
+ #~meths end
179
+ end
180
+ #:nested-with-auto-wire end
184
181
 
182
+ result = Create.(seq: [], what: JsonValidate)
185
183
 
186
- create.(seq: [], what: pass_fast).inspect(:seq).must_equal %{<Result:false [[:a, :c]] >}
184
+ result.inspect(:seq).must_equal %{<Result:false [[:create, :validate]] >}
185
+ result.event.inspect.must_equal %{#<Trailblazer::Activity::End semantic=:jsoned>}
186
+ end
187
187
  end
188
188
  end
189
189
 
@@ -66,7 +66,7 @@ plain Rescue()
66
66
  =begin
67
67
  Rescue( handler: X )
68
68
  =end
69
- class RescueWithHandlerTest < Minitest::Spec
69
+ class RescueWithClassHandlerTest < Minitest::Spec
70
70
  Memo = Class.new
71
71
 
72
72
  #:rescue-handler
@@ -97,10 +97,35 @@ Rescue( handler: X )
97
97
  it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq, :exception_class).must_equal %{<Result:false [[:find_model, :update, :rehash, :log_error], RuntimeError] >} }
98
98
  end
99
99
 
100
+ class RescueWithModuleHandlerTest < Minitest::Spec
101
+ Memo = Class.new
102
+
103
+ module MyHandler
104
+ def self.call(exception, (ctx), *)
105
+ ctx[:exception_class] = exception.class
106
+ end
107
+ end
108
+
109
+ class Memo::Create < Trailblazer::Operation
110
+ step :find_model
111
+ step Rescue( RuntimeError, handler: MyHandler ) {
112
+ step :update
113
+ step :rehash
114
+ }
115
+ step :notify
116
+ fail :log_error
117
+ include T.def_steps(:find_model, :update, :notify, :log_error)
118
+ include Rehash
119
+ end
120
+
121
+ it { Memo::Create.( { seq: [], } ).inspect(:seq, :exception_class).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify], nil] >} }
122
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq, :exception_class).must_equal %{<Result:false [[:find_model, :update, :rehash, :log_error], RuntimeError] >} }
123
+ end
124
+
100
125
  =begin
101
126
  Rescue( handler: :instance_method )
102
127
  =end
103
- class RescueWithHandlerTest < Minitest::Spec
128
+ class RescueWithMethodHandlerTest < Minitest::Spec
104
129
  Memo = Class.new
105
130
 
106
131
  #:rescue-method
@@ -0,0 +1,76 @@
1
+ require 'test_helper'
2
+
3
+ class NestedTest < Minitest::Spec
4
+ DatabaseError = Class.new(Trailblazer::Activity::Signal)
5
+
6
+ class SignUp < Trailblazer::Operation
7
+ def self.b(ctx, **)
8
+ ctx[:seq] << :b
9
+ return DatabaseError
10
+ end
11
+
12
+ step method(:b), Output(DatabaseError, :db_error) => End(:db_error)
13
+ end
14
+
15
+ class SignIn < Trailblazer::Operation
16
+ include T.def_steps(:c)
17
+ step :c
18
+ end
19
+
20
+ it "allows connection with custom output of a nested activity" do
21
+ create = Class.new(Trailblazer::Operation) do
22
+ include T.def_steps(:a, :d)
23
+
24
+ step :a
25
+ step Nested(SignUp), Output(:db_error) => Track(:no_user)
26
+ step :d, magnetic_to: :no_user
27
+ end
28
+
29
+ result = create.(seq: [])
30
+ result.inspect(:seq).must_equal %{<Result:true [[:a, :b, :d]] >}
31
+ result.event.inspect.must_equal %{#<Trailblazer::Activity::Railway::End::Success semantic=:success>}
32
+ end
33
+
34
+ it "allows connecting dynamically nested activities with custom output when auto wired" do
35
+ create = Class.new(Trailblazer::Operation) do
36
+ def compute_nested(ctx, what:, **)
37
+ what
38
+ end
39
+
40
+ include T.def_steps(:a, :d)
41
+
42
+ step :a
43
+ step Nested(:compute_nested, auto_wire: [SignUp, SignIn]), Output(:db_error) => Track(:no_user)
44
+ step :d, magnetic_to: :no_user
45
+ end
46
+
47
+ result = create.(seq: [], what: SignUp)
48
+ result.inspect(:seq).must_equal %{<Result:true [[:a, :b, :d]] >}
49
+ result.event.inspect.must_equal %{#<Trailblazer::Activity::Railway::End::Success semantic=:success>}
50
+ end
51
+
52
+ it "raises RuntimeError if dynamically nested activities with custom output are not auto wired" do
53
+ exception = assert_raises RuntimeError do
54
+ Class.new(Trailblazer::Operation) do
55
+ def compute_nested(ctx, what:, **)
56
+ what
57
+ end
58
+
59
+ step Nested(:compute_nested), Output(:db_error) => Track(:no_user)
60
+ end
61
+ end
62
+
63
+ exception.inspect.must_match 'No `db_error` output found'
64
+ end
65
+
66
+ it "shows warning if `Nested()` is being used instead of `Subprocess()` for static activities" do
67
+ _, warnings = capture_io do
68
+ Class.new(Trailblazer::Operation) do
69
+ step Nested(SignUp)
70
+ end
71
+ end
72
+
73
+ warnings.must_equal %Q{[Trailblazer]#{__FILE__}: Using the `Nested()` macro with operations and activities is deprecated. Replace `Nested(NestedTest::SignUp)` with `Subprocess(NestedTest::SignUp)`.
74
+ }
75
+ end
76
+ end
@@ -25,9 +25,8 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency "roar"
26
26
  spec.add_development_dependency "trailblazer-developer"
27
27
 
28
- spec.add_dependency "trailblazer-activity", ">= 0.11.2", "< 1.0.0"
29
- spec.add_dependency "trailblazer-activity-dsl-linear", ">= 0.3.3", "< 1.0.0"
30
- spec.add_dependency "trailblazer-operation", ">= 0.6.5" # TODO: this dependency will be removed.
28
+ spec.add_dependency "trailblazer-activity-dsl-linear", ">= 0.5.0", "< 0.6.0"
29
+ spec.add_dependency "trailblazer-operation", ">= 0.7.0" # TODO: this dependency will be removed.
31
30
 
32
31
  spec.required_ruby_version = ">= 2.2.0"
33
32
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trailblazer-macro
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.4
4
+ version: 2.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  - Marc Tich
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-12-06 00:00:00.000000000 Z
12
+ date: 2021-12-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -95,60 +95,40 @@ dependencies:
95
95
  - - ">="
96
96
  - !ruby/object:Gem::Version
97
97
  version: '0'
98
- - !ruby/object:Gem::Dependency
99
- name: trailblazer-activity
100
- requirement: !ruby/object:Gem::Requirement
101
- requirements:
102
- - - ">="
103
- - !ruby/object:Gem::Version
104
- version: 0.11.2
105
- - - "<"
106
- - !ruby/object:Gem::Version
107
- version: 1.0.0
108
- type: :runtime
109
- prerelease: false
110
- version_requirements: !ruby/object:Gem::Requirement
111
- requirements:
112
- - - ">="
113
- - !ruby/object:Gem::Version
114
- version: 0.11.2
115
- - - "<"
116
- - !ruby/object:Gem::Version
117
- version: 1.0.0
118
98
  - !ruby/object:Gem::Dependency
119
99
  name: trailblazer-activity-dsl-linear
120
100
  requirement: !ruby/object:Gem::Requirement
121
101
  requirements:
122
102
  - - ">="
123
103
  - !ruby/object:Gem::Version
124
- version: 0.3.3
104
+ version: 0.5.0
125
105
  - - "<"
126
106
  - !ruby/object:Gem::Version
127
- version: 1.0.0
107
+ version: 0.6.0
128
108
  type: :runtime
129
109
  prerelease: false
130
110
  version_requirements: !ruby/object:Gem::Requirement
131
111
  requirements:
132
112
  - - ">="
133
113
  - !ruby/object:Gem::Version
134
- version: 0.3.3
114
+ version: 0.5.0
135
115
  - - "<"
136
116
  - !ruby/object:Gem::Version
137
- version: 1.0.0
117
+ version: 0.6.0
138
118
  - !ruby/object:Gem::Dependency
139
119
  name: trailblazer-operation
140
120
  requirement: !ruby/object:Gem::Requirement
141
121
  requirements:
142
122
  - - ">="
143
123
  - !ruby/object:Gem::Version
144
- version: 0.6.5
124
+ version: 0.7.0
145
125
  type: :runtime
146
126
  prerelease: false
147
127
  version_requirements: !ruby/object:Gem::Requirement
148
128
  requirements:
149
129
  - - ">="
150
130
  - !ruby/object:Gem::Version
151
- version: 0.6.5
131
+ version: 0.7.0
152
132
  description: Macros for Trailblazer's operation
153
133
  email:
154
134
  - apotonick@gmail.com
@@ -157,10 +137,10 @@ executables: []
157
137
  extensions: []
158
138
  extra_rdoc_files: []
159
139
  files:
140
+ - ".github/workflows/ci.yml"
160
141
  - ".gitignore"
161
142
  - ".rubocop.yml"
162
143
  - ".rubocop_todo.yml"
163
- - ".travis.yml"
164
144
  - CHANGES.md
165
145
  - Gemfile
166
146
  - LICENSE
@@ -185,6 +165,7 @@ files:
185
165
  - test/docs/wrap_test.rb
186
166
  - test/operation/integration_test.rb
187
167
  - test/operation/model_test.rb
168
+ - test/operation/nested_test.rb
188
169
  - test/operation/pundit_test.rb
189
170
  - test/test_helper.rb
190
171
  - trailblazer-macro.gemspec
@@ -192,7 +173,7 @@ homepage: http://trailblazer.to
192
173
  licenses:
193
174
  - LGPL-3.0
194
175
  metadata: {}
195
- post_install_message:
176
+ post_install_message:
196
177
  rdoc_options: []
197
178
  require_paths:
198
179
  - lib
@@ -207,8 +188,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
207
188
  - !ruby/object:Gem::Version
208
189
  version: '0'
209
190
  requirements: []
210
- rubygems_version: 3.0.8
211
- signing_key:
191
+ rubygems_version: 3.2.3
192
+ signing_key:
212
193
  specification_version: 4
213
194
  summary: 'Macros for Trailblazer''s operation: Policy, Wrap, Rescue and more.'
214
195
  test_files:
@@ -221,5 +202,6 @@ test_files:
221
202
  - test/docs/wrap_test.rb
222
203
  - test/operation/integration_test.rb
223
204
  - test/operation/model_test.rb
205
+ - test/operation/nested_test.rb
224
206
  - test/operation/pundit_test.rb
225
207
  - test/test_helper.rb
data/.travis.yml DELETED
@@ -1,12 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - ruby-head
4
- - 2.7
5
- - 2.6
6
- - 2.5
7
- - 2.4
8
- cache: bundler
9
- jobs:
10
- allow_failures:
11
- - rvm: ruby-head
12
- - rvm: 2.7