trailblazer 1.1.1 → 2.1.0
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 +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
|