trailblazer-macro 2.1.9 → 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: 639ab06bc834f5258e404d9c87e1e53db434884225e6f6c222afcb9df9597154
4
- data.tar.gz: b5f08c21b83529cd8a12fed2e6ef3e638a9db546b2d57d651f3a9f9adec8ff3c
3
+ metadata.gz: 4e07fee7d3fb35d6b830f020d3d3881be004a64ca3d6a82a9dce8ed314d732f5
4
+ data.tar.gz: e8a2f00963f34d5ac031e80df423700148110afd1f3ae7132837992de8542e5b
5
5
  SHA512:
6
- metadata.gz: ebf4969c76b10be6b3e842376ebbb6bb5fd73bd7a3c8fe1f6c61ab1efb9e8d945b3bb13404fcf6cdbead3590deb5cf9aeaf0150a32209d14107e2019981732ed
7
- data.tar.gz: 5d485adedac0da50b68f3528ddaf80329f9d33a06cd6768e7a7c1d430c4f7ff05bf5daafbf14eb174fd821bbb5d1108150bbb278fbc7eacb960c26fd8f058cca
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,9 @@
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
+
1
7
  # 2.1.9
2
8
 
3
9
  * Allow omitting `:params` when calling the operation by defaulting it to `{}` in `Model()`.
@@ -1,55 +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 = nil, action = :new, find_by_key = :id, 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
- injections = { # defaulting as per `:inject` API.
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, inject: [:params, injections]} # pass-through {:params} if it's in ctx.
18
+ options = {task: task, id: id}.merge(injections)
15
19
 
16
- options = options.merge(Linear::Output(:failure) => Linear::End(:not_found)) if not_found_terminus
20
+ options = options.merge(Activity::Railway.Output(:failure) => Activity::Railway.End(:not_found)) if not_found_terminus
17
21
 
18
- options
19
- end
20
-
21
- class Model
22
- def call(ctx, params: {}, **)
23
- builder = Model::Builder.new
24
- ctx[:model] = model = builder.call(ctx, params)
25
- ctx[:"result.model"] = result = Trailblazer::Operation::Result.new(!model.nil?, {})
26
-
27
- result.success?
22
+ options
28
23
  end
29
24
 
30
- class Builder
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"]
35
- action = :pass_through unless %i[new find_by].include?(action)
36
-
37
- send("#{action}!", model_class, params, ctx[:"model.action"], find_by_key)
38
- end
39
-
40
- def new!(model_class, params, *)
41
- model_class.new
42
- 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?, {})
43
30
 
44
- # Doesn't throw an exception and will return false to divert to Left.
45
- def find_by!(model_class, params, action, find_by_key, *)
46
- model_class.find_by(find_by_key.to_sym => params[find_by_key])
31
+ result.success?
47
32
  end
48
33
 
49
- # Call any method on the model class and pass find_by_key, for example find(params[:id]).
50
- def pass_through!(model_class, params, action, find_by_key, *)
51
- 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
52
57
  end
53
58
  end
54
- end
59
+ end # Macro
55
60
  end
@@ -9,24 +9,26 @@ module Trailblazer
9
9
  "Using the `Nested()` macro with operations and activities is deprecated. " \
10
10
  "Replace `Nested(#{callable})` with `Subprocess(#{callable})`."
11
11
 
12
- return Nested.operation_class.Subprocess(callable)
12
+ return Activity::Railway.Subprocess(callable)
13
13
  end
14
14
 
15
- # dynamic
16
- task = Nested::Dynamic.new(callable, auto_wire: auto_wire)
15
+ task, outputs, compute_legacy_return_signal = Nested.Dynamic(callable, auto_wire: auto_wire)
17
16
 
18
17
  merge = [
19
- [Activity::TaskWrap::Pipeline.method(:insert_before), "task_wrap.call_task", ["Nested.compute_nested_activity", task.method(:compute_nested_activity)]],
20
- [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"],
21
19
  ]
22
20
 
23
- 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))
24
26
 
25
27
  {
26
28
  task: task,
27
29
  id: id,
28
30
  extensions: [task_wrap_extension],
29
- outputs: task.outputs,
31
+ outputs: outputs,
30
32
  }
31
33
  end
32
34
 
@@ -45,57 +47,64 @@ module Trailblazer
45
47
  # by passing their list to {:auto_wire} option.
46
48
  #
47
49
  # step Nested(:compute_nested, auto_wire: [Create, Update])
48
- class Dynamic
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
- }
53
-
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
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)]
58
54
  end
59
55
 
60
- 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
61
76
 
62
77
  # TaskWrap step.
63
- def compute_nested_activity(wrap_ctx, original_args)
78
+ def call(wrap_ctx, original_args)
64
79
  (ctx, _), original_circuit_options = original_args
65
80
 
66
81
  # TODO: evaluate the option to get the actual "object" to call.
67
82
  activity = @nested_activity_decider.(ctx, keyword_arguments: ctx.to_hash, **original_circuit_options)
68
83
 
69
84
  # Overwrite :task so task_wrap.call_task will call this activity.
70
- # 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.
71
86
  wrap_ctx[:task] = activity
72
87
 
73
88
  return wrap_ctx, original_args
74
89
  end
75
90
 
76
- def compute_return_signal(wrap_ctx, original_args)
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
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!
83
97
  end
84
98
 
85
- return wrap_ctx, original_args
86
- 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
87
102
 
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?
103
+ wrap_ctx[:return_signal] = @outputs.fetch(applied_semantic).signal
92
104
 
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
105
+ return wrap_ctx, original_args
106
+ end
107
+ end # ComputeLegacyReturnSignal
99
108
  end
100
109
  end
101
110
  end
@@ -33,11 +33,13 @@ module Trailblazer::Macro
33
33
  task = Eval.new(name: name, path: path)
34
34
 
35
35
  injections = {
36
- # :"policy.default.eval"
37
- path => ->(*) { condition }
36
+ Trailblazer::Activity::Railway.Inject() => {
37
+ # :"policy.default.eval"
38
+ path => ->(*) { condition }
39
+ }
38
40
  }
39
41
 
40
- {task: task, id: path, inject: [injections]}
42
+ {task: task, id: path}.merge(injections)
41
43
  end
42
44
  end
43
45
  end
@@ -1,7 +1,7 @@
1
1
  module Trailblazer
2
2
  module Version
3
3
  module Macro
4
- VERSION = "2.1.9"
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
@@ -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
@@ -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
@@ -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.5.0", "< 0.6.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,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trailblazer-macro
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.9
4
+ version: 2.1.10.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-12-13 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.5.0
104
+ version: 1.0.0.beta1
105
105
  - - "<"
106
106
  - !ruby/object:Gem::Version
107
- version: 0.6.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.5.0
114
+ version: 1.0.0.beta1
115
115
  - - "<"
116
116
  - !ruby/object:Gem::Version
117
- version: 0.6.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
@@ -184,9 +184,9 @@ 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
192
  signing_key: