trailblazer 2.0.0 → 2.0.1
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/CHANGES.md +13 -0
- data/COMM-LICENSE +21 -21
- data/Gemfile +3 -0
- data/README.md +15 -77
- data/Rakefile +1 -2
- data/doc/operation-2017.png +0 -0
- data/lib/trailblazer.rb +0 -1
- data/lib/trailblazer/operation/callback.rb +10 -19
- data/lib/trailblazer/operation/contract.rb +7 -8
- data/lib/trailblazer/operation/guard.rb +4 -13
- data/lib/trailblazer/operation/model.rb +30 -32
- data/lib/trailblazer/operation/nested.rb +21 -32
- data/lib/trailblazer/operation/persist.rb +4 -8
- data/lib/trailblazer/operation/policy.rb +5 -15
- data/lib/trailblazer/operation/pundit.rb +8 -17
- data/lib/trailblazer/operation/rescue.rb +17 -17
- data/lib/trailblazer/operation/validate.rb +34 -21
- data/lib/trailblazer/operation/wrap.rb +12 -25
- data/lib/trailblazer/version.rb +1 -1
- data/test/docs/dry_test.rb +4 -4
- data/test/docs/fast_test.rb +164 -0
- data/test/docs/guard_test.rb +2 -2
- data/test/docs/macro_test.rb +36 -0
- data/test/docs/model_test.rb +52 -0
- data/test/docs/nested_test.rb +101 -4
- data/test/docs/operation_test.rb +225 -1
- data/test/docs/pundit_test.rb +18 -17
- data/test/docs/rescue_test.rb +3 -3
- data/test/docs/wrap_test.rb +1 -0
- data/test/operation/callback_test.rb +5 -5
- data/test/operation/contract_test.rb +30 -19
- data/test/operation/dsl/callback_test.rb +2 -2
- data/test/operation/dsl/contract_test.rb +4 -4
- data/test/operation/model_test.rb +12 -57
- data/test/operation/persist_test.rb +7 -7
- data/test/operation/pipedream_test.rb +9 -9
- data/test/operation/pipetree_test.rb +5 -5
- data/test/operation/pundit_test.rb +7 -7
- data/test/operation/resolver_test.rb +47 -47
- data/trailblazer.gemspec +1 -1
- metadata +18 -11
- data/lib/trailblazer/operation/builder.rb +0 -24
- data/lib/trailblazer/operation/resolver.rb +0 -22
- data/test/controller_test.rb +0 -115
- data/test/operation/builder_test.rb +0 -89
data/test/docs/pundit_test.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
|
-
#:policy
|
4
|
-
class MyPolicy
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
#:policy
|
4
|
+
class MyPolicy
|
5
|
+
def initialize(user, model)
|
6
|
+
@user, @model = user, model
|
7
|
+
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
def create?
|
10
|
+
@user == Module && @model.id.nil?
|
11
|
+
end
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
def new?
|
14
|
+
@user == Class
|
15
|
+
end
|
15
16
|
end
|
16
|
-
end
|
17
|
-
#:policy end
|
17
|
+
#:policy end
|
18
18
|
|
19
19
|
#--
|
20
20
|
# with policy
|
@@ -29,6 +29,7 @@ class DocsPunditProcTest < Minitest::Spec
|
|
29
29
|
end
|
30
30
|
#:pundit end
|
31
31
|
|
32
|
+
it { Create["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>policy.default.eval]} }
|
32
33
|
it { Create.({}, "current_user" => Module).inspect("model").must_equal %{<Result:true [#<struct DocsPunditProcTest::Song id=nil>] >} }
|
33
34
|
it { Create.({} ).inspect("model").must_equal %{<Result:false [#<struct DocsPunditProcTest::Song id=nil>] >} }
|
34
35
|
|
@@ -45,10 +46,10 @@ class DocsPunditProcTest < Minitest::Spec
|
|
45
46
|
#---
|
46
47
|
#- override
|
47
48
|
class New < Create
|
48
|
-
|
49
|
+
step Policy::Pundit( MyPolicy, :new? ), override: true
|
49
50
|
end
|
50
51
|
|
51
|
-
it { New["pipetree"].inspect.must_equal %{[
|
52
|
+
it { New["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>policy.default.eval]} }
|
52
53
|
it { New.({}, "current_user" => Class ).inspect("model").must_equal %{<Result:true [#<struct DocsPunditProcTest::Song id=nil>] >} }
|
53
54
|
it { New.({}, "current_user" => nil ).inspect("model").must_equal %{<Result:false [#<struct DocsPunditProcTest::Song id=nil>] >} }
|
54
55
|
|
@@ -60,12 +61,12 @@ class DocsPunditProcTest < Minitest::Spec
|
|
60
61
|
end
|
61
62
|
|
62
63
|
class Update < Edit
|
63
|
-
|
64
|
+
step Policy::Pundit( MyPolicy, :new?, name: "first" ), override: true
|
64
65
|
end
|
65
66
|
|
66
|
-
it { Edit["pipetree"].inspect.must_equal %{[
|
67
|
+
it { Edit["pipetree"].inspect.must_equal %{[>operation.new,>policy.first.eval,>policy.second.eval]} }
|
67
68
|
it { Edit.({}, "current_user" => Class).inspect("model").must_equal %{<Result:false [nil] >} }
|
68
|
-
it { Update["pipetree"].inspect.must_equal %{[
|
69
|
+
it { Update["pipetree"].inspect.must_equal %{[>operation.new,>policy.first.eval,>policy.second.eval]} }
|
69
70
|
it { Update.({}, "current_user" => Class).inspect("model").must_equal %{<Result:true [nil] >} }
|
70
71
|
|
71
72
|
#---
|
data/test/docs/rescue_test.rb
CHANGED
@@ -17,13 +17,13 @@ class NestedRescueTest < Minitest::Spec
|
|
17
17
|
step ->(options) { options["b"] = true }
|
18
18
|
success ->(options) { raise A if options["raise-a"] }
|
19
19
|
step ->(options) { options["c"] = true }
|
20
|
-
|
20
|
+
failure ->(options) { options["inner-err"] = true }
|
21
21
|
}
|
22
22
|
step ->(options) { options["e"] = true }
|
23
23
|
failure ->(options) { options["outer-err"] = true }
|
24
24
|
end
|
25
25
|
|
26
|
-
it { NestedInsanity["pipetree"].inspect.must_equal %{[
|
26
|
+
it { NestedInsanity["pipetree"].inspect.must_equal %{[>operation.new,>Rescue:10,>rescue_test.rb:22,<rescue_test.rb:23]} }
|
27
27
|
it { NestedInsanity.({}).inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:true [true, true, true, true, true, true, nil, nil] >} }
|
28
28
|
it { NestedInsanity.({}, "raise-y" => true).inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:false [true, true, nil, nil, nil, nil, true, true] >} }
|
29
29
|
it { NestedInsanity.({}, "raise-a" => true).inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:false [true, true, true, true, nil, nil, nil, true] >} }
|
@@ -148,7 +148,7 @@ class RescueTest < Minitest::Spec
|
|
148
148
|
it { assert_raises(RuntimeError) { Create.( id: "RuntimeError!" ) } }
|
149
149
|
it do
|
150
150
|
Create.( id: 1, title: "Pie" )
|
151
|
-
Sequel.result.first.must_equal Pipetree::
|
151
|
+
Sequel.result.first.must_equal Pipetree::Railway::Right
|
152
152
|
end
|
153
153
|
end
|
154
154
|
end
|
data/test/docs/wrap_test.rb
CHANGED
@@ -13,10 +13,10 @@ class OperationCallbackTest < MiniTest::Spec
|
|
13
13
|
property :name
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
step Model( Song, :new )
|
17
|
+
step Contract::Build()
|
18
|
+
step Contract::Validate()
|
19
|
+
step Callback( :default )
|
20
20
|
|
21
21
|
|
22
22
|
extend Callback::DSL
|
@@ -55,7 +55,7 @@ class OperationCallbackTest < MiniTest::Spec
|
|
55
55
|
|
56
56
|
#---
|
57
57
|
#- inheritance
|
58
|
-
it { Update["pipetree"].inspect.must_equal %{[
|
58
|
+
it { Update["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>contract.build,>contract.default.validate,>callback.default]} }
|
59
59
|
|
60
60
|
|
61
61
|
it "invokes all callbacks" do
|
@@ -1,5 +1,16 @@
|
|
1
1
|
require "test_helper"
|
2
|
-
|
2
|
+
|
3
|
+
class ContractExtractMacroTest < Minitest::Spec
|
4
|
+
class Create < Trailblazer::Operation
|
5
|
+
step Contract::Validate::Extract( key: "song", params_path: "x" )
|
6
|
+
end
|
7
|
+
|
8
|
+
it { Create["pipetree"].inspect.must_equal %{[>operation.new,>x]} }
|
9
|
+
it { Create.({}).inspect("x").must_equal %{<Result:false [nil] >} }
|
10
|
+
it { Create.({ "song" => Object }).inspect("x").must_equal %{<Result:true [Object] >} }
|
11
|
+
end
|
12
|
+
|
13
|
+
|
3
14
|
|
4
15
|
require "dry/validation"
|
5
16
|
|
@@ -14,7 +25,7 @@ class DryValidationTest < Minitest::Spec
|
|
14
25
|
# required(:id).filled
|
15
26
|
# end
|
16
27
|
|
17
|
-
|
28
|
+
step :process
|
18
29
|
|
19
30
|
include Procedural::Validate
|
20
31
|
|
@@ -40,7 +51,7 @@ class DryValidationTest < Minitest::Spec
|
|
40
51
|
required(:id).filled
|
41
52
|
end)
|
42
53
|
|
43
|
-
|
54
|
+
step ->(options) { options["contract.params"].(options["params"]).success? }, before: "operation.new"
|
44
55
|
end
|
45
56
|
|
46
57
|
it { Update.( id: 1 ).success?.must_equal true }
|
@@ -71,10 +82,10 @@ class ContractTest < Minitest::Spec
|
|
71
82
|
property :title
|
72
83
|
end
|
73
84
|
|
74
|
-
|
75
|
-
#
|
76
|
-
|
77
|
-
|
85
|
+
success ->(options) { options["model"] = Song.new }
|
86
|
+
# step Model( Song, :new )
|
87
|
+
step Contract::Build()
|
88
|
+
step :process
|
78
89
|
|
79
90
|
include Procedural::Validate
|
80
91
|
# TODO: get model automatically in validate!
|
@@ -216,9 +227,9 @@ class ValidateTest < Minitest::Spec
|
|
216
227
|
end
|
217
228
|
end
|
218
229
|
|
219
|
-
|
220
|
-
|
221
|
-
|
230
|
+
step Model( Song, :new ) # FIXME.
|
231
|
+
step Contract::Build()
|
232
|
+
step :process
|
222
233
|
end
|
223
234
|
|
224
235
|
# validate returns the #validate result
|
@@ -258,9 +269,9 @@ class ValidateTest < Minitest::Spec
|
|
258
269
|
validates :title, presence: true
|
259
270
|
end
|
260
271
|
|
261
|
-
|
262
|
-
|
263
|
-
|
272
|
+
step Model( Song, :new ) # FIXME.
|
273
|
+
step Contract::Build()
|
274
|
+
step Contract::Validate() # generic validate call for you.
|
264
275
|
|
265
276
|
# include Procedural::Validate
|
266
277
|
->(*) { validate(options["params"][:song]) } # <-- TODO
|
@@ -291,9 +302,9 @@ class ValidateTest < Minitest::Spec
|
|
291
302
|
validates :title, presence: true
|
292
303
|
end
|
293
304
|
|
294
|
-
|
295
|
-
|
296
|
-
|
305
|
+
step Model( Song, :new ) # FIXME.
|
306
|
+
step Contract::Build()
|
307
|
+
step Contract::Validate( key: :song) # generic validate call for you.
|
297
308
|
# ->(*) { validate(options["params"][:song]) } # <-- TODO
|
298
309
|
step Contract::Persist( method: :sync )
|
299
310
|
end
|
@@ -315,14 +326,14 @@ class ValidateTest < Minitest::Spec
|
|
315
326
|
class New < Upsert
|
316
327
|
end
|
317
328
|
|
318
|
-
it { New["pipetree"].inspect.must_equal %{[
|
329
|
+
it { New["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>contract.build,>contract.default.validate,>persist.save]} }
|
319
330
|
|
320
331
|
#- overwriting Validate
|
321
332
|
class NewHit < Upsert
|
322
|
-
|
333
|
+
step Contract::Validate( key: :hit ), override: true
|
323
334
|
end
|
324
335
|
|
325
|
-
it { NewHit["pipetree"].inspect.must_equal %{[
|
336
|
+
it { NewHit["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>contract.build,>contract.default.validate,>persist.save]} }
|
326
337
|
it { NewHit.(:hit => { title: "Hooray For Me" }).inspect("model").must_equal %{<Result:true [#<struct ContractTest::Song title=\"Hooray For Me\">] >} }
|
327
338
|
end
|
328
339
|
|
@@ -37,7 +37,7 @@ class DslCallbackTest < MiniTest::Spec
|
|
37
37
|
def admin_default!(*); _invocations << :admin_default!; end
|
38
38
|
def after_save!(*); _invocations << :after_save!; end
|
39
39
|
|
40
|
-
|
40
|
+
step Trailblazer::Operation::Callback[:after_save]
|
41
41
|
end
|
42
42
|
|
43
43
|
def default!(*); _invocations << :default!; end
|
@@ -61,7 +61,7 @@ class DslCallbackTest < MiniTest::Spec
|
|
61
61
|
extend Callback::DSL
|
62
62
|
callback :after_save, AfterSaveCallback
|
63
63
|
|
64
|
-
|
64
|
+
step Callback[:after_save]
|
65
65
|
end
|
66
66
|
|
67
67
|
it { OpWithExternalCallback.("title"=>"Thunder Rising").must_equal([:after_save!]) }
|
@@ -14,10 +14,10 @@ class DslContractTest < MiniTest::Spec
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.included(includer)
|
17
|
-
includer
|
18
|
-
includer
|
19
|
-
includer
|
20
|
-
includer
|
17
|
+
includer.step Trailblazer::Operation::Model( OpenStruct, :new )
|
18
|
+
includer.step Trailblazer::Operation::Contract::Build()
|
19
|
+
includer.step Trailblazer::Operation::Contract::Validate()
|
20
|
+
includer.step Trailblazer::Operation::Contract::Persist( method: :sync )
|
21
21
|
# includer.> ->(op, *) { op["x"] = [] }
|
22
22
|
end
|
23
23
|
end
|
@@ -9,34 +9,27 @@ class ModelTest < Minitest::Spec
|
|
9
9
|
#---
|
10
10
|
# use Model semantics, no customizations.
|
11
11
|
class Create < Trailblazer::Operation
|
12
|
-
|
12
|
+
step Model Song, :new
|
13
13
|
end
|
14
14
|
|
15
15
|
# :new new.
|
16
16
|
it { Create.({})["model"].inspect.must_equal %{#<struct ModelTest::Song id=nil>} }
|
17
17
|
|
18
18
|
class Update < Create
|
19
|
-
|
19
|
+
step Model( Song, :find ), override: true
|
20
20
|
end
|
21
21
|
|
22
22
|
# :find it
|
23
23
|
it { Update.({ id: 1 })["model"].inspect.must_equal %{#<struct ModelTest::Song id=1>} }
|
24
24
|
|
25
25
|
#- inheritance
|
26
|
-
it { Update["pipetree"].inspect.must_equal %{[
|
27
|
-
|
28
|
-
# override #model with Model included.
|
29
|
-
class Upsert < Create
|
30
|
-
def model!(params); params.to_s end
|
31
|
-
end
|
32
|
-
|
33
|
-
it { Upsert.(id: 9)["model"].must_equal %{{:id=>9}} }
|
26
|
+
it { Update["pipetree"].inspect.must_equal %{[>operation.new,>model.build]} }
|
34
27
|
|
35
28
|
#---
|
36
29
|
# :find_by, exceptionless.
|
37
30
|
class Find < Trailblazer::Operation
|
38
|
-
|
39
|
-
|
31
|
+
step Model Song, :find_by
|
32
|
+
step :process
|
40
33
|
|
41
34
|
def process(*); self["x"] = true end
|
42
35
|
end
|
@@ -56,50 +49,12 @@ class ModelTest < Minitest::Spec
|
|
56
49
|
Find.(id: 9)["model"].inspect.must_equal %{#<struct ModelTest::Song id=9>}
|
57
50
|
end
|
58
51
|
|
59
|
-
#---
|
60
|
-
#
|
61
|
-
class
|
62
|
-
|
63
|
-
|
64
|
-
end
|
65
|
-
|
66
|
-
it { Delete.(id: 1)["model"].must_equal %{{:id=>1}} }
|
67
|
-
|
68
|
-
#---
|
69
|
-
# creating the model before operation instantiation (ex Model::External)
|
70
|
-
class Show < Create
|
71
|
-
extend Model::BuildMethods # FIXME: how do we communicate that and prevent the include from Model[] ?
|
72
|
-
self.| Model( Song, :update ), before: "operation.new"
|
73
|
-
end
|
74
|
-
|
75
|
-
it { Show.({id: 1})["model"].inspect.must_equal %{#<struct ModelTest::Song id=1>} }
|
76
|
-
|
77
|
-
|
78
|
-
# TODO: with builder!
|
79
|
-
|
80
|
-
#---
|
81
|
-
# provide your own object ModelBuilder that includes BuilderMethods.
|
82
|
-
# this tests that BuildMethods is interchangable and acts as an example how to decouple
|
83
|
-
# the model building from the operation.
|
84
|
-
class Index < Trailblazer::Operation
|
85
|
-
# DISCUSS: help user to do this kind of behavior?
|
86
|
-
# model Song, :find # ModelBuilder can read this via skills that we pass to it.
|
87
|
-
self["model.class"] = Song
|
88
|
-
self["model.action"] = :find
|
89
|
-
|
90
|
-
# this is to be able to use BuildModel.
|
91
|
-
class ModelBuilder
|
92
|
-
include Trailblazer::Operation::Model::BuildMethods # #instantiate_model and so on.
|
93
|
-
alias_method :call, :model!
|
94
|
-
|
95
|
-
def initialize(skills); @delegator = skills end
|
96
|
-
|
97
|
-
extend Uber::Delegates
|
98
|
-
delegates :@delegator, :[]
|
99
|
-
end
|
100
|
-
|
101
|
-
self.> ->(options) { options["model"] = ModelBuilder.new(options).(options["params"]) }, after: "operation.new"
|
102
|
-
end
|
52
|
+
# #---
|
53
|
+
# # creating the model before operation instantiation (ex Model::External)
|
54
|
+
# class Show < Create
|
55
|
+
# extend Model::BuildMethods # FIXME: how do we communicate that and prevent the include from Model[] ?
|
56
|
+
# step Model( Song, :update ), before: "operation.new"
|
57
|
+
# end
|
103
58
|
|
104
|
-
it {
|
59
|
+
# it { Show.({id: 1})["model"].inspect.must_equal %{#<struct ModelTest::Song id=1>} }
|
105
60
|
end
|
@@ -11,12 +11,12 @@ class PersistTest < Minitest::Spec
|
|
11
11
|
property :title
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
step Model( Song, :new )
|
15
|
+
step Contract::Build()
|
16
|
+
step Contract::Validate()
|
17
|
+
failure ->(options) { options["1. fail"] = "Validate" }
|
18
|
+
step Contract::Persist()
|
19
|
+
failure ->(options) { options["2. fail"] = "Persist" }
|
20
20
|
end
|
21
21
|
|
22
22
|
it { Create.(title: "In Recital")["model"].title.must_equal "In Recital" }
|
@@ -35,7 +35,7 @@ class PersistTest < Minitest::Spec
|
|
35
35
|
class Update < Create
|
36
36
|
end
|
37
37
|
|
38
|
-
it { Update["pipetree"].inspect.must_equal %{[
|
38
|
+
it { Update["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>contract.build,>contract.default.validate,<persist_test.rb:17,>persist.save,<persist_test.rb:19]} }
|
39
39
|
|
40
40
|
#---
|
41
41
|
it do
|
@@ -20,22 +20,22 @@ class PipedreamTest < Minitest::Spec
|
|
20
20
|
|
21
21
|
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
step Model[ Song, :new ] # model!)
|
24
|
+
step Policy::Guard[ ->(options){ options["current_user"] == ::Module } ]
|
25
|
+
step Contract[ MyContract]
|
26
|
+
step Policy[ Auth, :user_and_model?]
|
27
|
+
failure Contract[ MyContract]
|
28
28
|
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
29
|
+
# step :model
|
30
|
+
# step :guard
|
31
|
+
# step :contract
|
32
32
|
|
33
33
|
|
34
34
|
# ok Model[Song, :new] # model!)
|
35
35
|
# ok Policy::Guard[ ->(options){ options["current_user"] == ::Module } ]
|
36
36
|
# ok Contract[MyContract]
|
37
37
|
# fail Contract[MyContract]
|
38
|
-
#
|
38
|
+
# step> "contract"
|
39
39
|
|
40
40
|
# | :bla
|
41
41
|
# | ->
|
@@ -42,21 +42,21 @@ class PipetreeTest < Minitest::Spec
|
|
42
42
|
|
43
43
|
MyValidate = ->(input, options) { res= input.validate(options["params"]) { |f| f.sync } }
|
44
44
|
# we can have a separate persist step and wrap in transaction. where do we pass contract, though?
|
45
|
-
|
45
|
+
step MyValidate, before: Call #replace: Contract::ValidLegacySwitch
|
46
46
|
#
|
47
47
|
MyAfterSave = ->(input, options) { input["after_save"] = true }
|
48
|
-
|
48
|
+
success MyAfterSave, after: MyValidate
|
49
49
|
|
50
50
|
ValidateFailureLogger = ->(input, options) { input["validate fail"] = true }
|
51
|
-
|
51
|
+
failure ValidateFailureLogger, after: MyValidate
|
52
52
|
|
53
|
-
|
53
|
+
success ->(input, options) { input.process(options["params"]) }, replace: Call, name: "my.params"
|
54
54
|
|
55
55
|
include Model
|
56
56
|
|
57
57
|
LogBreach = ->(input, options) { input.log_breach! }
|
58
58
|
|
59
|
-
|
59
|
+
failure LogBreach, after: Policy::Evaluate
|
60
60
|
|
61
61
|
model Song
|
62
62
|
policy ->(*) { self["current_user"] }
|