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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +194 -502
- data/CHANGES.md +4 -0
- data/CONTRIBUTING.md +170 -0
- data/Gemfile +4 -1
- data/README.md +183 -40
- data/Rakefile +6 -2
- data/lib/trailblazer/version.rb +1 -1
- data/lib/trailblazer.rb +3 -12
- data/test/{operation/dsl → dsl}/contract_test.rb +2 -2
- data/trailblazer.gemspec +3 -3
- metadata +17 -63
- data/lib/trailblazer/operation/contract.rb +0 -82
- data/lib/trailblazer/operation/guard.rb +0 -18
- data/lib/trailblazer/operation/model.rb +0 -65
- data/lib/trailblazer/operation/nested.rb +0 -91
- data/lib/trailblazer/operation/persist.rb +0 -14
- data/lib/trailblazer/operation/policy.rb +0 -44
- data/lib/trailblazer/operation/pundit.rb +0 -38
- data/lib/trailblazer/operation/representer.rb +0 -36
- data/lib/trailblazer/operation/rescue.rb +0 -24
- data/lib/trailblazer/operation/validate.rb +0 -74
- data/lib/trailblazer/operation/wrap.rb +0 -64
- data/test/docs/contract_test.rb +0 -545
- data/test/docs/dry_test.rb +0 -31
- data/test/docs/guard_test.rb +0 -162
- data/test/docs/macro_test.rb +0 -36
- data/test/docs/model_test.rb +0 -75
- data/test/docs/nested_test.rb +0 -300
- data/test/docs/policy_test.rb +0 -2
- data/test/docs/pundit_test.rb +0 -133
- data/test/docs/representer_test.rb +0 -268
- data/test/docs/rescue_test.rb +0 -154
- data/test/docs/wrap_test.rb +0 -219
- data/test/nested_test.rb +0 -293
- data/test/operation/contract_test.rb +0 -290
- data/test/operation/dsl/representer_test.rb +0 -169
- data/test/operation/model_test.rb +0 -54
- data/test/operation/persist_test.rb +0 -51
- data/test/operation/pundit_test.rb +0 -106
- 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.
|
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-
|
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.
|
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.
|
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-
|
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.
|
39
|
+
version: 2.1.0.beta2
|
40
40
|
- - "<"
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version:
|
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.
|
49
|
+
version: 2.1.0.beta2
|
50
50
|
- - "<"
|
51
51
|
- !ruby/object:Gem::Version
|
52
|
-
version:
|
52
|
+
version: 2.2.0
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
|
-
name:
|
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.
|
59
|
+
version: 2.1.0.beta2
|
60
60
|
- - "<"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version:
|
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.
|
69
|
+
version: 2.1.0.beta2
|
70
70
|
- - "<"
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version:
|
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/
|
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/
|
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
|