trailblazer 1.1.1 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.rubocop-https---raw-githubusercontent-com-trailblazer-meta-master-rubocop-yml +101 -0
- data/.rubocop.yml +20 -0
- data/.rubocop_todo.yml +556 -0
- data/.travis.yml +6 -7
- data/CHANGES.md +224 -0
- data/COMM-LICENSE +62 -0
- data/CONTRIBUTING.md +179 -0
- data/Gemfile +0 -10
- data/LICENSE +9 -0
- data/README.md +68 -189
- data/Rakefile +3 -3
- data/lib/trailblazer/version.rb +3 -1
- data/lib/trailblazer.rb +3 -6
- data/test/test_helper.rb +32 -3
- data/trailblazer.gemspec +11 -15
- metadata +28 -132
- data/LICENSE.txt +0 -22
- data/TODO.md +0 -11
- data/doc/Trb-The-Stack.png +0 -0
- data/doc/trb.jpg +0 -0
- 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/builder.rb +0 -26
- data/lib/trailblazer/operation/callback.rb +0 -53
- data/lib/trailblazer/operation/collection.rb +0 -6
- data/lib/trailblazer/operation/controller.rb +0 -72
- 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/model.rb +0 -50
- data/lib/trailblazer/operation/module.rb +0 -29
- data/lib/trailblazer/operation/policy/guard.rb +0 -35
- data/lib/trailblazer/operation/policy.rb +0 -85
- data/lib/trailblazer/operation/representer.rb +0 -98
- data/lib/trailblazer/operation/resolver.rb +0 -30
- data/lib/trailblazer/operation/uploaded_file.rb +0 -77
- data/lib/trailblazer/operation.rb +0 -175
- data/test/callback_test.rb +0 -104
- data/test/collection_test.rb +0 -57
- data/test/model_test.rb +0 -148
- data/test/module_test.rb +0 -93
- data/test/operation/builder_test.rb +0 -41
- data/test/operation/contract_test.rb +0 -30
- data/test/operation/controller_test.rb +0 -111
- data/test/operation/dsl/callback_test.rb +0 -118
- data/test/operation/dsl/contract_test.rb +0 -104
- data/test/operation/dsl/representer_test.rb +0 -142
- data/test/operation/external_model_test.rb +0 -71
- data/test/operation/guard_test.rb +0 -152
- data/test/operation/policy_test.rb +0 -97
- data/test/operation/reject_test.rb +0 -34
- data/test/operation/resolver_test.rb +0 -83
- data/test/operation_test.rb +0 -275
- data/test/representer_test.rb +0 -238
- data/test/rollback_test.rb +0 -47
@@ -1,104 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
# ::contract builds Reform::Form class
|
4
|
-
class DslContractTest < MiniTest::Spec
|
5
|
-
module SongProcess
|
6
|
-
def process(params)
|
7
|
-
validate(params, @model = OpenStruct.new)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
describe "inheritance across operations" do
|
12
|
-
# inheritance
|
13
|
-
class Operation < Trailblazer::Operation
|
14
|
-
contract do
|
15
|
-
property :title
|
16
|
-
property :band
|
17
|
-
end
|
18
|
-
|
19
|
-
class JSON < self
|
20
|
-
contract do # inherit Contract
|
21
|
-
property :genre, validates: {presence: true}
|
22
|
-
property :band, virtual: true
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# inherits subclassed Contract.
|
28
|
-
it { Operation.contract_class.wont_equal Operation::JSON.contract_class }
|
29
|
-
|
30
|
-
it do
|
31
|
-
form = Operation.contract_class.new(OpenStruct.new)
|
32
|
-
form.validate({})#.must_equal true
|
33
|
-
form.errors.to_s.must_equal "{}"
|
34
|
-
|
35
|
-
form = Operation::JSON.contract_class.new(OpenStruct.new)
|
36
|
-
form.validate({})#.must_equal true
|
37
|
-
form.errors.to_s.must_equal "{:genre=>[\"can't be blank\"]}"
|
38
|
-
end
|
39
|
-
|
40
|
-
# allows overriding options
|
41
|
-
it do
|
42
|
-
form = Operation::JSON.contract_class.new(song = OpenStruct.new)
|
43
|
-
form.validate({genre: "Punkrock", band: "Osker"}).must_equal true
|
44
|
-
form.sync
|
45
|
-
|
46
|
-
song.genre.must_equal "Punkrock"
|
47
|
-
song.band.must_equal nil
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
describe "Op.contract" do
|
52
|
-
it { Operation.contract.must_equal Operation.contract_class }
|
53
|
-
end
|
54
|
-
|
55
|
-
describe "Op.contract CommentForm" do
|
56
|
-
class SongForm < Reform::Form
|
57
|
-
property :songTitle, validates: {presence: true}
|
58
|
-
end
|
59
|
-
|
60
|
-
class OpWithExternalContract < Trailblazer::Operation
|
61
|
-
contract SongForm
|
62
|
-
include SongProcess
|
63
|
-
end
|
64
|
-
|
65
|
-
it { OpWithExternalContract.("songTitle"=> "Monsterparty").contract.songTitle.must_equal "Monsterparty" }
|
66
|
-
end
|
67
|
-
|
68
|
-
describe "Op.contract CommentForm do .. end" do
|
69
|
-
class DifferentSongForm < Reform::Form
|
70
|
-
property :songTitle, validates: {presence: true}
|
71
|
-
end
|
72
|
-
|
73
|
-
class OpNotExtendingContract < Trailblazer::Operation
|
74
|
-
contract DifferentSongForm
|
75
|
-
include SongProcess
|
76
|
-
end
|
77
|
-
|
78
|
-
class OpExtendingContract < Trailblazer::Operation
|
79
|
-
contract DifferentSongForm do
|
80
|
-
property :genre
|
81
|
-
end
|
82
|
-
include SongProcess
|
83
|
-
end
|
84
|
-
|
85
|
-
# this operation copies DifferentSongForm and shouldn't have `genre`.
|
86
|
-
it do
|
87
|
-
contract = OpNotExtendingContract.("songTitle"=>"Monsterparty", "genre"=>"Punk").contract
|
88
|
-
contract.songTitle.must_equal "Monsterparty"
|
89
|
-
assert_raises(NoMethodError) { contract.genre }
|
90
|
-
end
|
91
|
-
|
92
|
-
# this operation copies DifferentSongForm and extends it with the property `genre`.
|
93
|
-
it do
|
94
|
-
contract = OpExtendingContract.("songTitle"=>"Monsterparty", "genre"=>"Punk").contract
|
95
|
-
contract.songTitle.must_equal "Monsterparty"
|
96
|
-
contract.genre.must_equal "Punk"
|
97
|
-
end
|
98
|
-
|
99
|
-
# of course, the original contract wasn't modified, either.
|
100
|
-
it do
|
101
|
-
assert_raises(NoMethodError) { DifferentSongForm.new(OpenStruct.new).genre }
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
@@ -1,142 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
require "representable/json"
|
3
|
-
require "trailblazer/operation/representer"
|
4
|
-
|
5
|
-
class DslRepresenterTest < MiniTest::Spec
|
6
|
-
module SongProcess
|
7
|
-
def process(params)
|
8
|
-
@model = OpenStruct.new(params)
|
9
|
-
end
|
10
|
-
|
11
|
-
def represented
|
12
|
-
model
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
describe "inheritance across operations" do
|
17
|
-
class Operation < Trailblazer::Operation
|
18
|
-
include Representer
|
19
|
-
include SongProcess
|
20
|
-
|
21
|
-
representer do
|
22
|
-
property :title
|
23
|
-
end
|
24
|
-
|
25
|
-
class JSON < self
|
26
|
-
representer do
|
27
|
-
property :band
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
it { Operation.(title: "Nothing To Lose", band: "Gary Moore").to_json.must_equal %{{"title":"Nothing To Lose"}} }
|
33
|
-
# only the subclass must have the `band` field, even though it's set in the original operation.
|
34
|
-
it { Operation::JSON.(title: "Nothing To Lose", band: "Gary Moore").to_json.must_equal %{{"title":"Nothing To Lose","band":"Gary Moore"}} }
|
35
|
-
end
|
36
|
-
|
37
|
-
describe "Op.representer" do
|
38
|
-
it { Operation.representer.must_equal Operation.representer_class }
|
39
|
-
end
|
40
|
-
|
41
|
-
describe "Op.representer CommentRepresenter" do
|
42
|
-
class SongRepresenter < Representable::Decorator
|
43
|
-
include Representable::JSON
|
44
|
-
property :songTitle
|
45
|
-
end
|
46
|
-
|
47
|
-
class OpWithExternalRepresenter < Trailblazer::Operation
|
48
|
-
include Representer
|
49
|
-
include SongProcess
|
50
|
-
representer SongRepresenter
|
51
|
-
end
|
52
|
-
|
53
|
-
it { OpWithExternalRepresenter.("songTitle"=>"Listen To Your Heartbeat").to_json.must_equal %{{"songTitle":"Listen To Your Heartbeat"}} }
|
54
|
-
end
|
55
|
-
|
56
|
-
describe "Op.representer CommentRepresenter do .. end" do
|
57
|
-
class HitRepresenter < Representable::Decorator
|
58
|
-
include Representable::JSON
|
59
|
-
property :title
|
60
|
-
end
|
61
|
-
|
62
|
-
class OpNotExtendingRepresenter < Trailblazer::Operation
|
63
|
-
include Representer
|
64
|
-
include SongProcess
|
65
|
-
representer HitRepresenter
|
66
|
-
end
|
67
|
-
|
68
|
-
class OpExtendingRepresenter < Trailblazer::Operation
|
69
|
-
include Representer
|
70
|
-
include SongProcess
|
71
|
-
representer HitRepresenter do
|
72
|
-
property :genre
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# this operation copies HitRepresenter and shouldn't have `genre`.
|
77
|
-
it do
|
78
|
-
OpNotExtendingRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty"}}
|
79
|
-
end
|
80
|
-
|
81
|
-
# # this operation copies HitRepresenter and extends it with the property `genre`.
|
82
|
-
it do
|
83
|
-
OpExtendingRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty","genre":"Punk"}}
|
84
|
-
end
|
85
|
-
|
86
|
-
# # of course, the original representer wasn't modified, either.
|
87
|
-
it do
|
88
|
-
HitRepresenter.new(OpenStruct.new(title: "Monsterparty", genre: "Punk")).to_json.must_equal %{{"title":"Monsterparty"}}
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
describe "Op.representer (inferring)" do
|
93
|
-
class OpWithContract < Trailblazer::Operation
|
94
|
-
include Representer
|
95
|
-
include SongProcess
|
96
|
-
|
97
|
-
contract do
|
98
|
-
property :songTitle
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
class OpWithContract2 < Trailblazer::Operation
|
103
|
-
include Representer
|
104
|
-
include SongProcess
|
105
|
-
|
106
|
-
contract OpWithContract.contract
|
107
|
-
representer do
|
108
|
-
property :genre
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
it { OpWithContract.("songTitle"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"songTitle":"Monsterparty"}} }
|
113
|
-
# this representer block extends the inferred from contract.
|
114
|
-
it { OpWithContract2.("songTitle"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"songTitle":"Monsterparty","genre":"Punk"}} }
|
115
|
-
end
|
116
|
-
|
117
|
-
describe "Op.representer_class" do
|
118
|
-
class PlayRepresenter < Representable::Decorator
|
119
|
-
include Representable::JSON
|
120
|
-
property :title
|
121
|
-
end
|
122
|
-
|
123
|
-
class OpSettingRepresenter < Trailblazer::Operation
|
124
|
-
include Representer
|
125
|
-
include SongProcess
|
126
|
-
self.representer_class= PlayRepresenter
|
127
|
-
end
|
128
|
-
|
129
|
-
class OpExtendRepresenter < Trailblazer::Operation
|
130
|
-
include Representer
|
131
|
-
include SongProcess
|
132
|
-
self.representer_class= PlayRepresenter
|
133
|
-
representer do
|
134
|
-
property :genre
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
# both operations produce the same as the representer is shared, not copied.
|
139
|
-
it { OpSettingRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty","genre":"Punk"}} }
|
140
|
-
it { OpExtendRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty","genre":"Punk"}} }
|
141
|
-
end
|
142
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
require "trailblazer/operation/model/external"
|
3
|
-
|
4
|
-
class ExternalModelTest < MiniTest::Spec
|
5
|
-
Song = Struct.new(:title, :id) do
|
6
|
-
class << self
|
7
|
-
attr_accessor :find_result # TODO: eventually, replace with AR test.
|
8
|
-
attr_accessor :all_records
|
9
|
-
|
10
|
-
def find(id)
|
11
|
-
find_result.tap do |song|
|
12
|
-
song.id = id
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end # FIXME: use from CrudTest.
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
class Bla < Trailblazer::Operation
|
22
|
-
include Model::External
|
23
|
-
model Song, :update
|
24
|
-
|
25
|
-
def process(params)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
let (:song) { Song.new("Numbers") }
|
30
|
-
|
31
|
-
before do
|
32
|
-
Song.find_result = song
|
33
|
-
end
|
34
|
-
|
35
|
-
# ::model!
|
36
|
-
it do
|
37
|
-
Bla.model!(id: 1).must_equal song
|
38
|
-
song.id.must_equal 1
|
39
|
-
end
|
40
|
-
|
41
|
-
# call style.
|
42
|
-
it do
|
43
|
-
Bla.(id: 2).model.must_equal song
|
44
|
-
song.id.must_equal 2
|
45
|
-
end
|
46
|
-
|
47
|
-
# #present.
|
48
|
-
it do
|
49
|
-
Bla.present({}).model.must_equal song
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
|
-
class OpWithBuilder < Bla
|
54
|
-
class A < self
|
55
|
-
end
|
56
|
-
|
57
|
-
builds -> (model, params) do
|
58
|
-
return A if model.id == 1 and params[:user] == 2
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
describe "::builds args" do
|
63
|
-
it do
|
64
|
-
OpWithBuilder.(id: 1, user: "different").must_be_instance_of OpWithBuilder
|
65
|
-
end
|
66
|
-
|
67
|
-
it do
|
68
|
-
OpWithBuilder.(id: 1, user: 2).must_be_instance_of OpWithBuilder::A
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
@@ -1,152 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
require "trailblazer/operation/policy"
|
3
|
-
|
4
|
-
class OpPolicyGuardTest < MiniTest::Spec
|
5
|
-
Song = Struct.new(:name)
|
6
|
-
|
7
|
-
class Create < Trailblazer::Operation
|
8
|
-
include Policy::Guard
|
9
|
-
|
10
|
-
def model!(*)
|
11
|
-
Song.new
|
12
|
-
end
|
13
|
-
|
14
|
-
policy do |params|
|
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
|
@@ -1,97 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
require "trailblazer/operation/policy"
|
3
|
-
|
4
|
-
|
5
|
-
class OpPunditPolicyTest < MiniTest::Spec
|
6
|
-
Song = Struct.new(:name)
|
7
|
-
User = Struct.new(:name)
|
8
|
-
|
9
|
-
class BlaPolicy
|
10
|
-
def initialize(user, song)
|
11
|
-
@user = user
|
12
|
-
@song = song
|
13
|
-
end
|
14
|
-
|
15
|
-
def create?
|
16
|
-
@user.is_a?(User) and @song.is_a?(Song)
|
17
|
-
end
|
18
|
-
|
19
|
-
def edit?
|
20
|
-
"yepp"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
class BlaOperation < Trailblazer::Operation
|
25
|
-
include Policy
|
26
|
-
policy BlaPolicy, :create?
|
27
|
-
|
28
|
-
def model!(*)
|
29
|
-
Song.new
|
30
|
-
end
|
31
|
-
|
32
|
-
def process(*)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
# valid.
|
37
|
-
it do
|
38
|
-
op = BlaOperation.({current_user: User.new})
|
39
|
-
|
40
|
-
# #policy provides the Policy instance.
|
41
|
-
op.policy.edit?.must_equal "yepp"
|
42
|
-
end
|
43
|
-
|
44
|
-
# invalid.
|
45
|
-
it do
|
46
|
-
assert_raises Trailblazer::NotAuthorizedError do
|
47
|
-
op = BlaOperation.({current_user: nil})
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
|
52
|
-
# no policy set
|
53
|
-
class NoPolicyOperation < Trailblazer::Operation
|
54
|
-
include Policy
|
55
|
-
# no policy.
|
56
|
-
|
57
|
-
def process(*)
|
58
|
-
@model = Song.new
|
59
|
-
end
|
60
|
-
|
61
|
-
class Delete < self
|
62
|
-
end
|
63
|
-
|
64
|
-
|
65
|
-
class LocalPolicy
|
66
|
-
def initialize(user, song)
|
67
|
-
@user = user
|
68
|
-
@song = song
|
69
|
-
end
|
70
|
-
|
71
|
-
def update?; false end
|
72
|
-
end
|
73
|
-
|
74
|
-
class Update < self
|
75
|
-
policy LocalPolicy, :update?
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# valid.
|
80
|
-
it do
|
81
|
-
op = NoPolicyOperation.({})
|
82
|
-
op.model.must_be_instance_of Song
|
83
|
-
end
|
84
|
-
|
85
|
-
# inherited without config works.
|
86
|
-
it do
|
87
|
-
op = NoPolicyOperation::Delete.({})
|
88
|
-
op.model.must_be_instance_of Song
|
89
|
-
end
|
90
|
-
|
91
|
-
# inherited can override.
|
92
|
-
it do
|
93
|
-
assert_raises Trailblazer::NotAuthorizedError do
|
94
|
-
op = NoPolicyOperation::Update.({})
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class OperationRejectTest < MiniTest::Spec
|
4
|
-
class Operation < Trailblazer::Operation
|
5
|
-
def process(params)
|
6
|
-
invalid! if params == false
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
it do
|
11
|
-
run = nil
|
12
|
-
Operation.run(true) { run = true }
|
13
|
-
run.must_equal true
|
14
|
-
end
|
15
|
-
|
16
|
-
it do
|
17
|
-
run = nil
|
18
|
-
Operation.run(false) { run = true }
|
19
|
-
run.must_equal nil
|
20
|
-
end
|
21
|
-
|
22
|
-
it do
|
23
|
-
run = nil
|
24
|
-
op = Operation.reject(true) { run = true }
|
25
|
-
run.must_equal nil
|
26
|
-
op.must_be_instance_of Operation
|
27
|
-
end
|
28
|
-
|
29
|
-
it do
|
30
|
-
run = nil
|
31
|
-
Operation.reject(false) { run = true }
|
32
|
-
run.must_equal true
|
33
|
-
end
|
34
|
-
end
|
@@ -1,83 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
require "trailblazer/operation/resolver"
|
3
|
-
|
4
|
-
class ResolverTest < MiniTest::Spec
|
5
|
-
Song = Struct.new(:title)
|
6
|
-
User = Struct.new(:name)
|
7
|
-
|
8
|
-
class MyKitchenRules
|
9
|
-
def initialize(user, song)
|
10
|
-
@user = user
|
11
|
-
@song = song
|
12
|
-
end
|
13
|
-
|
14
|
-
def create?
|
15
|
-
@user.is_a?(User) and @song.is_a?(Song)
|
16
|
-
end
|
17
|
-
|
18
|
-
def admin?
|
19
|
-
@user && @user.name == "admin" && @song.is_a?(Song)
|
20
|
-
end
|
21
|
-
|
22
|
-
def true?
|
23
|
-
true
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class Create < Trailblazer::Operation
|
28
|
-
include Resolver
|
29
|
-
model Song, :create
|
30
|
-
policy MyKitchenRules, :create?
|
31
|
-
|
32
|
-
builds-> (model, policy, params) do
|
33
|
-
return ForGaryMoore if model.title == "Friday On My Mind"
|
34
|
-
return Admin if policy.admin?
|
35
|
-
return SignedIn if params[:current_user] && params[:current_user].name
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.model!(params)
|
39
|
-
Song.new(params[:title])
|
40
|
-
end
|
41
|
-
|
42
|
-
def process(*)
|
43
|
-
end
|
44
|
-
|
45
|
-
class Admin < self
|
46
|
-
end
|
47
|
-
class SignedIn < self
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
# valid.
|
52
|
-
it { Create.({current_user: User.new}).must_be_instance_of Create }
|
53
|
-
it { Create.({current_user: User.new("admin")}).must_be_instance_of Create::Admin }
|
54
|
-
it { Create.({current_user: User.new("kenneth")}).must_be_instance_of Create::SignedIn }
|
55
|
-
|
56
|
-
# invalid.
|
57
|
-
it do
|
58
|
-
assert_raises Trailblazer::NotAuthorizedError do
|
59
|
-
Create.({})
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
|
64
|
-
describe "passes policy into operation" do
|
65
|
-
class Update < Trailblazer::Operation
|
66
|
-
include Resolver
|
67
|
-
model Song, :create
|
68
|
-
policy MyKitchenRules, :true?
|
69
|
-
|
70
|
-
builds-> (model, policy, params) do
|
71
|
-
policy.instance_eval { def whoami; "me!" end }
|
72
|
-
nil
|
73
|
-
end
|
74
|
-
|
75
|
-
def process(*)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
it do
|
80
|
-
Update.({}).policy.whoami.must_equal "me!"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|