trailblazer 2.1.0.beta4 → 2.1.0.beta5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +194 -502
  3. data/CHANGES.md +4 -0
  4. data/CONTRIBUTING.md +170 -0
  5. data/Gemfile +4 -1
  6. data/README.md +183 -40
  7. data/Rakefile +6 -2
  8. data/lib/trailblazer/version.rb +1 -1
  9. data/lib/trailblazer.rb +3 -12
  10. data/test/{operation/dsl → dsl}/contract_test.rb +2 -2
  11. data/trailblazer.gemspec +3 -3
  12. metadata +17 -63
  13. data/lib/trailblazer/operation/contract.rb +0 -82
  14. data/lib/trailblazer/operation/guard.rb +0 -18
  15. data/lib/trailblazer/operation/model.rb +0 -65
  16. data/lib/trailblazer/operation/nested.rb +0 -91
  17. data/lib/trailblazer/operation/persist.rb +0 -14
  18. data/lib/trailblazer/operation/policy.rb +0 -44
  19. data/lib/trailblazer/operation/pundit.rb +0 -38
  20. data/lib/trailblazer/operation/representer.rb +0 -36
  21. data/lib/trailblazer/operation/rescue.rb +0 -24
  22. data/lib/trailblazer/operation/validate.rb +0 -74
  23. data/lib/trailblazer/operation/wrap.rb +0 -64
  24. data/test/docs/contract_test.rb +0 -545
  25. data/test/docs/dry_test.rb +0 -31
  26. data/test/docs/guard_test.rb +0 -162
  27. data/test/docs/macro_test.rb +0 -36
  28. data/test/docs/model_test.rb +0 -75
  29. data/test/docs/nested_test.rb +0 -300
  30. data/test/docs/policy_test.rb +0 -2
  31. data/test/docs/pundit_test.rb +0 -133
  32. data/test/docs/representer_test.rb +0 -268
  33. data/test/docs/rescue_test.rb +0 -154
  34. data/test/docs/wrap_test.rb +0 -219
  35. data/test/nested_test.rb +0 -293
  36. data/test/operation/contract_test.rb +0 -290
  37. data/test/operation/dsl/representer_test.rb +0 -169
  38. data/test/operation/model_test.rb +0 -54
  39. data/test/operation/persist_test.rb +0 -51
  40. data/test/operation/pundit_test.rb +0 -106
  41. data/test/operation/representer_test.rb +0 -254
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trailblazer
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0.beta4
4
+ version: 2.1.0.beta5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-02 00:00:00.000000000 Z
11
+ date: 2018-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: trailblazer-operation
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.2.3
19
+ version: 0.2.4
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: 0.3.0
@@ -26,50 +26,50 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 0.2.3
29
+ version: 0.2.4
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: 0.3.0
33
33
  - !ruby/object:Gem::Dependency
34
- name: trailblazer-activity
34
+ name: trailblazer-macro
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: 0.4.3
39
+ version: 2.1.0.beta2
40
40
  - - "<"
41
41
  - !ruby/object:Gem::Version
42
- version: 0.6.0
42
+ version: 2.2.0
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
46
46
  requirements:
47
47
  - - ">="
48
48
  - !ruby/object:Gem::Version
49
- version: 0.4.3
49
+ version: 2.1.0.beta2
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
- version: 0.6.0
52
+ version: 2.2.0
53
53
  - !ruby/object:Gem::Dependency
54
- name: reform
54
+ name: trailblazer-macro-contract
55
55
  requirement: !ruby/object:Gem::Requirement
56
56
  requirements:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
- version: 2.2.0
59
+ version: 2.1.0.beta2
60
60
  - - "<"
61
61
  - !ruby/object:Gem::Version
62
- version: 3.0.0
62
+ version: 2.2.0
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - ">="
68
68
  - !ruby/object:Gem::Version
69
- version: 2.2.0
69
+ version: 2.1.0.beta2
70
70
  - - "<"
71
71
  - !ruby/object:Gem::Version
72
- version: 3.0.0
72
+ version: 2.2.0
73
73
  - !ruby/object:Gem::Dependency
74
74
  name: declarative
75
75
  requirement: !ruby/object:Gem::Requirement
@@ -182,6 +182,7 @@ files:
182
182
  - ".travis.yml"
183
183
  - CHANGES.md
184
184
  - COMM-LICENSE
185
+ - CONTRIBUTING.md
185
186
  - Gemfile
186
187
  - LICENSE.txt
187
188
  - README.md
@@ -195,54 +196,25 @@ files:
195
196
  - lib/trailblazer/deprecation/context.rb
196
197
  - lib/trailblazer/dsl.rb
197
198
  - lib/trailblazer/operation/auto_inject.rb
198
- - lib/trailblazer/operation/contract.rb
199
199
  - lib/trailblazer/operation/deprecations.rb
200
- - lib/trailblazer/operation/guard.rb
201
200
  - lib/trailblazer/operation/input_output.rb
202
- - lib/trailblazer/operation/model.rb
203
201
  - lib/trailblazer/operation/module.rb
204
- - lib/trailblazer/operation/nested.rb
205
- - lib/trailblazer/operation/persist.rb
206
- - lib/trailblazer/operation/policy.rb
207
- - lib/trailblazer/operation/pundit.rb
208
- - lib/trailblazer/operation/representer.rb
209
- - lib/trailblazer/operation/rescue.rb
210
202
  - lib/trailblazer/operation/test.rb
211
- - lib/trailblazer/operation/validate.rb
212
- - lib/trailblazer/operation/wrap.rb
213
203
  - lib/trailblazer/task.rb
214
204
  - lib/trailblazer/version.rb
215
205
  - test/benchmark.rb
216
206
  - test/deprecation/call_test.rb
217
207
  - test/deprecation/context_test.rb
218
208
  - test/docs/auto_inject_test.rb
219
- - test/docs/contract_test.rb
220
- - test/docs/dry_test.rb
221
209
  - test/docs/fast_test.rb
222
- - test/docs/guard_test.rb
223
210
  - test/docs/input_output_test.rb
224
- - test/docs/macro_test.rb
225
- - test/docs/model_test.rb
226
- - test/docs/nested_test.rb
227
211
  - test/docs/operation_test.rb
228
- - test/docs/policy_test.rb
229
- - test/docs/pundit_test.rb
230
- - test/docs/representer_test.rb
231
- - test/docs/rescue_test.rb
232
212
  - test/docs/trace_test.rb
233
- - test/docs/wrap_test.rb
213
+ - test/dsl/contract_test.rb
234
214
  - test/gemfiles/Gemfile.ruby-1.9
235
215
  - test/gemfiles/Gemfile.ruby-2.0
236
216
  - test/gemfiles/Gemfile.ruby-2.3
237
217
  - test/module_test.rb
238
- - test/nested_test.rb
239
- - test/operation/contract_test.rb
240
- - test/operation/dsl/contract_test.rb
241
- - test/operation/dsl/representer_test.rb
242
- - test/operation/model_test.rb
243
- - test/operation/persist_test.rb
244
- - test/operation/pundit_test.rb
245
- - test/operation/representer_test.rb
246
218
  - test/test_helper.rb
247
219
  - test/variables_test.rb
248
220
  - trailblazer.gemspec
@@ -276,32 +248,14 @@ test_files:
276
248
  - test/deprecation/call_test.rb
277
249
  - test/deprecation/context_test.rb
278
250
  - test/docs/auto_inject_test.rb
279
- - test/docs/contract_test.rb
280
- - test/docs/dry_test.rb
281
251
  - test/docs/fast_test.rb
282
- - test/docs/guard_test.rb
283
252
  - test/docs/input_output_test.rb
284
- - test/docs/macro_test.rb
285
- - test/docs/model_test.rb
286
- - test/docs/nested_test.rb
287
253
  - test/docs/operation_test.rb
288
- - test/docs/policy_test.rb
289
- - test/docs/pundit_test.rb
290
- - test/docs/representer_test.rb
291
- - test/docs/rescue_test.rb
292
254
  - test/docs/trace_test.rb
293
- - test/docs/wrap_test.rb
255
+ - test/dsl/contract_test.rb
294
256
  - test/gemfiles/Gemfile.ruby-1.9
295
257
  - test/gemfiles/Gemfile.ruby-2.0
296
258
  - test/gemfiles/Gemfile.ruby-2.3
297
259
  - test/module_test.rb
298
- - test/nested_test.rb
299
- - test/operation/contract_test.rb
300
- - test/operation/dsl/contract_test.rb
301
- - test/operation/dsl/representer_test.rb
302
- - test/operation/model_test.rb
303
- - test/operation/persist_test.rb
304
- - test/operation/pundit_test.rb
305
- - test/operation/representer_test.rb
306
260
  - test/test_helper.rb
307
261
  - test/variables_test.rb
@@ -1,82 +0,0 @@
1
- # Best practices for using contract.
2
- #
3
- # * inject contract instance via constructor to #contract
4
- # * allow contract setup and memo via #contract(model, options)
5
- # * allow implicit automatic setup via #contract and class.contract_class
6
- #
7
- # Needs Operation#model.
8
- # Needs #[], #[]= skill dependency.
9
- module Trailblazer
10
- class Operation
11
- module Contract
12
- def self.Build(name: "default", constant: nil, builder: nil)
13
- task = ->((options, flow_options), **circuit_options) do
14
- result = Build.(options, circuit_options, name: name, constant: constant, builder: builder)
15
-
16
- return Activity::TaskBuilder::Binary.binary_direction_for( result, Activity::Right, Activity::Left ),
17
- [options, flow_options]
18
- end
19
-
20
- { task: task, id: "contract.build" }
21
- end
22
-
23
- module Build
24
- # Build contract at runtime.
25
- def self.call(options, circuit_options, name: "default", constant: nil, builder: nil)
26
- # TODO: we could probably clean this up a bit at some point.
27
- contract_class = constant || options["contract.#{name}.class"] # DISCUSS: Injection possible here?
28
- model = options[:model]
29
- name = "contract.#{name}"
30
-
31
- options[name] =
32
- if builder
33
- call_builder( options, circuit_options, builder: builder, constant: contract_class, name: name )
34
- else
35
- contract_class.new(model)
36
- end
37
- end
38
-
39
- def self.call_builder(options, circuit_options, builder:raise, constant:raise, name:raise)
40
- # builder_options = Trailblazer::Context( options, constant: constant, name: name ) # options.merge( .. )
41
-
42
- # Trailblazer::Option::KW(builder).(builder_options, circuit_options)
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
- # FIXME: almost identical with Option::KW.
52
- # FIXME: see Nested::Options::Dynamic, the same shit
53
- tmp_options = options.to_hash.merge(
54
- constant: constant,
55
- name: name
56
- )
57
-
58
- Trailblazer::Option(builder).( options, tmp_options, circuit_options )
59
- end
60
- end
61
-
62
- module DSL
63
- def self.extended(extender)
64
- extender.extend(ClassDependencies)
65
- warn "[Trailblazer] Using `contract do...end` is deprecated. Please use a form class and the Builder( constant: <Form> ) option."
66
- end
67
- # This is the class level DSL method.
68
- # Op.contract #=> returns contract class
69
- # Op.contract do .. end # defines contract
70
- # Op.contract CommentForm # copies (and subclasses) external contract.
71
- # Op.contract CommentForm do .. end # copies and extends contract.
72
- def contract(name=:default, constant=nil, base: Reform::Form, &block)
73
- heritage.record(:contract, name, constant, &block)
74
-
75
- path, form_class = Trailblazer::DSL::Build.new.({ prefix: :contract, class: base, container: self }, name, constant, block)
76
-
77
- self[path] = form_class
78
- end
79
- end # Contract
80
- end
81
- end
82
- end
@@ -1,18 +0,0 @@
1
- class Trailblazer::Operation
2
- module Policy
3
- def self.Guard(proc, name: :default, &block)
4
- Policy.step( Guard.build(proc), name: name )
5
- end
6
-
7
- module Guard
8
- def self.build(callable)
9
- option = Trailblazer::Option::KW(callable)
10
-
11
- # this gets wrapped in a Operation::Result object.
12
- ->( (options, *), circuit_args ) do
13
- Result.new( !!option.(options, circuit_args), {} )
14
- end
15
- end
16
- end # Guard
17
- end
18
- end
@@ -1,65 +0,0 @@
1
- class Trailblazer::Operation
2
- def self.Model(model_class, action=nil)
3
- # step = Pipetree::Step.new(step, "model.class" => model_class, "model.action" => action)
4
-
5
- task = Trailblazer::Activity::TaskBuilder::Binary.( Model.new )
6
-
7
- extension = Trailblazer::Activity::TaskWrap::Merge.new(
8
- Wrap::Inject::Defaults(
9
- "model.class" => model_class,
10
- "model.action" => action
11
- )
12
- )
13
-
14
- { task: task, id: "model.build", extension: [extension] }
15
- end
16
-
17
- class Model
18
- def call(options, params:, **)
19
- builder = Model::Builder.new
20
-
21
- options[:model] = model = builder.(options, params)
22
-
23
- options["result.model"] = result = Result.new(!model.nil?, {})
24
-
25
- result.success?
26
- end
27
-
28
- class Builder
29
- def call(options, params)
30
- deprecate_update!(options)
31
- action = options["model.action"] || :new
32
- model_class = options["model.class"]
33
-
34
- action = :pass_through unless [:new, :find_by, :find].include?(action)
35
-
36
- send("#{action}!", model_class, params, options["model.action"])
37
- end
38
-
39
- def new!(model_class, params, *)
40
- model_class.new
41
- end
42
-
43
- def find!(model_class, params, *)
44
- model_class.find(params[:id])
45
- end
46
-
47
- # Doesn't throw an exception and will return false to divert to Left.
48
- def find_by!(model_class, params, *)
49
- model_class.find_by(id: params[:id])
50
- end
51
-
52
- # Call any method on the model class and pass :id.
53
- def pass_through!(model_class, params, action)
54
- model_class.send(action, params[:id])
55
- end
56
-
57
- private
58
- def deprecate_update!(options) # TODO: remove in 2.1.
59
- return unless options["model.action"] == :update
60
- options["model.action"] = :find
61
- warn "[Trailblazer] Model( .., :update ) is deprecated, please use :find or :find_by."
62
- end
63
- end
64
- end
65
- end
@@ -1,91 +0,0 @@
1
- # per default, everything we pass into a circuit is immutable. it's the ops/act's job to allow writing (via a Context)
2
- module Trailblazer
3
- class Operation
4
- # {Nested} macro.
5
- def self.Nested(callable, input:nil, output:nil, id: "Nested(#{callable})")
6
- task_wrap_extensions = Module.new do
7
- extend Activity::Path::Plan()
8
- end
9
-
10
- task, operation, is_dynamic = Nested.build(callable)
11
-
12
- if input || output # TODO: move this to the generic step DSL
13
- task_wrap_extensions = InputOutput.plan( input, output )
14
- end
15
-
16
- if is_dynamic
17
- # task_wrap_extensions += Activity::Magnetic::Builder::Path.plan do
18
- task_wrap_extensions.task task.method(:compute_nested_activity), id: ".compute_nested_activity", after: "Start.default", group: :start
19
- task_wrap_extensions.task task.method(:compute_return_signal), id: ".compute_return_signal", after: "task_wrap.call_task"
20
- # end
21
- end
22
-
23
- {
24
- task: task,
25
- id: id,
26
- extension: [ Trailblazer::Activity::TaskWrap::Merge.new(task_wrap_extensions) ],
27
- outputs: operation.outputs
28
- }
29
- end
30
-
31
- # @private
32
- module Nested
33
- def self.build(nested_operation) # DISCUSS: use builders here?
34
- return dynamic = Dynamic.new(nested_operation), dynamic, true unless nestable_object?(nested_operation)
35
-
36
- # The returned {Nested} instance is a valid circuit element and will be `call`ed in the circuit.
37
- # It simply returns the nested activity's `signal,options,flow_options` return set.
38
- # The actual wiring - where to go with that - is done by the step DSL.
39
- return Trailblazer::Operation::Callable(nested_operation, call: :__call__), nested_operation, false
40
- end
41
-
42
- def self.nestable_object?(object)
43
- object.is_a?( Trailblazer::Activity::Interface )
44
- end
45
-
46
- def self.operation_class
47
- Operation
48
- end
49
-
50
- private
51
-
52
- # For dynamic `Nested`s that do not expose an {Activity} interface.
53
- # Since we do not know its outputs, we have to map them to :success and :failure, only.
54
- #
55
- # This is what {Nested} in 2.0 used to do, where the outcome could only be true/false (or success/failure).
56
- class Dynamic
57
- def initialize(nested_activity)
58
- @nested_activity = Trailblazer::Option::KW(nested_activity)
59
- @outputs = {
60
- :success => Activity::Output( Railway::End::Success.new(semantic: :success), :success ),
61
- :failure => Activity::Output( Railway::End::Failure.new(semantic: :failure), :failure ),
62
- }
63
- end
64
-
65
- attr_reader :outputs
66
-
67
- # TaskWrap step.
68
- def compute_nested_activity( (wrap_ctx, original_args), **circuit_options )
69
- (ctx, _), original_circuit_options = original_args
70
-
71
- activity = @nested_activity.( ctx, original_circuit_options ) # evaluate the option to get the actual "object" to call.
72
-
73
- # overwrite :task so task_wrap.call_task will call this activity. This is a trick so we don't have to repeat
74
- # logic from #call_task here.
75
- wrap_ctx[:task] = Trailblazer::Operation::Callable( activity, call: :__call__ )
76
-
77
- return Activity::Right, [ wrap_ctx, original_args ]
78
- end
79
-
80
- def compute_return_signal( (wrap_ctx, original_args), **circuit_options )
81
- # Translate the genuine nested signal to the generic Dynamic end (success/failure, only).
82
- # Note that here we lose information about what specific event was emitted.
83
- wrap_ctx[:return_signal] = wrap_ctx[:return_signal].kind_of?(Railway::End::Success) ?
84
- @outputs[:success].signal : @outputs[:failure].signal
85
-
86
- return Activity::Right, [ wrap_ctx, original_args ]
87
- end
88
- end
89
- end
90
- end # Operation
91
- end
@@ -1,14 +0,0 @@
1
- module Trailblazer
2
- class Operation
3
- module Contract
4
- def self.Persist(method: :save, name: "default")
5
- path = "contract.#{name}"
6
- step = ->(options, **) { options[path].send(method) }
7
-
8
- task = Activity::TaskBuilder::Binary.( step )
9
-
10
- { task: task, id: "persist.save" }
11
- end
12
- end
13
- end
14
- end
@@ -1,44 +0,0 @@
1
- class Trailblazer::Operation
2
- module Policy
3
- # Step: This generically `call`s a policy and then pushes its result to `options`.
4
- # You can use any callable object as a policy with this step.
5
- class Eval
6
- def initialize(name:nil, path:nil)
7
- @name = name
8
- @path = path
9
- end
10
-
11
- # incoming low-level {Task API}.
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 )
16
-
17
- options["policy.#{@name}"] = result["policy"] # assign the policy as a skill.
18
- options["result.policy.#{@name}"] = result
19
-
20
- # 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.
22
-
23
- return signal, [ options, flow_options ]
24
- end
25
- end
26
-
27
- # Adds the `yield` result to the pipe and treats it like a
28
- # policy-compatible object at runtime.
29
- def self.step(condition, options, &block)
30
- name = options[:name]
31
- path = "policy.#{name}.eval"
32
-
33
- task = Eval.new( name: name, path: path )
34
-
35
- extension = Trailblazer::Activity::TaskWrap::Merge.new(
36
- Trailblazer::Operation::Wrap::Inject::Defaults(
37
- path => condition
38
- )
39
- )
40
-
41
- { task: task, id: path, extension: [extension] }
42
- end
43
- end
44
- end
@@ -1,38 +0,0 @@
1
- class Trailblazer::Operation
2
- module Policy
3
- def self.Pundit(policy_class, action, name: :default)
4
- Policy.step( Pundit.build(policy_class, action), name: name )
5
- end
6
-
7
- module Pundit
8
- def self.build(*args, &block)
9
- Condition.new(*args, &block)
10
- end
11
-
12
- # Pundit::Condition is invoked at runtime when iterating the pipe.
13
- class Condition
14
- def initialize(policy_class, action)
15
- @policy_class, @action = policy_class, action
16
- end
17
-
18
- # Instantiate the actual policy object, and call it.
19
- def call((options), *)
20
- policy = build_policy(options) # this translates to Pundit interface.
21
- result!(policy.send(@action), policy)
22
- end
23
-
24
- private
25
- def build_policy(options)
26
- @policy_class.new(options[:current_user], options[:model])
27
- end
28
-
29
- def result!(success, policy)
30
- data = { "policy" => policy }
31
- data["message"] = "Breach" if !success # TODO: how to allow messages here?
32
-
33
- Result.new(success, data)
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,36 +0,0 @@
1
- class Trailblazer::Operation
2
- module Representer
3
- def self.infer(contract_class, format:Representable::JSON)
4
- Disposable::Rescheme.from(contract_class,
5
- include: [format],
6
- options_from: :deserializer, # use :instance etc. in deserializer.
7
- superclass: Representable::Decorator,
8
- definitions_from: lambda { |inline| inline.definitions },
9
- exclude_options: [:default, :populator], # TODO: test with populator: in an operation.
10
- exclude_properties: [:persisted?]
11
- )
12
- end
13
-
14
- module DSL
15
- def self.extended(extender)
16
- extender.extend(ClassDependencies)
17
- warn "[Trailblazer] Using `representer do...end` is deprecated. Please use a dedicated representer class."
18
- end
19
-
20
- def representer(name=:default, constant=nil, &block)
21
- heritage.record(:representer, name, constant, &block)
22
-
23
- # FIXME: make this nicer. we want to extend same-named callback groups.
24
- # TODO: allow the same with contract, or better, test it!
25
- path, representer_class = Trailblazer::DSL::Build.new.({ prefix: :representer, class: representer_base_class, container: self }, name, constant, block)
26
-
27
- self[path] = representer_class
28
- end
29
-
30
- # TODO: make engine configurable?
31
- def representer_base_class
32
- Class.new(Representable::Decorator) { include Representable::JSON; self }
33
- end
34
- end
35
- end
36
- end
@@ -1,24 +0,0 @@
1
- class Trailblazer::Operation
2
- NoopHandler = lambda { |*| }
3
-
4
- def self.Rescue(*exceptions, handler: NoopHandler, &block)
5
- exceptions = [StandardError] unless exceptions.any?
6
- handler = Trailblazer::Option(handler)
7
-
8
- # This block is evaluated by {Wrap} which currently expects a binary return type.
9
- rescue_block = ->(options, flow_options, **circuit_options, &nested_activity) {
10
- begin
11
- nested_activity.call
12
- rescue *exceptions => exception
13
- # DISCUSS: should we deprecate this signature and rather apply the Task API here?
14
- handler.call(exception, options, **circuit_options) # FIXME: when there's an error here, it shows the wrong exception!
15
- false
16
- end
17
- }
18
-
19
- Wrap(rescue_block, id: "Rescue(#{rand(100)})", &block)
20
- # FIXME: name
21
- # [ step, name: "Rescue:#{block.source_location.last}" ]
22
- end
23
- end
24
-
@@ -1,74 +0,0 @@
1
- module Trailblazer
2
- class Operation
3
- module Contract
4
- # result.contract = {..}
5
- # result.contract.errors = {..}
6
- # Deviate to left track if optional key is not found in params.
7
- # Deviate to left if validation result falsey.
8
- def self.Validate(skip_extract: false, name: "default", representer: false, key: nil) # DISCUSS: should we introduce something like Validate::Deserializer?
9
- params_path = "contract.#{name}.params" # extract_params! save extracted params here.
10
-
11
- extract = Validate::Extract.new( key: key, params_path: params_path ).freeze
12
- validate = Validate.new( name: name, representer: representer, params_path: params_path ).freeze
13
-
14
- # Build a simple Railway {Activity} for the internal flow.
15
- activity = Module.new do
16
- extend Activity::Railway()
17
-
18
- step extract, id: "#{params_path}_extract" unless skip_extract || representer
19
- step validate, id: "contract.#{name}.call"
20
- end
21
-
22
- # activity, _ = activity.decompose
23
-
24
- # DISCUSS: use Nested here?
25
- { task: activity, id: "contract.#{name}.validate", outputs: activity.outputs }
26
- end
27
-
28
- class Validate
29
- # Task: extract the contract's input from params by reading `:key`.
30
- class Extract
31
- def initialize(key:nil, params_path:nil)
32
- @key, @params_path = key, params_path
33
- end
34
-
35
- def call( ctx, params:, ** )
36
- ctx[@params_path] = @key ? params[@key] : params
37
- end
38
- end
39
-
40
- def initialize(name:"default", representer:false, params_path:nil)
41
- @name, @representer, @params_path = name, representer, params_path
42
- end
43
-
44
- # Task: Validates contract `:name`.
45
- def call( ctx, ** )
46
- validate!(
47
- ctx,
48
- representer: ctx["representer.#{@name}.class"] ||= @representer, # FIXME: maybe @representer should use DI.
49
- params_path: @params_path
50
- )
51
- end
52
-
53
- def validate!(options, representer:false, from: :document, params_path:nil)
54
- path = "contract.#{@name}"
55
- contract = options[path]
56
-
57
- # this is for 1.1-style compatibility and should be removed once we have Deserializer in place:
58
- options["result.#{path}"] = result =
59
- if representer
60
- # use :document as the body and let the representer deserialize to the contract.
61
- # this will be simplified once we have Deserializer.
62
- # translates to contract.("{document: bla}") { MyRepresenter.new(contract).from_json .. }
63
- contract.(options[from]) { |document| representer.new(contract).parse(document) }
64
- else
65
- # let Reform handle the deserialization.
66
- contract.(options[params_path])
67
- end
68
-
69
- result.success?
70
- end
71
- end
72
- end
73
- end # Operation
74
- end