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
data/test/operation_test.rb
DELETED
@@ -1,275 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
module Inspect
|
4
|
-
def inspect
|
5
|
-
"<#{self.class.to_s.split("::").last} @model=#{@model}>"
|
6
|
-
end
|
7
|
-
alias_method :to_s, :inspect
|
8
|
-
end
|
9
|
-
|
10
|
-
class OperationSetupParamsTest < MiniTest::Spec
|
11
|
-
class OperationSetupParam < Trailblazer::Operation
|
12
|
-
def process(params)
|
13
|
-
@model = params
|
14
|
-
end
|
15
|
-
|
16
|
-
def setup_params!(params)
|
17
|
-
params.merge!(garrett: "Rocks!")
|
18
|
-
end
|
19
|
-
|
20
|
-
include Inspect
|
21
|
-
end
|
22
|
-
|
23
|
-
# allows you changing params in #setup_params!.
|
24
|
-
it { OperationSetupParam.run({valid: true}).to_s.must_equal "[true, <OperationSetupParam @model={:valid=>true, :garrett=>\"Rocks!\"}>]" }
|
25
|
-
end
|
26
|
-
|
27
|
-
class OperationParamsTest < MiniTest::Spec
|
28
|
-
class Operation < Trailblazer::Operation
|
29
|
-
def process(params)
|
30
|
-
@model = "#{params} and #{@params==params}"
|
31
|
-
end
|
32
|
-
|
33
|
-
def params!(params)
|
34
|
-
{ params: params }
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# allows you returning new params in #params!.
|
39
|
-
it { Operation.({valid: true}).model.to_s.must_equal "{:params=>{:valid=>true}} and true" }
|
40
|
-
end
|
41
|
-
|
42
|
-
# Operation#model.
|
43
|
-
class OperationModelTest < MiniTest::Spec
|
44
|
-
class Operation < Trailblazer::Operation
|
45
|
-
def process(params)
|
46
|
-
end
|
47
|
-
|
48
|
-
def model!(params)
|
49
|
-
params
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# #model.
|
54
|
-
it { Operation.(Object).model.must_equal Object }
|
55
|
-
end
|
56
|
-
|
57
|
-
# Operation#model=.
|
58
|
-
class OperationModelWriterTest < MiniTest::Spec
|
59
|
-
class Operation < Trailblazer::Operation
|
60
|
-
def process(params)
|
61
|
-
self.model = "#{params}"
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
it { Operation.("I can set @model via a private setter").model.to_s.must_equal "I can set @model via a private setter" }
|
66
|
-
end
|
67
|
-
|
68
|
-
class OperationRunTest < MiniTest::Spec
|
69
|
-
class Operation < Trailblazer::Operation
|
70
|
-
# allow providing your own contract.
|
71
|
-
self.contract_class = class Contract
|
72
|
-
def initialize(*)
|
73
|
-
end
|
74
|
-
def validate(params)
|
75
|
-
return true if params == "yes, true"
|
76
|
-
false
|
77
|
-
end
|
78
|
-
|
79
|
-
def errors
|
80
|
-
Struct.new(:to_s).new("Op just calls #to_s on Errors!")
|
81
|
-
end
|
82
|
-
self
|
83
|
-
end
|
84
|
-
|
85
|
-
def process(params)
|
86
|
-
model = Object
|
87
|
-
validate(params, model)
|
88
|
-
end
|
89
|
-
|
90
|
-
include Inspect
|
91
|
-
end
|
92
|
-
|
93
|
-
# contract is inferred from self::contract_class.
|
94
|
-
# ::run returns result set when run without block.
|
95
|
-
it { Operation.run("not true").to_s.must_equal %{[false, <Operation @model=>]} }
|
96
|
-
it { Operation.run("yes, true").to_s.must_equal %{[true, <Operation @model=>]} }
|
97
|
-
|
98
|
-
# ::call raises exception when invalid.
|
99
|
-
it do
|
100
|
-
exception = assert_raises(Trailblazer::Operation::InvalidContract) { Operation.("not true") }
|
101
|
-
exception.message.must_equal "Op just calls #to_s on Errors!"
|
102
|
-
end
|
103
|
-
|
104
|
-
# return operation when ::call
|
105
|
-
it { Operation.("yes, true").to_s.must_equal %{<Operation @model=>} }
|
106
|
-
# #[] is alias for .()
|
107
|
-
it { Operation["yes, true"].to_s.must_equal %{<Operation @model=>} }
|
108
|
-
|
109
|
-
|
110
|
-
# ::run with block returns operation.
|
111
|
-
# valid executes block.
|
112
|
-
it "block" do
|
113
|
-
outcome = nil
|
114
|
-
res = Operation.run("yes, true") do
|
115
|
-
outcome = "true"
|
116
|
-
end
|
117
|
-
|
118
|
-
outcome.must_equal "true" # block was executed.
|
119
|
-
res.to_s.must_equal %{<Operation @model=>}
|
120
|
-
end
|
121
|
-
|
122
|
-
# invalid doesn't execute block.
|
123
|
-
it "block, invalid" do
|
124
|
-
outcome = nil
|
125
|
-
res = Operation.run("no, not true, false") do
|
126
|
-
outcome = "true"
|
127
|
-
end
|
128
|
-
|
129
|
-
outcome.must_equal nil # block was _not_ executed.
|
130
|
-
res.to_s.must_equal %{<Operation @model=>}
|
131
|
-
end
|
132
|
-
|
133
|
-
# block yields operation
|
134
|
-
it do
|
135
|
-
outcome = nil
|
136
|
-
res = Operation.run("yes, true") do |op|
|
137
|
-
outcome = op
|
138
|
-
end
|
139
|
-
|
140
|
-
outcome.to_s.must_equal %{<Operation @model=>} # block was executed.
|
141
|
-
res.to_s.must_equal %{<Operation @model=>}
|
142
|
-
end
|
143
|
-
|
144
|
-
# # Operation#contract returns @contract
|
145
|
-
it { Operation.("yes, true").contract.class.to_s.must_equal "OperationRunTest::Operation::Contract" }
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
describe "::present" do
|
151
|
-
class NoContractOp < Trailblazer::Operation
|
152
|
-
self.contract_class = nil
|
153
|
-
|
154
|
-
def model!(*)
|
155
|
-
Object
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# the operation and model are available, but no contract.
|
160
|
-
it { NoContractOp.present({}).model.must_equal Object }
|
161
|
-
# no contract is built.
|
162
|
-
it { assert_raises(NoMethodError) { NoContractOp.present({}).contract } }
|
163
|
-
it { assert_raises(NoMethodError) { NoContractOp.run({}) } }
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
|
168
|
-
class OperationTest < MiniTest::Spec
|
169
|
-
# test #invalid!
|
170
|
-
class OperationWithoutValidateCall < Trailblazer::Operation
|
171
|
-
def process(params)
|
172
|
-
params || invalid!(params)
|
173
|
-
end
|
174
|
-
|
175
|
-
include Inspect
|
176
|
-
end
|
177
|
-
|
178
|
-
# ::run
|
179
|
-
it { OperationWithoutValidateCall.run(true).to_s.must_equal %{[true, <OperationWithoutValidateCall @model=>]} }
|
180
|
-
# invalid.
|
181
|
-
it { OperationWithoutValidateCall.run(false).to_s.must_equal %{[false, <OperationWithoutValidateCall @model=>]} }
|
182
|
-
|
183
|
-
|
184
|
-
# #validate yields contract when valid
|
185
|
-
class OperationWithValidateBlock < Trailblazer::Operation
|
186
|
-
self.contract_class = class Contract
|
187
|
-
def initialize(*)
|
188
|
-
end
|
189
|
-
|
190
|
-
def validate(params)
|
191
|
-
params
|
192
|
-
end
|
193
|
-
self
|
194
|
-
end
|
195
|
-
|
196
|
-
def process(params)
|
197
|
-
validate(params, Object.new) do |c|
|
198
|
-
@secret_contract = c.class
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
attr_reader :secret_contract
|
203
|
-
end
|
204
|
-
|
205
|
-
it { OperationWithValidateBlock.run(false).last.secret_contract.must_equal nil }
|
206
|
-
it { OperationWithValidateBlock.(true).secret_contract.must_equal OperationWithValidateBlock::Contract }
|
207
|
-
|
208
|
-
|
209
|
-
# test validate wit if/else
|
210
|
-
class OperationWithValidateAndIf < Trailblazer::Operation
|
211
|
-
self.contract_class = class Contract
|
212
|
-
def initialize(*)
|
213
|
-
end
|
214
|
-
|
215
|
-
def validate(params)
|
216
|
-
params
|
217
|
-
end
|
218
|
-
self
|
219
|
-
end
|
220
|
-
|
221
|
-
def process(params)
|
222
|
-
if validate(params, Object.new)
|
223
|
-
@secret_contract = contract.class
|
224
|
-
else
|
225
|
-
@secret_contract = "so wrong!"
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
attr_reader :secret_contract
|
230
|
-
end
|
231
|
-
|
232
|
-
it { OperationWithValidateAndIf.run(false).last.secret_contract.must_equal "so wrong!" }
|
233
|
-
it { OperationWithValidateAndIf.(true).secret_contract.must_equal OperationWithValidateAndIf::Contract }
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
# ::present only runs #setup! which runs #model!.
|
238
|
-
class ContractOnlyOperation < Trailblazer::Operation
|
239
|
-
self.contract_class = class Contract
|
240
|
-
def initialize(model)
|
241
|
-
@_model = model
|
242
|
-
end
|
243
|
-
attr_reader :_model
|
244
|
-
self
|
245
|
-
end
|
246
|
-
|
247
|
-
def model!(params)
|
248
|
-
Object
|
249
|
-
end
|
250
|
-
|
251
|
-
def process(params)
|
252
|
-
raise "This is not run!"
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
it { ContractOnlyOperation.present({}).contract._model.must_equal Object }
|
257
|
-
end
|
258
|
-
|
259
|
-
|
260
|
-
class OperationErrorsTest < MiniTest::Spec
|
261
|
-
class Operation < Trailblazer::Operation
|
262
|
-
contract do
|
263
|
-
property :title, validates: {presence: true}
|
264
|
-
end
|
265
|
-
|
266
|
-
def process(params)
|
267
|
-
validate(params, OpenStruct.new) {}
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
it do
|
272
|
-
res, op = Operation.run({})
|
273
|
-
op.errors.to_s.must_equal "{:title=>[\"can't be blank\"]}"
|
274
|
-
end
|
275
|
-
end
|
data/test/representer_test.rb
DELETED
@@ -1,238 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
require "representable/json"
|
4
|
-
|
5
|
-
class RepresenterTest < MiniTest::Spec
|
6
|
-
Album = Struct.new(:title, :artist)
|
7
|
-
Artist = Struct.new(:name)
|
8
|
-
|
9
|
-
class Create < Trailblazer::Operation
|
10
|
-
require "trailblazer/operation/representer"
|
11
|
-
include Representer
|
12
|
-
|
13
|
-
contract do
|
14
|
-
property :title
|
15
|
-
validates :title, presence: true
|
16
|
-
property :artist, populate_if_empty: Artist do
|
17
|
-
property :name
|
18
|
-
validates :name, presence: true
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def process(params)
|
23
|
-
@model = Album.new # NO artist!!!
|
24
|
-
validate(params[:album], @model)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
|
29
|
-
# Infers representer from contract, no customization.
|
30
|
-
class Show < Create
|
31
|
-
def process(params)
|
32
|
-
@model = Album.new("After The War", Artist.new("Gary Moore"))
|
33
|
-
@contract = @model
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
|
38
|
-
# Infers representer, adds hypermedia.
|
39
|
-
require "roar/json/hal"
|
40
|
-
class HypermediaCreate < Create
|
41
|
-
representer do
|
42
|
-
include Roar::JSON::HAL
|
43
|
-
|
44
|
-
link(:self) { "//album/#{represented.title}" }
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
class HypermediaShow < HypermediaCreate
|
49
|
-
def process(params)
|
50
|
-
@model = Album.new("After The War", Artist.new("Gary Moore"))
|
51
|
-
@contract = @model
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
|
56
|
-
# rendering
|
57
|
-
# generic contract -> representer
|
58
|
-
it do
|
59
|
-
res, op = Show.run({})
|
60
|
-
op.to_json.must_equal %{{"title":"After The War","artist":{"name":"Gary Moore"}}}
|
61
|
-
end
|
62
|
-
|
63
|
-
# contract -> representer with hypermedia
|
64
|
-
it do
|
65
|
-
res, op = HypermediaShow.run({})
|
66
|
-
op.to_json.must_equal %{{"title":"After The War","artist":{"name":"Gary Moore"},"_links":{"self":{"href":"//album/After The War"}}}}
|
67
|
-
end
|
68
|
-
|
69
|
-
|
70
|
-
# parsing
|
71
|
-
it do
|
72
|
-
res, op = Create.run(album: %{{"title":"Run For Cover","artist":{"name":"Gary Moore"}}})
|
73
|
-
op.contract.title.must_equal "Run For Cover"
|
74
|
-
op.contract.artist.name.must_equal "Gary Moore"
|
75
|
-
end
|
76
|
-
|
77
|
-
it do
|
78
|
-
res, op = HypermediaCreate.run(album: %{{"title":"After The War","artist":{"name":"Gary Moore"},"_links":{"self":{"href":"//album/After The War"}}}})
|
79
|
-
op.contract.title.must_equal "After The War"
|
80
|
-
op.contract.artist.name.must_equal "Gary Moore"
|
81
|
-
end
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
# explicit representer set with ::representer_class=.
|
88
|
-
require "roar/decorator"
|
89
|
-
class JsonApiCreate < Trailblazer::Operation
|
90
|
-
include Representer
|
91
|
-
|
92
|
-
contract do # we still need contract as the representer writes to the contract twin.
|
93
|
-
property :title
|
94
|
-
end
|
95
|
-
|
96
|
-
class AlbumRepresenter < Roar::Decorator
|
97
|
-
include Roar::JSON
|
98
|
-
property :title
|
99
|
-
end
|
100
|
-
self.representer_class = AlbumRepresenter
|
101
|
-
|
102
|
-
def process(params)
|
103
|
-
@model = Album.new # NO artist!!!
|
104
|
-
validate(params[:album], @model)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
class JsonApiShow < JsonApiCreate
|
109
|
-
def process(params)
|
110
|
-
@model = Album.new("After The War", Artist.new("Gary Moore"))
|
111
|
-
@contract = @model
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
# render.
|
116
|
-
it do
|
117
|
-
res, op = JsonApiShow.run({})
|
118
|
-
op.to_json.must_equal %{{"title":"After The War"}}
|
119
|
-
end
|
120
|
-
|
121
|
-
# parse.
|
122
|
-
it do
|
123
|
-
res, op = JsonApiCreate.run(album: %{{"title":"Run For Cover"}})
|
124
|
-
op.contract.title.must_equal "Run For Cover"
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
class InternalRepresenterAPITest < MiniTest::Spec
|
129
|
-
Song = Struct.new(:id)
|
130
|
-
|
131
|
-
describe "#represented" do
|
132
|
-
class Show < Trailblazer::Operation
|
133
|
-
include Representer, Model
|
134
|
-
model Song, :create
|
135
|
-
|
136
|
-
representer do
|
137
|
-
property :class
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
it "uses #model as represented, per default" do
|
142
|
-
Show.present({}).to_json.must_equal '{"class":"InternalRepresenterAPITest::Song"}'
|
143
|
-
end
|
144
|
-
|
145
|
-
class ShowContract < Show
|
146
|
-
def represented
|
147
|
-
contract
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
it "can be overriden to use the contract" do
|
152
|
-
ShowContract.present({}).to_json.must_equal %{{"class":"#{ShowContract.contract_class}"}}
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
describe "#to_json" do
|
157
|
-
class OptionsShow < Trailblazer::Operation
|
158
|
-
include Representer
|
159
|
-
|
160
|
-
representer do
|
161
|
-
property :class
|
162
|
-
property :id
|
163
|
-
end
|
164
|
-
|
165
|
-
def to_json(*)
|
166
|
-
super(@params)
|
167
|
-
end
|
168
|
-
|
169
|
-
def model!(params)
|
170
|
-
Song.new(1)
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
it "allows to pass options to #to_json" do
|
175
|
-
OptionsShow.present(include: [:id]).to_json.must_equal '{"id":1}'
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
class DifferentParseAndRenderingRepresenterTest < MiniTest::Spec
|
181
|
-
Album = Struct.new(:title)
|
182
|
-
|
183
|
-
# rendering
|
184
|
-
class Create < Trailblazer::Operation
|
185
|
-
extend Representer::DSL
|
186
|
-
include Representer::Rendering # no Deserializer::Hash here or anything.
|
187
|
-
|
188
|
-
contract do
|
189
|
-
property :title
|
190
|
-
end
|
191
|
-
|
192
|
-
representer do
|
193
|
-
property :title, as: :Title
|
194
|
-
end
|
195
|
-
|
196
|
-
def process(params)
|
197
|
-
@model = Album.new
|
198
|
-
validate(params) do
|
199
|
-
contract.sync
|
200
|
-
end
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
it do
|
205
|
-
Create.(title: "The Kids").to_json.must_equal %{{"Title":"The Kids"}}
|
206
|
-
end
|
207
|
-
|
208
|
-
# parsing
|
209
|
-
class Update < Trailblazer::Operation
|
210
|
-
extend Representer::DSL
|
211
|
-
include Representer::Deserializer::Hash # no Rendering.
|
212
|
-
|
213
|
-
representer do
|
214
|
-
property :title, as: :Title
|
215
|
-
end
|
216
|
-
|
217
|
-
contract do
|
218
|
-
property :title
|
219
|
-
end
|
220
|
-
|
221
|
-
|
222
|
-
def process(params)
|
223
|
-
@model = Album.new
|
224
|
-
|
225
|
-
validate(params) do
|
226
|
-
contract.sync
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
def to_json(*)
|
231
|
-
%{{"title": "#{model.title}"}}
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
it do
|
236
|
-
Update.("Title" => "The Kids").to_json.must_equal %{{"title": "The Kids"}}
|
237
|
-
end
|
238
|
-
end
|
data/test/rollback_test.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
# TODO: rollback for composed operations: this is basically implemented already as every operation knows how to rollback.
|
4
|
-
# however, this has to be run for composed operations.
|
5
|
-
# we can also add Transaction and Lock for real uniqueness validators, etc.
|
6
|
-
#
|
7
|
-
# i am keen to try integrating https://github.com/collectiveidea/interactor organizers!
|
8
|
-
module Trailblazer::Operation::Rollback
|
9
|
-
def run
|
10
|
-
begin
|
11
|
-
super
|
12
|
-
rescue
|
13
|
-
rollback!(@params, $!)
|
14
|
-
[false, self]
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
class RollbackTest < MiniTest::Spec
|
20
|
-
class ExceptionalOperation < Trailblazer::Operation
|
21
|
-
include Rollback
|
22
|
-
|
23
|
-
def process(params)
|
24
|
-
@_params = params
|
25
|
-
raise # something happens.
|
26
|
-
end
|
27
|
-
|
28
|
-
attr_reader :_params, :_rollback_args
|
29
|
-
|
30
|
-
def rollback!(params, exception)
|
31
|
-
@_rollback_args = [params, exception]
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
module Comparable
|
36
|
-
def ==(other)
|
37
|
-
self.class == other.class
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
it do
|
42
|
-
op = ExceptionalOperation.("amazing")
|
43
|
-
op._params.must_equal "amazing"
|
44
|
-
|
45
|
-
op._rollback_args.must_equal ["amazing", RuntimeError.new.extend(Comparable)]
|
46
|
-
end
|
47
|
-
end
|