trailblazer 2.0.7 → 2.1.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/CHANGES.md +35 -1
- data/Gemfile +6 -12
- data/README.md +3 -1
- data/Rakefile +6 -17
- data/lib/trailblazer.rb +7 -4
- data/lib/trailblazer/deprecation/call.rb +46 -0
- data/lib/trailblazer/deprecation/context.rb +43 -0
- data/lib/trailblazer/operation/contract.rb +40 -9
- data/lib/trailblazer/operation/deprecations.rb +21 -0
- data/lib/trailblazer/operation/guard.rb +5 -5
- data/lib/trailblazer/operation/model.rb +15 -10
- data/lib/trailblazer/operation/nested.rb +56 -85
- data/lib/trailblazer/operation/persist.rb +4 -2
- data/lib/trailblazer/operation/policy.rb +16 -7
- data/lib/trailblazer/operation/pundit.rb +3 -3
- data/lib/trailblazer/operation/representer.rb +5 -0
- data/lib/trailblazer/operation/rescue.rb +12 -9
- data/lib/trailblazer/operation/validate.rb +36 -29
- data/lib/trailblazer/operation/wrap.rb +49 -11
- data/lib/trailblazer/task.rb +20 -0
- data/lib/trailblazer/version.rb +1 -1
- data/test/benchmark.rb +63 -0
- data/test/deprecation/call_test.rb +42 -0
- data/test/deprecation/context_test.rb +19 -0
- data/test/docs/contract_test.rb +73 -53
- data/test/docs/dry_test.rb +2 -2
- data/test/docs/fast_test.rb +133 -13
- data/test/docs/guard_test.rb +28 -35
- data/test/docs/macro_test.rb +1 -1
- data/test/docs/model_test.rb +13 -13
- data/test/docs/nested_test.rb +54 -122
- data/test/docs/operation_test.rb +42 -43
- data/test/docs/pundit_test.rb +16 -16
- data/test/docs/representer_test.rb +18 -18
- data/test/docs/rescue_test.rb +29 -29
- data/test/docs/trace_test.rb +82 -0
- data/test/docs/wrap_test.rb +59 -26
- data/test/module_test.rb +75 -75
- data/test/nested_test.rb +293 -0
- data/test/operation/contract_test.rb +23 -153
- data/test/operation/dsl/contract_test.rb +9 -9
- data/test/operation/dsl/representer_test.rb +169 -169
- data/test/operation/model_test.rb +15 -21
- data/test/operation/persist_test.rb +18 -11
- data/test/operation/pundit_test.rb +25 -23
- data/test/operation/representer_test.rb +254 -254
- data/test/test_helper.rb +5 -2
- data/test/variables_test.rb +158 -0
- data/trailblazer.gemspec +1 -1
- data/untitled +33 -0
- metadata +25 -27
- data/lib/trailblazer/operation/callback.rb +0 -35
- data/lib/trailblazer/operation/procedural/contract.rb +0 -15
- data/lib/trailblazer/operation/procedural/validate.rb +0 -22
- data/test/operation/callback_test.rb +0 -70
- data/test/operation/dsl/callback_test.rb +0 -106
- data/test/operation/params_test.rb +0 -36
- data/test/operation/pipedream_test.rb +0 -59
- data/test/operation/pipetree_test.rb +0 -104
- data/test/operation/present_test.rb +0 -24
- data/test/operation/resolver_test.rb +0 -47
- data/test/operation_test.rb +0 -143
@@ -1,62 +1,14 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
|
-
class ContractExtractMacroTest < Minitest::Spec
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
-
|
14
|
-
|
15
|
-
require "dry/validation"
|
16
|
-
|
17
|
-
class DryValidationTest < Minitest::Spec
|
18
|
-
class Create < Trailblazer::Operation
|
19
|
-
extend Contract::DSL
|
20
|
-
|
21
|
-
contract "params", (Dry::Validation.Schema do
|
22
|
-
required(:id).filled
|
23
|
-
end)
|
24
|
-
# self["contract.params"] = Dry::Validation.Schema do
|
25
|
-
# required(:id).filled
|
26
|
-
# end
|
27
|
-
|
28
|
-
step :process
|
29
|
-
|
30
|
-
include Procedural::Validate
|
31
|
-
|
32
|
-
def process(options)
|
33
|
-
validate(options["params"], contract: self["contract.params"], path: "contract.params") { |f| puts f.inspect }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
#- result object, contract
|
38
|
-
# success
|
39
|
-
it { Create.(id: 1)["result.contract.params"].success?.must_equal true }
|
40
|
-
it { Create.(id: 1)["result.contract.params"].errors.must_equal({}) }
|
41
|
-
# failure
|
42
|
-
it { Create.(id: nil)["result.contract.params"].success?.must_equal false }
|
43
|
-
it { Create.(id: nil)["result.contract.params"].errors.must_equal({:id=>["must be filled"]}) }
|
44
|
-
|
45
|
-
#---
|
46
|
-
# with Contract::Validate, but before op even gets instantiated.
|
47
|
-
class Update < Trailblazer::Operation #["contract"]
|
48
|
-
extend Contract::DSL
|
49
|
-
|
50
|
-
contract "params", (Dry::Validation.Schema do
|
51
|
-
required(:id).filled
|
52
|
-
end)
|
53
|
-
|
54
|
-
step ->(options) { options["contract.params"].(options["params"]).success? }, before: "operation.new"
|
55
|
-
end
|
3
|
+
# class ContractExtractMacroTest < Minitest::Spec
|
4
|
+
# class Create < Trailblazer::Operation
|
5
|
+
# step Contract::Validate::Extract( key: "song", params_path: "x" )
|
6
|
+
# end
|
56
7
|
|
57
|
-
|
58
|
-
|
59
|
-
|
8
|
+
# it { Trailblazer::Operation::Inspect.(Create).must_equal %{[>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
|
60
12
|
|
61
13
|
class ContractTest < Minitest::Spec
|
62
14
|
Song = Struct.new(:title)
|
@@ -71,35 +23,6 @@ class ContractTest < Minitest::Spec
|
|
71
23
|
# end
|
72
24
|
# end
|
73
25
|
|
74
|
-
#---
|
75
|
-
# contract do..end (without constant)
|
76
|
-
#- explicit validate call
|
77
|
-
describe "contract do .. end" do
|
78
|
-
class Index < Trailblazer::Operation
|
79
|
-
extend Contract::DSL
|
80
|
-
|
81
|
-
contract do
|
82
|
-
property :title
|
83
|
-
end
|
84
|
-
|
85
|
-
success ->(options) { options["model"] = Song.new }
|
86
|
-
# step Model( Song, :new )
|
87
|
-
step Contract::Build()
|
88
|
-
step :process
|
89
|
-
|
90
|
-
include Procedural::Validate
|
91
|
-
# TODO: get model automatically in validate!
|
92
|
-
|
93
|
-
def process(options)
|
94
|
-
# validate(params, model: Song.new) { |f| self["x"] = f.to_nested_hash }
|
95
|
-
validate(options["params"]) { |f| self["x"] = f.to_nested_hash }
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# will create a Reform::Form for us.
|
100
|
-
it { Index.(title: "Falling Down")["x"].must_equal({"title"=>"Falling Down"}) }
|
101
|
-
end
|
102
|
-
|
103
26
|
# # TODO: in all step tests.
|
104
27
|
# describe "dependency injection" do
|
105
28
|
# class Delete < Trailblazer::Operation
|
@@ -208,69 +131,17 @@ class ContractTest < Minitest::Spec
|
|
208
131
|
#---
|
209
132
|
#- validate
|
210
133
|
class ValidateTest < Minitest::Spec
|
211
|
-
class Create < Trailblazer::Operation
|
212
|
-
extend Contract::DSL
|
213
|
-
contract do
|
214
|
-
property :title
|
215
|
-
validates :title, presence: true
|
216
|
-
end
|
217
|
-
|
218
|
-
|
219
|
-
include Procedural::Validate
|
220
|
-
def process(options)
|
221
|
-
if validate(options["params"])
|
222
|
-
self["x"] = "works!"
|
223
|
-
true
|
224
|
-
else
|
225
|
-
self["x"] = "try again"
|
226
|
-
false
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
step Model( Song, :new ) # FIXME.
|
231
|
-
step Contract::Build()
|
232
|
-
step :process
|
233
|
-
end
|
234
|
-
|
235
|
-
# validate returns the #validate result
|
236
|
-
it do
|
237
|
-
Create.(title: nil)["x"].must_equal "try again"
|
238
|
-
Create.(title: nil).success?.must_equal false
|
239
|
-
end
|
240
|
-
it { Create.(title: "SVG")["x"].must_equal "works!" }
|
241
|
-
it { Create.(title: "SVG").success?.must_equal true }
|
242
|
-
|
243
|
-
# result object from validation.
|
244
|
-
#- result object, contract
|
245
|
-
it { Create.(title: 1)["result.contract.default"].success?.must_equal true }
|
246
|
-
it { Create.(title: 1)["result.contract.default"].errors.messages.must_equal({}) } # FIXME: change API with Fran.
|
247
|
-
it { Create.(title: nil)["result.contract.default"].success?.must_equal false }
|
248
|
-
it { Create.(title: nil)["result.contract.default"].errors.messages.must_equal({:title=>["can't be blank"]}) } # FIXME: change API with Fran.
|
249
|
-
# #---
|
250
|
-
# # validate with block returns result.
|
251
|
-
# class Update < Trailblazer::Operation
|
252
|
-
# include Contract::Explicit
|
253
|
-
# contract Form
|
254
|
-
|
255
|
-
# def process(params)
|
256
|
-
# self["x"] = validate(params) { }
|
257
|
-
# end
|
258
|
-
# end
|
259
|
-
|
260
|
-
# it { Update.(false)["x"].must_equal false}
|
261
|
-
# it { Update.(true)["x"]. must_equal true}
|
262
134
|
|
263
135
|
#---
|
264
136
|
# Contract::Validate[]
|
265
137
|
class Update < Trailblazer::Operation
|
266
|
-
|
267
|
-
contract do
|
138
|
+
class Form < Reform::Form
|
268
139
|
property :title
|
269
140
|
validates :title, presence: true
|
270
141
|
end
|
271
142
|
|
272
|
-
step Model( Song, :new )
|
273
|
-
step Contract::Build()
|
143
|
+
step Model( Song, :new )
|
144
|
+
step Contract::Build( constant: Form )
|
274
145
|
step Contract::Validate() # generic validate call for you.
|
275
146
|
|
276
147
|
# include Procedural::Validate
|
@@ -279,7 +150,7 @@ class ValidateTest < Minitest::Spec
|
|
279
150
|
|
280
151
|
# success
|
281
152
|
it do
|
282
|
-
result = Update.(title: "SVG")
|
153
|
+
result = Update.(params: {title: "SVG"})
|
283
154
|
result.success?.must_equal true
|
284
155
|
result["result.contract.default"].success?.must_equal true
|
285
156
|
result["result.contract.default"].errors.messages.must_equal({})
|
@@ -287,7 +158,7 @@ class ValidateTest < Minitest::Spec
|
|
287
158
|
|
288
159
|
# failure
|
289
160
|
it do
|
290
|
-
result = Update.(title: nil)
|
161
|
+
result = Update.(params: {title: nil})
|
291
162
|
result.success?.must_equal false
|
292
163
|
result["result.contract.default"].success?.must_equal false
|
293
164
|
result["result.contract.default"].errors.messages.must_equal({:title=>["can't be blank"]})
|
@@ -296,45 +167,44 @@ class ValidateTest < Minitest::Spec
|
|
296
167
|
#---
|
297
168
|
# Contract::Validate[key: :song]
|
298
169
|
class Upsert < Trailblazer::Operation
|
299
|
-
|
300
|
-
contract do
|
170
|
+
class Form < Reform::Form
|
301
171
|
property :title
|
302
172
|
validates :title, presence: true
|
303
173
|
end
|
304
174
|
|
305
175
|
step Model( Song, :new ) # FIXME.
|
306
|
-
step Contract::Build()
|
176
|
+
step Contract::Build( constant: Form )
|
307
177
|
step Contract::Validate( key: :song) # generic validate call for you.
|
308
178
|
# ->(*) { validate(options["params"][:song]) } # <-- TODO
|
309
179
|
step Contract::Persist( method: :sync )
|
310
180
|
end
|
311
181
|
|
312
182
|
# success
|
313
|
-
it { Upsert.(song: { title: "SVG" }).success?.must_equal true }
|
183
|
+
it { Upsert.(params: {song: { title: "SVG" }}).success?.must_equal true }
|
314
184
|
# failure
|
315
|
-
it { Upsert.(song: { title: nil }).success?.must_equal false }
|
185
|
+
it { Upsert.(params: {song: { title: nil }}).success?.must_equal false }
|
316
186
|
# key not found
|
317
|
-
it { Upsert.().success?.must_equal false }
|
187
|
+
it { Upsert.(params: {}).success?.must_equal false }
|
318
188
|
|
319
189
|
#---
|
320
190
|
# contract.default.params gets set (TODO: change in 2.1)
|
321
|
-
it { Upsert.(song: { title: "SVG" })[
|
322
|
-
it { Upsert.(song: { title: "SVG" })["contract.default.params"].must_equal({:title=>"SVG"}) }
|
191
|
+
it { Upsert.( params: {song: { title: "SVG" }})[:params].must_equal({:song=>{:title=>"SVG"}} ) }
|
192
|
+
it { Upsert.( params: {song: { title: "SVG" }})["contract.default.params"].must_equal({:title=>"SVG"} ) }
|
323
193
|
|
324
194
|
#---
|
325
195
|
#- inheritance
|
326
196
|
class New < Upsert
|
327
197
|
end
|
328
198
|
|
329
|
-
it { New
|
199
|
+
it { Trailblazer::Operation::Inspect.(New).must_equal %{[>model.build,>contract.build,>contract.default.validate,>persist.save]} }
|
330
200
|
|
331
201
|
#- overwriting Validate
|
332
202
|
class NewHit < Upsert
|
333
203
|
step Contract::Validate( key: :hit ), override: true
|
334
204
|
end
|
335
205
|
|
336
|
-
it { NewHit
|
337
|
-
it { NewHit.(:hit => { title: "Hooray For Me" }).inspect(
|
206
|
+
it { Trailblazer::Operation::Inspect.(NewHit).must_equal %{[>model.build,>contract.build,>contract.default.validate,>persist.save]} }
|
207
|
+
it { NewHit.(params: {:hit => { title: "Hooray For Me" }}).inspect(:model).must_equal %{<Result:true [#<struct ContractTest::Song title=\"Hooray For Me\">] >} }
|
338
208
|
end
|
339
209
|
|
340
210
|
# #---
|
@@ -56,7 +56,7 @@ class DslContractTest < MiniTest::Spec
|
|
56
56
|
# UT: subclasses contract.
|
57
57
|
it { Update["contract.default.class"].superclass.must_equal Update::IdContract }
|
58
58
|
# IT: only knows `id`.
|
59
|
-
it { Update.(id: 1, title: "Coaster")[
|
59
|
+
it { Update.(params: {id: 1, title: "Coaster"})[:model].inspect.must_equal %{#<OpenStruct id=1>} }
|
60
60
|
|
61
61
|
# Op::contract with inheritance
|
62
62
|
# no ::contract call.
|
@@ -67,7 +67,7 @@ class DslContractTest < MiniTest::Spec
|
|
67
67
|
it { Upgrade["contract.default.class"].superclass.must_equal Update::IdContract }
|
68
68
|
it { Upgrade["contract.default.class"].wont_equal Update["contract.default.class"] }
|
69
69
|
# IT: only knows `id`.
|
70
|
-
it { Upgrade.(id: 1, title: "Coaster")[
|
70
|
+
it { Upgrade.(params: {id: 1, title: "Coaster"})[:model].inspect.must_equal %{#<OpenStruct id=1>} }
|
71
71
|
|
72
72
|
# ::contract B overrides old A contract.
|
73
73
|
# this makes sure when calling contract(Constant), the old class gets wiped and is replaced with the new constant.
|
@@ -82,7 +82,7 @@ class DslContractTest < MiniTest::Spec
|
|
82
82
|
# UT: subclasses contract.
|
83
83
|
it { Upsert["contract.default.class"].superclass.must_equal Upsert::TitleContract }
|
84
84
|
# IT: only knows `title`.
|
85
|
-
it { Upsert.(id: 1, title: "Coaster")[
|
85
|
+
it { Upsert.(params: {id: 1, title: "Coaster"})[:model].inspect.must_equal %{#<OpenStruct title="Coaster">} }
|
86
86
|
|
87
87
|
# ::contract B do ..end overrides and extends new.
|
88
88
|
# using a constant will wipe out the existing class.
|
@@ -95,7 +95,7 @@ class DslContractTest < MiniTest::Spec
|
|
95
95
|
# UT: subclasses contract.
|
96
96
|
it { Upside["contract.default.class"].superclass.must_equal Upsert::TitleContract }
|
97
97
|
# IT: only knows `title`.
|
98
|
-
it { Upside.(id: 1, title: "Coaster")[
|
98
|
+
it { Upside.(params: {id: 1, title: "Coaster"})[:model].inspect.must_equal %{#<OpenStruct title="Coaster", id=1>} }
|
99
99
|
|
100
100
|
|
101
101
|
|
@@ -113,7 +113,7 @@ class DslContractTest < MiniTest::Spec
|
|
113
113
|
# UT: contract path is "contract.default.class"
|
114
114
|
it { Delete["contract.default.class"].definitions.keys.must_equal ["title"] }
|
115
115
|
# IT: knows `title`.
|
116
|
-
it { Delete.(id: 1, title: "Coaster")[
|
116
|
+
it { Delete.(params: {id: 1, title: "Coaster"})[:model].inspect.must_equal %{#<OpenStruct title=\"Coaster\">} }
|
117
117
|
|
118
118
|
class Wipe < Trailblazer::Operation
|
119
119
|
extend Contract::DSL
|
@@ -138,7 +138,7 @@ class DslContractTest < MiniTest::Spec
|
|
138
138
|
end
|
139
139
|
|
140
140
|
# IT: knows `title` and `id`, since contracts get merged.
|
141
|
-
it { Remove.(id: 1, title: "Coaster")[
|
141
|
+
it { Remove.(params: {id: 1, title: "Coaster"})[:model].inspect.must_equal %{#<OpenStruct title=\"Coaster\", id=1>} }
|
142
142
|
|
143
143
|
|
144
144
|
|
@@ -212,7 +212,7 @@ class DslContractTest < MiniTest::Spec
|
|
212
212
|
include Call
|
213
213
|
end
|
214
214
|
|
215
|
-
it { OpWithExternalContract.("songTitle"=> "Monsterparty")["contract.default"].songTitle.must_equal "Monsterparty" }
|
215
|
+
it { OpWithExternalContract.(params: {"songTitle"=> "Monsterparty"})["contract.default"].songTitle.must_equal "Monsterparty" }
|
216
216
|
end
|
217
217
|
|
218
218
|
describe "Op.contract CommentForm do .. end" do
|
@@ -236,14 +236,14 @@ class DslContractTest < MiniTest::Spec
|
|
236
236
|
|
237
237
|
# this operation copies DifferentSongForm and shouldn't have `genre`.
|
238
238
|
it do
|
239
|
-
contract = OpNotExtendingContract.("songTitle"=>"Monsterparty", "genre"=>"Punk")["contract.default"]
|
239
|
+
contract = OpNotExtendingContract.(params: {"songTitle"=>"Monsterparty", "genre"=>"Punk"})["contract.default"]
|
240
240
|
contract.songTitle.must_equal "Monsterparty"
|
241
241
|
assert_raises(NoMethodError) { contract.genre }
|
242
242
|
end
|
243
243
|
|
244
244
|
# this operation copies DifferentSongForm and extends it with the property `genre`.
|
245
245
|
it do
|
246
|
-
contract = OpExtendingContract.("songTitle"=>"Monsterparty", "genre"=>"Punk")["contract.default"]
|
246
|
+
contract = OpExtendingContract.(params: {"songTitle"=>"Monsterparty", "genre"=>"Punk"})["contract.default"]
|
247
247
|
contract.songTitle.must_equal "Monsterparty"
|
248
248
|
contract.genre.must_equal "Punk"
|
249
249
|
end
|
@@ -1,169 +1,169 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
require "representable/json"
|
3
|
-
require "trailblazer/operation/representer"
|
4
|
-
require "trailblazer/operation/contract"
|
5
|
-
|
6
|
-
class DslRepresenterTest < MiniTest::Spec
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
end
|
1
|
+
# require "test_helper"
|
2
|
+
# require "representable/json"
|
3
|
+
# require "trailblazer/operation/representer"
|
4
|
+
# require "trailblazer/operation/contract"
|
5
|
+
|
6
|
+
# class DslRepresenterTest < MiniTest::Spec
|
7
|
+
# module SongProcess
|
8
|
+
# def process(params)
|
9
|
+
# self["model"] = OpenStruct.new(params)
|
10
|
+
# end
|
11
|
+
|
12
|
+
# def represented
|
13
|
+
# self["model"]
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
|
17
|
+
# describe "inheritance across operations" do
|
18
|
+
# class Operation < Trailblazer::Operation
|
19
|
+
# include Representer
|
20
|
+
# include SongProcess
|
21
|
+
|
22
|
+
# representer do
|
23
|
+
# property :title
|
24
|
+
# end
|
25
|
+
|
26
|
+
# class JSON < self
|
27
|
+
# representer do
|
28
|
+
# property :band
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
|
33
|
+
# it { Operation.(title: "Nothing To Lose", band: "Gary Moore").to_json.must_equal %{{"title":"Nothing To Lose"}} }
|
34
|
+
# # only the subclass must have the `band` field, even though it's set in the original operation.
|
35
|
+
# it { Operation::JSON.(title: "Nothing To Lose", band: "Gary Moore").to_json.must_equal %{{"title":"Nothing To Lose","band":"Gary Moore"}} }
|
36
|
+
# end
|
37
|
+
|
38
|
+
# describe "Op.representer CommentRepresenter" do
|
39
|
+
# class SongRepresenter < Representable::Decorator
|
40
|
+
# include Representable::JSON
|
41
|
+
# property :songTitle
|
42
|
+
# end
|
43
|
+
|
44
|
+
# class OpWithExternalRepresenter < Trailblazer::Operation
|
45
|
+
# include Representer
|
46
|
+
# include SongProcess
|
47
|
+
# representer SongRepresenter
|
48
|
+
# end
|
49
|
+
|
50
|
+
# it { OpWithExternalRepresenter.("songTitle"=>"Listen To Your Heartbeat").to_json.must_equal %{{"songTitle":"Listen To Your Heartbeat"}} }
|
51
|
+
# end
|
52
|
+
|
53
|
+
# # name for representer
|
54
|
+
# describe "1) Op.representer :parse, Representer" do
|
55
|
+
# class Op1 < Trailblazer::Operation
|
56
|
+
# include Representer
|
57
|
+
# representer :parse, String
|
58
|
+
# end
|
59
|
+
|
60
|
+
# it { Op1["representer.parse.class"].superclass.must_equal String }
|
61
|
+
# it { Op1.({})["representer.parse.class"].superclass.must_equal String }
|
62
|
+
# end
|
63
|
+
|
64
|
+
# # name for default representer
|
65
|
+
# describe "2) Op.representer Representer" do
|
66
|
+
# class Op2 < Trailblazer::Operation
|
67
|
+
# include Representer
|
68
|
+
# representer String
|
69
|
+
# def call(*); self; end
|
70
|
+
# end
|
71
|
+
|
72
|
+
# it { Op2["representer.default.class"].superclass.must_equal String }
|
73
|
+
# it { Op2.({})["representer.default.class"].superclass.must_equal String }
|
74
|
+
# it { Op2.({}, "representer.default.class" => Integer)["representer.default.class"].must_equal Integer }
|
75
|
+
# end
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
# describe "Op.representer CommentRepresenter do .. end" do
|
80
|
+
# class HitRepresenter < Representable::Decorator
|
81
|
+
# include Representable::JSON
|
82
|
+
# property :title
|
83
|
+
# end
|
84
|
+
|
85
|
+
# class OpNotExtendingRepresenter < Trailblazer::Operation
|
86
|
+
# include Representer
|
87
|
+
# include SongProcess
|
88
|
+
# representer HitRepresenter
|
89
|
+
# end
|
90
|
+
|
91
|
+
# class OpExtendingRepresenter < Trailblazer::Operation
|
92
|
+
# include Representer
|
93
|
+
# include SongProcess
|
94
|
+
# representer HitRepresenter do
|
95
|
+
# property :genre
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
|
99
|
+
# # this operation copies HitRepresenter and shouldn't have `genre`.
|
100
|
+
# it do
|
101
|
+
# OpNotExtendingRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty"}}
|
102
|
+
# end
|
103
|
+
|
104
|
+
# # # this operation copies HitRepresenter and extends it with the property `genre`.
|
105
|
+
# it do
|
106
|
+
# OpExtendingRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty","genre":"Punk"}}
|
107
|
+
# end
|
108
|
+
|
109
|
+
# # # of course, the original representer wasn't modified, either.
|
110
|
+
# it do
|
111
|
+
# HitRepresenter.new(OpenStruct.new(title: "Monsterparty", genre: "Punk")).to_json.must_equal %{{"title":"Monsterparty"}}
|
112
|
+
# end
|
113
|
+
# end
|
114
|
+
|
115
|
+
# describe "Op.representer (inferring)" do
|
116
|
+
# class ContractOperation < Trailblazer::Operation
|
117
|
+
# include Representer
|
118
|
+
# include Representer::InferFromContract
|
119
|
+
# include SongProcess
|
120
|
+
# include Contract::Explicit
|
121
|
+
|
122
|
+
# contract do
|
123
|
+
# property :songTitle
|
124
|
+
# end
|
125
|
+
# end
|
126
|
+
|
127
|
+
# class ContractOperation2 < Trailblazer::Operation
|
128
|
+
# include Representer
|
129
|
+
# include SongProcess
|
130
|
+
# include Contract::Explicit
|
131
|
+
# include Representer::InferFromContract
|
132
|
+
# contract ContractOperation["contract.default.class"]
|
133
|
+
|
134
|
+
# representer do
|
135
|
+
# property :genre
|
136
|
+
# end
|
137
|
+
# end
|
138
|
+
|
139
|
+
# it { ContractOperation.("songTitle"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"songTitle":"Monsterparty"}} }
|
140
|
+
# # this representer block extends the inferred from contract.
|
141
|
+
# it { ContractOperation2.("songTitle"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"songTitle":"Monsterparty","genre":"Punk"}} }
|
142
|
+
# end
|
143
|
+
|
144
|
+
# describe "Op.representer_class" do
|
145
|
+
# class PlayRepresenter < Representable::Decorator
|
146
|
+
# include Representable::JSON
|
147
|
+
# property :title
|
148
|
+
# end
|
149
|
+
|
150
|
+
# class OpSettingRepresenter < Trailblazer::Operation
|
151
|
+
# include Representer
|
152
|
+
# include SongProcess
|
153
|
+
# self["representer.default.class"] = PlayRepresenter
|
154
|
+
# end
|
155
|
+
|
156
|
+
# class OpExtendRepresenter < Trailblazer::Operation
|
157
|
+
# include Representer
|
158
|
+
# include SongProcess
|
159
|
+
# self["representer.default.class"] = PlayRepresenter
|
160
|
+
# representer do
|
161
|
+
# property :genre
|
162
|
+
# end
|
163
|
+
# end
|
164
|
+
|
165
|
+
# # both operations produce the same as the representer is shared, not copied.
|
166
|
+
# it { skip; OpSettingRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty","genre":"Punk"}} }
|
167
|
+
# it { OpExtendRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty","genre":"Punk"}} }
|
168
|
+
# end
|
169
|
+
# end
|