trailblazer-macro 2.1.6 → 2.1.10.beta1

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: d789d88096949c41a15147feed363efab5c86e586b4f76c819e12f6e8f2d4a70
4
- data.tar.gz: fbe7775b4330bc7b607058da66c896fe461e73b1337e747c23eb87acb04b8d30
3
+ metadata.gz: 4e07fee7d3fb35d6b830f020d3d3881be004a64ca3d6a82a9dce8ed314d732f5
4
+ data.tar.gz: e8a2f00963f34d5ac031e80df423700148110afd1f3ae7132837992de8542e5b
5
5
  SHA512:
6
- metadata.gz: df7f0de0c1107ea8c9b3803e8343907c0deb78e943ac2298a114db9fedb6de88252f5055a247b516fa3c80a2432f52913fd6d84ceb189779b58c87da6204a20f
7
- data.tar.gz: a1e7a11851d02b26badfbff8aa6932810bc2e32ae3d1f4ce5fd73f2bf26284bff62a88b1fd27d29f5b82a7962e61fd0655b97f483483ff6df5cffdb3326f51e0
6
+ metadata.gz: 2d304e5385fa03e95a51db017f7d53bb7afb6d9e6e52f90869f01642d3c40848c9cd4abc327daf5a7bfb8ede5d3266f6a73c704fd742fe55e7ee7a37f7634571
7
+ data.tar.gz: 971ffebfbcd3ef1ba280bc53efef3983ea489568aafa85e4e53089dda7401c2cfd988aea0d0f6cc425a523ecd8a1372ae4b502f21d15d20f49345ffd486ab6c4
@@ -6,7 +6,7 @@ jobs:
6
6
  fail-fast: false
7
7
  matrix:
8
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]
9
+ ruby: [2.5, 2.6, 2.7, '3.0', "3.1", head, jruby]
10
10
  runs-on: ubuntu-latest
11
11
  steps:
12
12
  - uses: actions/checkout@v2
data/CHANGES.md CHANGED
@@ -1,3 +1,23 @@
1
+ # 2.1.10
2
+
3
+ * In `Nested()`, we no longer use `Railway::End::Success` and `Railway::End::Failure` as static outputs but
4
+ simply use `Railway`'s default termini objects.
5
+ * Use `dsl`'s new `Inject()` API instead of a overriding `:inject` in `Model()` and `Policy()`.
6
+
7
+ # 2.1.9
8
+
9
+ * Allow omitting `:params` when calling the operation by defaulting it to `{}` in `Model()`.
10
+ * Use `dsl-linear` 0.5.0 and above, which allows removing `Inject()` uses.
11
+
12
+ # 2.1.8
13
+
14
+ Yanked due to inconsistency.
15
+
16
+ # 2.1.7
17
+
18
+ * Improve `Nested()` warning message.
19
+ * Fix exception in `Rescue()` macro when it's handler is a `Module`.
20
+
1
21
  # 2.1.6
2
22
 
3
23
  * Allow connecting ends of the dynamically selected activity for `Nested()` using `:auto_wire`.
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
@@ -8,7 +8,6 @@ module Trailblazer::Macro
8
8
  def self.build(callable)
9
9
  option = Trailblazer::Option(callable)
10
10
 
11
- # this gets wrapped in a Operation::Result object.
12
11
  ->((ctx, *), **circuit_args) do
13
12
  Trailblazer::Operation::Result.new(!!option.call(ctx, keyword_arguments: ctx.to_hash, **circuit_args), {})
14
13
  end
@@ -1,54 +1,60 @@
1
- module Trailblazer::Macro
1
+ module Trailblazer
2
+ module Macro
2
3
 
3
- Linear = Trailblazer::Activity::DSL::Linear
4
+ def self.Model(model_class = nil, action = :new, find_by_key = :id, id: 'model.build', not_found_terminus: false)
5
+ task = Activity::TaskBuilder::Binary(Model.new)
4
6
 
5
- def self.Model(model_class, action = nil, find_by_key = nil, id: 'model.build', not_found_terminus: false)
6
- task = Trailblazer::Activity::TaskBuilder::Binary(Model.new)
7
+ injections = {
8
+ Activity::Railway.Inject() => [:params], # pass-through {:params} if it's in ctx.
7
9
 
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
- )
10
+ # defaulting as per Inject() API.
11
+ Activity::Railway.Inject() => {
12
+ :"model.class" => ->(*) { model_class },
13
+ :"model.action" => ->(*) { action },
14
+ :"model.find_by_key" => ->(*) { find_by_key },
15
+ }
16
+ }
13
17
 
14
- options = { task: task, id: id, extensions: [injection] }
15
- options = options.merge(Linear::Output(:failure) => Linear::End(:not_found)) if not_found_terminus
18
+ options = {task: task, id: id}.merge(injections)
16
19
 
17
- options
18
- end
20
+ options = options.merge(Activity::Railway.Output(:failure) => Activity::Railway.End(:not_found)) if not_found_terminus
19
21
 
20
- class Model
21
- def call(options, params: nil, **)
22
- builder = Model::Builder.new
23
- options[:model] = model = builder.call(options, params)
24
- options[:"result.model"] = result = Trailblazer::Operation::Result.new(!model.nil?, {})
25
-
26
- result.success?
22
+ options
27
23
  end
28
24
 
29
- 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
34
- action = :pass_through unless %i[new find_by].include?(action)
35
-
36
- send("#{action}!", model_class, params, options[:"model.action"], find_by_key)
37
- end
38
-
39
- def new!(model_class, params, *)
40
- model_class.new
41
- end
25
+ class Model
26
+ def call(ctx, params: {}, **)
27
+ builder = Model::Builder.new
28
+ ctx[:model] = model = builder.call(ctx, params)
29
+ ctx[:"result.model"] = result = Operation::Result.new(!model.nil?, {})
42
30
 
43
- # Doesn't throw an exception and will return false to divert to Left.
44
- def find_by!(model_class, params, action, find_by_key, *)
45
- model_class.find_by(find_by_key.to_sym => params[find_by_key])
31
+ result.success?
46
32
  end
47
33
 
48
- # Call any method on the model class and pass find_by_key, for example find(params[:id]).
49
- def pass_through!(model_class, params, action, find_by_key, *)
50
- model_class.send(action, params[find_by_key])
34
+ class Builder
35
+ def call(ctx, params)
36
+ action = ctx[:"model.action"]
37
+ model_class = ctx[:"model.class"]
38
+ find_by_key = ctx[:"model.find_by_key"]
39
+ action = :pass_through unless %i[new find_by].include?(action)
40
+
41
+ send("#{action}!", model_class, params, ctx[:"model.action"], find_by_key)
42
+ end
43
+
44
+ def new!(model_class, params, *)
45
+ model_class.new
46
+ end
47
+
48
+ # Doesn't throw an exception and will return false to divert to Left.
49
+ def find_by!(model_class, params, action, find_by_key, *)
50
+ model_class.find_by(find_by_key.to_sym => params[find_by_key])
51
+ end
52
+
53
+ # Call any method on the model class and pass find_by_key, for example find(params[:id]).
54
+ def pass_through!(model_class, params, action, find_by_key, *)
55
+ model_class.send(action, params[find_by_key])
56
+ end
51
57
  end
52
58
  end
53
- end
59
+ end # Macro
54
60
  end
@@ -4,25 +4,31 @@ module Trailblazer
4
4
  # {Nested} macro.
5
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)`.}
8
- return Nested.operation_class.Subprocess(callable)
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
+
12
+ return Activity::Railway.Subprocess(callable)
9
13
  end
10
14
 
11
- # dynamic
12
- task = Nested::Dynamic.new(callable, auto_wire: auto_wire)
15
+ task, outputs, compute_legacy_return_signal = Nested.Dynamic(callable, auto_wire: auto_wire)
13
16
 
14
17
  merge = [
15
- [Activity::TaskWrap::Pipeline.method(:insert_before), "task_wrap.call_task", ["Nested.compute_nested_activity", task.method(:compute_nested_activity)]],
16
- [Activity::TaskWrap::Pipeline.method(:insert_after), "task_wrap.call_task", ["Nested.compute_return_signal", task.method(:compute_return_signal)]],
18
+ [task, id: "Nested.compute_nested_activity", prepend: "task_wrap.call_task"],
17
19
  ]
18
20
 
19
- task_wrap_extension = Activity::TaskWrap::Extension(merge: merge)
21
+ if compute_legacy_return_signal
22
+ merge << [compute_legacy_return_signal, id: "Nested.compute_return_signal", append: "task_wrap.call_task"]
23
+ end
24
+
25
+ task_wrap_extension = Activity::TaskWrap::Extension::WrapStatic.new(extension: Activity::TaskWrap::Extension(*merge))
20
26
 
21
27
  {
22
28
  task: task,
23
29
  id: id,
24
30
  extensions: [task_wrap_extension],
25
- outputs: task.outputs,
31
+ outputs: outputs,
26
32
  }
27
33
  end
28
34
 
@@ -41,57 +47,64 @@ module Trailblazer
41
47
  # by passing their list to {:auto_wire} option.
42
48
  #
43
49
  # step Nested(:compute_nested, auto_wire: [Create, Update])
44
- class Dynamic
45
- STATIC_OUTPUTS = {
46
- :success => Activity::Output(Activity::Railway::End::Success.new(semantic: :success), :success),
47
- :failure => Activity::Output(Activity::Railway::End::Failure.new(semantic: :failure), :failure),
48
- }
49
-
50
- def initialize(nested_activity_decider, auto_wire: [])
51
- @nested_activity_decider = Trailblazer::Option(nested_activity_decider)
52
- @known_activities = Array(auto_wire)
53
- @outputs = compute_task_outputs
50
+ def self.Dynamic(nested_activity_decider, auto_wire:)
51
+ if auto_wire.empty?
52
+ is_legacy = true # no auto_wire means we need to compute the legacy return signal.
53
+ auto_wire = [Class.new(Activity::Railway)]
54
54
  end
55
55
 
56
- attr_reader :outputs
56
+ outputs = outputs_for(auto_wire)
57
+ task = Dynamic.new(nested_activity_decider)
58
+ compute_legacy_return_signal = Dynamic::ComputeLegacyReturnSignal.new(outputs) if is_legacy
59
+
60
+ return task, outputs, compute_legacy_return_signal
61
+ end
62
+
63
+ # Go through the list of all possible nested activities and compile the total sum of possible outputs.
64
+ # FIXME: WHAT IF WE HAVE TWO IDENTICALLY NAMED OUTPUTS?
65
+ # @private
66
+ def self.outputs_for(activities)
67
+ activities.map do |activity|
68
+ Activity::Railway.Subprocess(activity)[:outputs]
69
+ end.inject(:merge)
70
+ end
71
+
72
+ class Dynamic
73
+ def initialize(nested_activity_decider)
74
+ @nested_activity_decider = Trailblazer::Option(nested_activity_decider)
75
+ end
57
76
 
58
77
  # TaskWrap step.
59
- def compute_nested_activity(wrap_ctx, original_args)
78
+ def call(wrap_ctx, original_args)
60
79
  (ctx, _), original_circuit_options = original_args
61
80
 
62
81
  # TODO: evaluate the option to get the actual "object" to call.
63
82
  activity = @nested_activity_decider.(ctx, keyword_arguments: ctx.to_hash, **original_circuit_options)
64
83
 
65
84
  # Overwrite :task so task_wrap.call_task will call this activity.
66
- # This is a trick so we don't have to repeat logic from #call_task here.
85
+ # This is a taskWrap trick so we don't have to repeat logic from #call_task here.
67
86
  wrap_ctx[:task] = activity
68
87
 
69
88
  return wrap_ctx, original_args
70
89
  end
71
90
 
72
- def compute_return_signal(wrap_ctx, original_args)
73
- # NOOP when @known_activities are present as all possible signals have been registered already.
74
- if @known_activities.empty?
75
- # Translate the genuine nested signal to the generic Dynamic end (success/failure, only).
76
- # Note that here we lose information about what specific event was emitted.
77
- wrap_ctx[:return_signal] = wrap_ctx[:return_signal].kind_of?(Activity::Railway::End::Success) ?
78
- @outputs[:success].signal : @outputs[:failure].signal
91
+ # TODO: remove me when we make {:auto_wire} mandatory.
92
+ class ComputeLegacyReturnSignal
93
+ SUCCESS_SEMANTICS = [:success, :pass_fast] # TODO: make this injectable/or get it from operation.
94
+
95
+ def initialize(outputs)
96
+ @outputs = outputs # not needed for auto_wire!
79
97
  end
80
98
 
81
- return wrap_ctx, original_args
82
- end
99
+ def call(wrap_ctx, original_args)
100
+ actual_semantic = wrap_ctx[:return_signal].to_h[:semantic]
101
+ applied_semantic = SUCCESS_SEMANTICS.include?(actual_semantic) ? :success : :failure
83
102
 
84
- private def compute_task_outputs
85
- # If :auto_wire is empty, we map outputs to :success and :failure only, for backward compatibility.
86
- # This is what {Nested} in 2.0 used to do, where the outcome could only be true/false (or success/failure).
87
- return STATIC_OUTPUTS if @known_activities.empty?
103
+ wrap_ctx[:return_signal] = @outputs.fetch(applied_semantic).signal
88
104
 
89
- # Merge activity#outputs from all given auto_wirable activities to wire up for this dynamic task.
90
- @known_activities.map do |activity|
91
- # TODO: Replace this when it's helper gets added.
92
- Hash[activity.to_h[:outputs].collect{ |output| [output.semantic, output] }]
93
- end.inject(:merge)
94
- end
105
+ return wrap_ctx, original_args
106
+ end
107
+ end # ComputeLegacyReturnSignal
95
108
  end
96
109
  end
97
110
  end
@@ -8,35 +8,38 @@ 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
+ Trailblazer::Activity::Railway.Inject() => {
37
+ # :"policy.default.eval"
38
+ path => ->(*) { condition }
39
+ }
40
+ }
38
41
 
39
- {task: task, id: path, extensions: [injection]}
42
+ {task: task, id: path}.merge(injections)
40
43
  end
41
44
  end
42
45
  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.6"
4
+ VERSION = "2.1.10.beta1"
5
5
  end
6
6
  end
7
7
  end
@@ -1,5 +1,4 @@
1
1
  require "forwardable"
2
- require "trailblazer/activity"
3
2
  require "trailblazer/activity/dsl/linear"
4
3
  require "trailblazer/operation" # TODO: remove this dependency
5
4
 
@@ -17,12 +16,13 @@ module Trailblazer
17
16
 
18
17
  # All macros sit in the {Trailblazer::Macro} namespace, where we forward calls from
19
18
  # operations and activities to.
19
+
20
20
  module Activity::DSL::Linear::Helper
21
- Policy = Trailblazer::Macro::Policy
21
+ Constants::Policy = Trailblazer::Macro::Policy
22
22
 
23
- module ClassMethods
24
- extend Forwardable
25
- def_delegators Trailblazer::Macro, :Model, :Nested, :Wrap, :Rescue
26
- end # ClassMethods
23
+ # Extending the {Linear::Helper} namespace is the canonical way to import
24
+ # macros into Railway, FastTrack, Operation, etc.
25
+ extend Forwardable
26
+ def_delegators Trailblazer::Macro, :Model, :Nested, :Wrap, :Rescue
27
27
  end # Helper
28
28
  end
@@ -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
@@ -151,7 +151,8 @@ class NestedInput < Minitest::Spec
151
151
  end
152
152
  #:nested-with-pass-fast end
153
153
 
154
- # pass fast
154
+ #= {#save} is still called because the {End.pass_fast} terminus is automatically wired to
155
+ #= the success "output" of Nested().
155
156
  create.(seq: []).inspect(:seq).must_equal %{<Result:true [[:create, :just_pass_fast, :save]] >}
156
157
  end
157
158
  end
@@ -159,15 +160,16 @@ class NestedInput < Minitest::Spec
159
160
  it "Nested(:method, auto_wire: *activities) with :pass_fast => End()" do
160
161
  module E
161
162
  class JsonValidate < Trailblazer::Operation
162
- step :validate, Output(:success) => End(:json_validate)
163
- include T.def_steps(:validate)
163
+ step :validate, Output(:failure) => End(:invalid_json)
164
+ step :save
165
+ include T.def_steps(:validate, :save)
164
166
  end
165
167
 
166
168
  #:nested-with-auto-wire
167
169
  class Create < Trailblazer::Operation
168
170
  step :create
169
171
  step Nested(:compute_nested, auto_wire: [Validate, JsonValidate]),
170
- Output(:json_validate) => End(:jsoned)
172
+ Output(:invalid_json) => End(:jsoned)
171
173
 
172
174
  #~meths
173
175
  def compute_nested(ctx, what:, **)
@@ -179,10 +181,36 @@ class NestedInput < Minitest::Spec
179
181
  end
180
182
  #:nested-with-auto-wire end
181
183
 
182
- result = Create.(seq: [], what: JsonValidate)
184
+
185
+ #@ nested {JsonValidate} ends on {End.success}
186
+ result = Create.(seq: [], what: JsonValidate, validate: true)
187
+
188
+ result.inspect(:seq).must_equal %{<Result:true [[:create, :validate, :save]] >}
189
+ result.event.inspect.must_equal %{#<Trailblazer::Activity::Railway::End::Success semantic=:success>}
190
+
191
+ #@ nested {JsonValidate} ends on {End.invalid_json} because validate fails.
192
+ result = Create.(seq: [], what: JsonValidate, validate: false)
183
193
 
184
194
  result.inspect(:seq).must_equal %{<Result:false [[:create, :validate]] >}
185
195
  result.event.inspect.must_equal %{#<Trailblazer::Activity::End semantic=:jsoned>}
196
+
197
+ #@ nested {JsonValidate} ends on {End.failure} because save fails.
198
+ result = Create.(seq: [], what: JsonValidate, save: false)
199
+
200
+ result.inspect(:seq).must_equal %{<Result:false [[:create, :validate, :save]] >}
201
+ result.event.inspect.must_equal %{#<Trailblazer::Activity::Railway::End::Failure semantic=:failure>}
202
+
203
+ #@ nested {Validate} ends on {End.failure} because validate fails.
204
+ result = Create.(seq: [], what: Validate, validate: false)
205
+
206
+ result.inspect(:seq).must_equal %{<Result:false [[:create, :validate]] >}
207
+ result.event.inspect.must_equal %{#<Trailblazer::Activity::Railway::End::Failure semantic=:failure>}
208
+
209
+ #@ nested {Validate} ends on {End.success}.
210
+ result = Create.(seq: [], what: Validate)
211
+
212
+ result.inspect(:seq).must_equal %{<Result:true [[:create, :validate]] >}
213
+ result.event.inspect.must_equal %{#<Trailblazer::Activity::Railway::End::Success semantic=:success>}
186
214
  end
187
215
  end
188
216
  end
@@ -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
@@ -49,6 +49,28 @@ class NestedTest < Minitest::Spec
49
49
  result.event.inspect.must_equal %{#<Trailblazer::Activity::Railway::End::Success semantic=:success>}
50
50
  end
51
51
 
52
+ #@ unit test
53
+ # TODO: make me a non-Operation test.
54
+ it "allows using multiple Nested() per operation" do
55
+ create = Class.new(Trailblazer::Operation) do
56
+ def compute_nested(ctx, what:, **)
57
+ what
58
+ end
59
+
60
+ step Nested(:compute_nested)
61
+ step Nested(:compute_nested), id: :compute_nested_again
62
+ end
63
+
64
+ #@ we want both Nested executed
65
+ result = create.(seq: [], what: SignIn)
66
+ result.inspect(:seq).must_equal %{<Result:true [[:c, :c]] >}
67
+ result.event.inspect.must_equal %{#<Trailblazer::Activity::Railway::End::Success semantic=:success>}
68
+
69
+ result = create.wtf?(seq: [], what: SignUp)
70
+ result.inspect(:seq).must_equal %{<Result:false [[:b]] >}
71
+ result.event.inspect.must_equal %{#<Trailblazer::Activity::Railway::End::Failure semantic=:failure>}
72
+ end
73
+
52
74
  it "raises RuntimeError if dynamically nested activities with custom output are not auto wired" do
53
75
  exception = assert_raises RuntimeError do
54
76
  Class.new(Trailblazer::Operation) do
@@ -62,4 +84,15 @@ class NestedTest < Minitest::Spec
62
84
 
63
85
  exception.inspect.must_match 'No `db_error` output found'
64
86
  end
87
+
88
+ it "shows warning if `Nested()` is being used instead of `Subprocess()` for static activities" do
89
+ _, warnings = capture_io do
90
+ Class.new(Trailblazer::Operation) do
91
+ step Nested(SignUp)
92
+ end
93
+ end
94
+
95
+ warnings.must_equal %Q{[Trailblazer]#{__FILE__}: Using the `Nested()` macro with operations and activities is deprecated. Replace `Nested(NestedTest::SignUp)` with `Subprocess(NestedTest::SignUp)`.
96
+ }
97
+ end
65
98
  end
@@ -25,8 +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-dsl-linear", ">= 0.4.0", "< 0.5.0"
29
- spec.add_dependency "trailblazer-operation", ">= 0.7.0" # TODO: this dependency will be removed.
28
+ spec.add_dependency "trailblazer-activity-dsl-linear", ">= 1.0.0.beta1", "< 1.1.0"
29
+ spec.add_dependency "trailblazer-operation", ">= 0.8.0.beta1" # TODO: this dependency will be removed. currently needed for tests?!
30
30
 
31
31
  spec.required_ruby_version = ">= 2.2.0"
32
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.6
4
+ version: 2.1.10.beta1
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: 2021-03-11 00:00:00.000000000 Z
12
+ date: 2022-07-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -101,34 +101,34 @@ dependencies:
101
101
  requirements:
102
102
  - - ">="
103
103
  - !ruby/object:Gem::Version
104
- version: 0.4.0
104
+ version: 1.0.0.beta1
105
105
  - - "<"
106
106
  - !ruby/object:Gem::Version
107
- version: 0.5.0
107
+ version: 1.1.0
108
108
  type: :runtime
109
109
  prerelease: false
110
110
  version_requirements: !ruby/object:Gem::Requirement
111
111
  requirements:
112
112
  - - ">="
113
113
  - !ruby/object:Gem::Version
114
- version: 0.4.0
114
+ version: 1.0.0.beta1
115
115
  - - "<"
116
116
  - !ruby/object:Gem::Version
117
- version: 0.5.0
117
+ version: 1.1.0
118
118
  - !ruby/object:Gem::Dependency
119
119
  name: trailblazer-operation
120
120
  requirement: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
- version: 0.7.0
124
+ version: 0.8.0.beta1
125
125
  type: :runtime
126
126
  prerelease: false
127
127
  version_requirements: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - ">="
130
130
  - !ruby/object:Gem::Version
131
- version: 0.7.0
131
+ version: 0.8.0.beta1
132
132
  description: Macros for Trailblazer's operation
133
133
  email:
134
134
  - apotonick@gmail.com
@@ -173,7 +173,7 @@ homepage: http://trailblazer.to
173
173
  licenses:
174
174
  - LGPL-3.0
175
175
  metadata: {}
176
- post_install_message:
176
+ post_install_message:
177
177
  rdoc_options: []
178
178
  require_paths:
179
179
  - lib
@@ -184,12 +184,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
184
184
  version: 2.2.0
185
185
  required_rubygems_version: !ruby/object:Gem::Requirement
186
186
  requirements:
187
- - - ">="
187
+ - - ">"
188
188
  - !ruby/object:Gem::Version
189
- version: '0'
189
+ version: 1.3.1
190
190
  requirements: []
191
191
  rubygems_version: 3.2.3
192
- signing_key:
192
+ signing_key:
193
193
  specification_version: 4
194
194
  summary: 'Macros for Trailblazer''s operation: Policy, Wrap, Rescue and more.'
195
195
  test_files: