trailblazer 1.1.2 → 2.0.0.beta1
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/.travis.yml +10 -7
- data/CHANGES.md +108 -0
- data/COMM-LICENSE +91 -0
- data/Gemfile +18 -4
- data/LICENSE.txt +7 -20
- data/README.md +55 -15
- data/Rakefile +21 -2
- data/draft-1.2.rb +7 -0
- data/lib/trailblazer.rb +17 -4
- data/lib/trailblazer/dsl.rb +47 -0
- data/lib/trailblazer/operation/auto_inject.rb +47 -0
- data/lib/trailblazer/operation/builder.rb +18 -18
- data/lib/trailblazer/operation/callback.rb +31 -38
- data/lib/trailblazer/operation/contract.rb +46 -0
- data/lib/trailblazer/operation/controller.rb +45 -27
- data/lib/trailblazer/operation/guard.rb +24 -0
- data/lib/trailblazer/operation/model.rb +41 -33
- data/lib/trailblazer/operation/nested.rb +43 -0
- data/lib/trailblazer/operation/params.rb +13 -0
- data/lib/trailblazer/operation/persist.rb +13 -0
- data/lib/trailblazer/operation/policy.rb +26 -72
- data/lib/trailblazer/operation/present.rb +19 -0
- data/lib/trailblazer/operation/procedural/contract.rb +15 -0
- data/lib/trailblazer/operation/procedural/validate.rb +22 -0
- data/lib/trailblazer/operation/pundit.rb +42 -0
- data/lib/trailblazer/operation/representer.rb +25 -92
- data/lib/trailblazer/operation/rescue.rb +23 -0
- data/lib/trailblazer/operation/resolver.rb +18 -24
- data/lib/trailblazer/operation/validate.rb +50 -0
- data/lib/trailblazer/operation/wrap.rb +37 -0
- data/lib/trailblazer/version.rb +1 -1
- data/test/{operation/controller_test.rb → controller_test.rb} +8 -4
- data/test/docs/auto_inject_test.rb +30 -0
- data/test/docs/contract_test.rb +429 -0
- data/test/docs/dry_test.rb +31 -0
- data/test/docs/guard_test.rb +143 -0
- data/test/docs/nested_test.rb +117 -0
- data/test/docs/policy_test.rb +2 -0
- data/test/docs/pundit_test.rb +109 -0
- data/test/docs/representer_test.rb +268 -0
- data/test/docs/rescue_test.rb +153 -0
- data/test/docs/wrap_test.rb +174 -0
- data/test/gemfiles/Gemfile.ruby-1.9 +3 -0
- data/test/gemfiles/Gemfile.ruby-2.0 +12 -0
- data/test/gemfiles/Gemfile.ruby-2.3 +12 -0
- data/test/module_test.rb +22 -15
- data/test/operation/builder_test.rb +66 -18
- data/test/operation/callback_test.rb +70 -0
- data/test/operation/contract_test.rb +385 -15
- data/test/operation/dsl/callback_test.rb +18 -30
- data/test/operation/dsl/contract_test.rb +209 -19
- data/test/operation/dsl/representer_test.rb +42 -15
- data/test/operation/guard_test.rb +1 -147
- data/test/operation/model_test.rb +105 -0
- data/test/operation/params_test.rb +36 -0
- data/test/operation/persist_test.rb +44 -0
- data/test/operation/pipedream_test.rb +59 -0
- data/test/operation/pipetree_test.rb +104 -0
- data/test/operation/present_test.rb +24 -0
- data/test/operation/pundit_test.rb +104 -0
- data/test/{representer_test.rb → operation/representer_test.rb} +58 -42
- data/test/operation/resolver_test.rb +34 -70
- data/test/operation_test.rb +57 -189
- data/test/test_helper.rb +23 -3
- data/trailblazer.gemspec +8 -7
- metadata +91 -59
- data/gemfiles/Gemfile.rails.lock +0 -130
- data/gemfiles/Gemfile.reform-2.0 +0 -6
- data/gemfiles/Gemfile.reform-2.1 +0 -7
- data/lib/trailblazer/autoloading.rb +0 -15
- data/lib/trailblazer/endpoint.rb +0 -31
- data/lib/trailblazer/operation.rb +0 -175
- data/lib/trailblazer/operation/collection.rb +0 -6
- data/lib/trailblazer/operation/dispatch.rb +0 -3
- data/lib/trailblazer/operation/model/dsl.rb +0 -29
- data/lib/trailblazer/operation/model/external.rb +0 -34
- data/lib/trailblazer/operation/policy/guard.rb +0 -35
- data/lib/trailblazer/operation/uploaded_file.rb +0 -77
- data/test/callback_test.rb +0 -104
- data/test/collection_test.rb +0 -57
- data/test/model_test.rb +0 -148
- data/test/operation/external_model_test.rb +0 -71
- data/test/operation/policy_test.rb +0 -97
- data/test/operation/reject_test.rb +0 -34
- data/test/rollback_test.rb +0 -47
@@ -1,152 +1,6 @@
|
|
1
1
|
require "test_helper"
|
2
|
-
require "trailblazer/operation/policy"
|
3
2
|
|
4
|
-
class OpPolicyGuardTest < MiniTest::Spec
|
5
|
-
Song = Struct.new(:name)
|
6
3
|
|
7
|
-
class Create < Trailblazer::Operation
|
8
|
-
include Policy::Guard
|
9
4
|
|
10
|
-
def model!(*)
|
11
|
-
Song.new
|
12
|
-
end
|
13
5
|
|
14
|
-
|
15
|
-
model.is_a?(Song) and params[:valid]
|
16
|
-
end
|
17
|
-
|
18
|
-
def process(*)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# valid.
|
23
|
-
it do
|
24
|
-
op = Create.(valid: true)
|
25
|
-
end
|
26
|
-
|
27
|
-
# invalid.
|
28
|
-
it do
|
29
|
-
assert_raises Trailblazer::NotAuthorizedError do
|
30
|
-
op = Create.(valid: false)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
|
35
|
-
describe "inheritance" do
|
36
|
-
class Update < Create
|
37
|
-
policy do |params|
|
38
|
-
params[:valid] == "correct"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
class Delete < Create
|
43
|
-
end
|
44
|
-
|
45
|
-
it do
|
46
|
-
Create.(valid: true).wont_equal nil
|
47
|
-
Delete.(valid: true).wont_equal nil
|
48
|
-
Update.(valid: "correct").wont_equal nil
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
|
-
describe "no policy defined, but included" do
|
54
|
-
class Show < Trailblazer::Operation
|
55
|
-
include Policy::Guard
|
56
|
-
|
57
|
-
def process(*)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
it { Show.({}).wont_equal nil }
|
62
|
-
end
|
63
|
-
|
64
|
-
|
65
|
-
describe "#params!" do
|
66
|
-
class Index < Trailblazer::Operation
|
67
|
-
include Policy::Guard
|
68
|
-
|
69
|
-
# make sure the guard receives the correct params.
|
70
|
-
policy { |params| params[:valid] == "true" }
|
71
|
-
|
72
|
-
def params!(params)
|
73
|
-
{ valid: params }
|
74
|
-
end
|
75
|
-
|
76
|
-
def process(*)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
it { Index.("true").wont_equal nil }
|
81
|
-
it { assert_raises(Trailblazer::NotAuthorizedError) { Index.(false).wont_equal nil } }
|
82
|
-
end
|
83
|
-
|
84
|
-
describe "with Callable" do
|
85
|
-
class Find < Trailblazer::Operation
|
86
|
-
include Policy::Guard
|
87
|
-
|
88
|
-
class Guardian
|
89
|
-
include Uber::Callable
|
90
|
-
|
91
|
-
def call(context, params)
|
92
|
-
params == "true"
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
policy Guardian.new
|
97
|
-
|
98
|
-
def process(*)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
it { Find.("true").wont_equal nil }
|
103
|
-
it { assert_raises(Trailblazer::NotAuthorizedError) { Find.(false).wont_equal nil } }
|
104
|
-
end
|
105
|
-
|
106
|
-
describe "with Proc" do
|
107
|
-
class Follow < Trailblazer::Operation
|
108
|
-
include Policy::Guard
|
109
|
-
|
110
|
-
policy ->(params) { params == "true" } # TODO: is this really executed in op context?
|
111
|
-
|
112
|
-
def process(*)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
it { Follow.("true").wont_equal nil }
|
117
|
-
it { assert_raises(Trailblazer::NotAuthorizedError) { Follow.(false).wont_equal nil } }
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
class OpBuilderDenyTest < MiniTest::Spec
|
122
|
-
Song = Struct.new(:name)
|
123
|
-
|
124
|
-
class Create < Trailblazer::Operation
|
125
|
-
include Deny
|
126
|
-
|
127
|
-
builds do |params|
|
128
|
-
deny! unless params[:valid]
|
129
|
-
end
|
130
|
-
|
131
|
-
def process(params)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
class Update < Create
|
136
|
-
builds -> (params) do
|
137
|
-
deny! unless params[:valid]
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
# valid.
|
142
|
-
it do
|
143
|
-
op = Create.(valid: true)
|
144
|
-
end
|
145
|
-
|
146
|
-
# invalid.
|
147
|
-
it do
|
148
|
-
assert_raises Trailblazer::NotAuthorizedError do
|
149
|
-
op = Create.(valid: false)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
6
|
+
# FIXME: what about block passed to ::policy?
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ModelTest < Minitest::Spec
|
4
|
+
Song = Struct.new(:id) do
|
5
|
+
def self.find(id); new(id) end
|
6
|
+
def self.find_by(id:nil); id.nil? ? nil : new(id) end
|
7
|
+
end
|
8
|
+
|
9
|
+
#---
|
10
|
+
# use Model semantics, no customizations.
|
11
|
+
class Create < Trailblazer::Operation
|
12
|
+
self.| Model Song, :new
|
13
|
+
end
|
14
|
+
|
15
|
+
# :new new.
|
16
|
+
it { Create.({})["model"].inspect.must_equal %{#<struct ModelTest::Song id=nil>} }
|
17
|
+
|
18
|
+
class Update < Create
|
19
|
+
self.~ Model :update # DISCUSS: do we need the ~ operator?
|
20
|
+
end
|
21
|
+
|
22
|
+
# :find it
|
23
|
+
it { Update.({ id: 1 })["model"].inspect.must_equal %{#<struct ModelTest::Song id=1>} }
|
24
|
+
|
25
|
+
#- inheritance
|
26
|
+
it { Update["pipetree"].inspect.must_equal %{[>>operation.new,&model.build]} }
|
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}} }
|
34
|
+
|
35
|
+
#---
|
36
|
+
# :find_by, exceptionless.
|
37
|
+
class Find < Trailblazer::Operation
|
38
|
+
self.| Model Song, :find_by
|
39
|
+
self.| :process
|
40
|
+
|
41
|
+
def process(*); self["x"] = true end
|
42
|
+
end
|
43
|
+
|
44
|
+
# can't find model.
|
45
|
+
#- result object, model
|
46
|
+
it do
|
47
|
+
Find.(id: nil)["result.model"].failure?.must_equal true
|
48
|
+
Find.(id: nil)["x"].must_equal nil
|
49
|
+
Find.(id: nil).failure?.must_equal true
|
50
|
+
end
|
51
|
+
|
52
|
+
#- result object, model
|
53
|
+
it do
|
54
|
+
Find.(id: 9)["result.model"].success?.must_equal true
|
55
|
+
Find.(id: 9)["x"].must_equal true
|
56
|
+
Find.(id: 9)["model"].inspect.must_equal %{#<struct ModelTest::Song id=9>}
|
57
|
+
end
|
58
|
+
|
59
|
+
#---
|
60
|
+
# override #model!, without any Model inclusions.
|
61
|
+
class Delete < Trailblazer::Operation
|
62
|
+
self.| Model :model!
|
63
|
+
def model!(params); params.to_s end
|
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
|
103
|
+
|
104
|
+
it { Index.(id: 1)["model"].inspect.must_equal %{#<struct ModelTest::Song id=1>} }
|
105
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "trailblazer/operation/params"
|
3
|
+
|
4
|
+
class OperationParamsTest < MiniTest::Spec
|
5
|
+
#---
|
6
|
+
# in call(params) and process(params), argument is always self["params"]
|
7
|
+
class Find < Trailblazer::Operation
|
8
|
+
def process(params); self["eql?"] = self["params"].object_id==params.object_id end
|
9
|
+
end
|
10
|
+
class Seek < Trailblazer::Operation
|
11
|
+
include Test::ReturnCall
|
12
|
+
def call(params); self["eql?"] = self["params"].object_id==params.object_id end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
it { Find.({ id: 1 })["eql?"].must_equal true }
|
17
|
+
# self["params"] is what gets passed in.
|
18
|
+
it { Find.({ id: 1 })["params"].must_equal({ id: 1 }) }
|
19
|
+
it { Seek.({ id: 1 }).must_equal true }
|
20
|
+
|
21
|
+
|
22
|
+
#---
|
23
|
+
# change self["params"] via Operation#
|
24
|
+
class Create < Trailblazer::Operation
|
25
|
+
include Params
|
26
|
+
def process(params) self[:x] = params end
|
27
|
+
def params!(params); params.merge(garrett: "Rocks!") end
|
28
|
+
end
|
29
|
+
|
30
|
+
# allows you changing params in #setup_params!.
|
31
|
+
it {
|
32
|
+
result = Create.( id: 1 )
|
33
|
+
result["params"].inspect.must_equal %{{:id=>1, :garrett=>\"Rocks!\"}}
|
34
|
+
result["params.original"].inspect.must_equal %{{:id=>1}}
|
35
|
+
}
|
36
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class PersistTest < Minitest::Spec
|
4
|
+
Song = Struct.new(:title, :saved) do
|
5
|
+
def save; title=="Fail!" ? false : self.saved = true; end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Create < Trailblazer::Operation
|
9
|
+
extend Contract::DSL
|
10
|
+
contract do
|
11
|
+
property :title
|
12
|
+
end
|
13
|
+
|
14
|
+
self.| Model( Song, :new )
|
15
|
+
self.| Contract::Build()
|
16
|
+
self.| Contract::Validate()
|
17
|
+
self.< ->(options) { options["1. fail"] = "Validate" }
|
18
|
+
self.| Persist()
|
19
|
+
self.< ->(options) { options["2. fail"] = "Persist" }
|
20
|
+
end
|
21
|
+
|
22
|
+
it { Create.(title: "In Recital")["model"].title.must_equal "In Recital" }
|
23
|
+
it { Create.(title: "In Recital")["model"].saved.must_equal true }
|
24
|
+
# failure
|
25
|
+
it do
|
26
|
+
result = Create.(title: "Fail!")
|
27
|
+
result["model"].saved.must_equal nil
|
28
|
+
result["model"].title.must_equal "Fail!"
|
29
|
+
result["2. fail"].must_equal "Persist"
|
30
|
+
result.success?.must_equal false
|
31
|
+
end
|
32
|
+
|
33
|
+
#---
|
34
|
+
#- inheritance
|
35
|
+
class Update < Create
|
36
|
+
end
|
37
|
+
|
38
|
+
it { Update["pipetree"].inspect.must_equal %{[>>operation.new,&model.build,>contract.build,&validate.params.extract,&contract.validate,<PersistTest::Update:17,&persist.save,<PersistTest::Update:19]} }
|
39
|
+
|
40
|
+
#---
|
41
|
+
it do
|
42
|
+
skip "show how save! could be applied and how we could rescue and deviate to left track"
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class PipedreamTest < Minitest::Spec
|
4
|
+
Song = Struct.new(:title)
|
5
|
+
|
6
|
+
class Create < Trailblazer::Operation
|
7
|
+
class MyContract < Reform::Form
|
8
|
+
property :title
|
9
|
+
end
|
10
|
+
|
11
|
+
class Auth
|
12
|
+
def initialize(user, model); @user, @model = user, model end
|
13
|
+
def user_and_model?; @user == Module && @model.class == Song end
|
14
|
+
end
|
15
|
+
|
16
|
+
# design principles:
|
17
|
+
# * include as less code as possible into the op class.
|
18
|
+
# * make the flow super explicit without making it cryptic (only 3 new operators)
|
19
|
+
# * avoid including DSL modules in favor of passing those configurations directly to the "step".
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
self.| Model[ Song, :new ] # model!)
|
24
|
+
self.| Policy::Guard[ ->(options){ options["current_user"] == ::Module } ]
|
25
|
+
self.| Contract[ MyContract]
|
26
|
+
self.| Policy[ Auth, :user_and_model?]
|
27
|
+
self.< Contract[ MyContract]
|
28
|
+
|
29
|
+
# self.| :model
|
30
|
+
# self.| :guard
|
31
|
+
# self.| :contract
|
32
|
+
|
33
|
+
|
34
|
+
# ok Model[Song, :new] # model!)
|
35
|
+
# ok Policy::Guard[ ->(options){ options["current_user"] == ::Module } ]
|
36
|
+
# ok Contract[MyContract]
|
37
|
+
# fail Contract[MyContract]
|
38
|
+
# self.|> "contract"
|
39
|
+
|
40
|
+
# | :bla
|
41
|
+
# | ->
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
# TODO: test with contract constant (done).
|
46
|
+
# test with inline contract.
|
47
|
+
# test with override contract!.
|
48
|
+
|
49
|
+
it do
|
50
|
+
puts Create["pipetree"].inspect(style: :rows)
|
51
|
+
result = Create.({}, { "current_user" => Module })
|
52
|
+
|
53
|
+
result["model"].inspect.must_equal %{#<struct PipedreamTest::Song title=nil>}
|
54
|
+
result["result.policy"].success?.must_equal true
|
55
|
+
result["contract"].class.superclass.must_equal Reform::Form
|
56
|
+
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
# self["pipetree"] = ::Pipetree[
|
4
|
+
# Trailblazer::Operation::New,
|
5
|
+
# # SetupParams,
|
6
|
+
# Trailblazer::Operation::Model::Build,
|
7
|
+
# Trailblazer::Operation::Model::Assign,
|
8
|
+
# Trailblazer::Operation::Call,
|
9
|
+
# ]
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
# def deserialize(*)
|
14
|
+
# super
|
15
|
+
# self.datetime = DateTime.parse("#{date} #{time}")
|
16
|
+
# end
|
17
|
+
|
18
|
+
class PipetreeTest < Minitest::Spec
|
19
|
+
Song = Struct.new(:title)
|
20
|
+
|
21
|
+
class Create < Trailblazer::Operation
|
22
|
+
include Builder
|
23
|
+
include Pipetree # this will add the functions, again, unfortunately. definitely an error source.
|
24
|
+
end
|
25
|
+
|
26
|
+
it { Create["pipetree"].inspect.must_equal %{[>>Build,>>New,>>Call,Result::Build,>>New,>>Call,Result::Build]} }
|
27
|
+
|
28
|
+
#---
|
29
|
+
# playground
|
30
|
+
require "trailblazer/operation/policy"
|
31
|
+
require "trailblazer/operation/guard"
|
32
|
+
|
33
|
+
class Edit < Trailblazer::Operation
|
34
|
+
include Builder
|
35
|
+
include Policy::Guard
|
36
|
+
include Contract::Step
|
37
|
+
contract do
|
38
|
+
property :title
|
39
|
+
validates :title, presence: true
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
MyValidate = ->(input, options) { res= input.validate(options["params"]) { |f| f.sync } }
|
44
|
+
# we can have a separate persist step and wrap in transaction. where do we pass contract, though?
|
45
|
+
self.& MyValidate, before: Call #replace: Contract::ValidLegacySwitch
|
46
|
+
#
|
47
|
+
MyAfterSave = ->(input, options) { input["after_save"] = true }
|
48
|
+
self.> MyAfterSave, after: MyValidate
|
49
|
+
|
50
|
+
ValidateFailureLogger = ->(input, options) { input["validate fail"] = true }
|
51
|
+
self.< ValidateFailureLogger, after: MyValidate
|
52
|
+
|
53
|
+
self.> ->(input, options) { input.process(options["params"]) }, replace: Call, name: "my.params"
|
54
|
+
|
55
|
+
include Model
|
56
|
+
|
57
|
+
LogBreach = ->(input, options) { input.log_breach! }
|
58
|
+
|
59
|
+
self.< LogBreach, after: Policy::Evaluate
|
60
|
+
|
61
|
+
model Song
|
62
|
+
policy ->(*) { self["current_user"] }
|
63
|
+
|
64
|
+
def log_breach!
|
65
|
+
self["breach"] = true
|
66
|
+
end
|
67
|
+
|
68
|
+
def process(params)
|
69
|
+
self["my.valid"] = true
|
70
|
+
end
|
71
|
+
|
72
|
+
self["pipetree"]._insert(Contract::ValidLegacySwitch, {delete: true}, nil, nil)
|
73
|
+
end
|
74
|
+
|
75
|
+
puts Edit["pipetree"].inspect(style: :rows)
|
76
|
+
|
77
|
+
it { Edit["pipetree"].inspect.must_equal %{[>>operation.build,>>operation.new,&model.build,&policy.guard.evaluate,<LogBreach,>contract.build,&MyValidate,<ValidateFailureLogger,>MyAfterSave,>my.params["params"]]} }
|
78
|
+
|
79
|
+
# valid case.
|
80
|
+
it {
|
81
|
+
# puts "valid"
|
82
|
+
# puts Edit["pipetree"].inspect(style: :rows)
|
83
|
+
result = Edit.({ title: "Stupid 7" }, "current_user" => true)
|
84
|
+
# puts "success! #{result.inspect}"
|
85
|
+
result["my.valid"].must_equal true
|
86
|
+
result["breach"].must_equal nil
|
87
|
+
result["after_save"].must_equal true
|
88
|
+
result["validate fail"].must_equal nil
|
89
|
+
}
|
90
|
+
# beach! i mean breach!
|
91
|
+
it {
|
92
|
+
# puts "beach"
|
93
|
+
# puts Edit["pipetree"].inspect(style: :rows)
|
94
|
+
result = Edit.({})
|
95
|
+
# puts "@@@@@ #{result.inspect}"
|
96
|
+
result["my.valid"].must_equal nil
|
97
|
+
result["breach"].must_equal true
|
98
|
+
result["validate fail"].must_equal true
|
99
|
+
result["after_save"].must_equal nil
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
# TODO: show the execution path in pipetree
|
104
|
+
# unified result.contract, result.policy interface
|