trailblazer 2.1.0.beta4 → 2.1.0.beta5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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