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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +35 -1
  3. data/Gemfile +6 -12
  4. data/README.md +3 -1
  5. data/Rakefile +6 -17
  6. data/lib/trailblazer.rb +7 -4
  7. data/lib/trailblazer/deprecation/call.rb +46 -0
  8. data/lib/trailblazer/deprecation/context.rb +43 -0
  9. data/lib/trailblazer/operation/contract.rb +40 -9
  10. data/lib/trailblazer/operation/deprecations.rb +21 -0
  11. data/lib/trailblazer/operation/guard.rb +5 -5
  12. data/lib/trailblazer/operation/model.rb +15 -10
  13. data/lib/trailblazer/operation/nested.rb +56 -85
  14. data/lib/trailblazer/operation/persist.rb +4 -2
  15. data/lib/trailblazer/operation/policy.rb +16 -7
  16. data/lib/trailblazer/operation/pundit.rb +3 -3
  17. data/lib/trailblazer/operation/representer.rb +5 -0
  18. data/lib/trailblazer/operation/rescue.rb +12 -9
  19. data/lib/trailblazer/operation/validate.rb +36 -29
  20. data/lib/trailblazer/operation/wrap.rb +49 -11
  21. data/lib/trailblazer/task.rb +20 -0
  22. data/lib/trailblazer/version.rb +1 -1
  23. data/test/benchmark.rb +63 -0
  24. data/test/deprecation/call_test.rb +42 -0
  25. data/test/deprecation/context_test.rb +19 -0
  26. data/test/docs/contract_test.rb +73 -53
  27. data/test/docs/dry_test.rb +2 -2
  28. data/test/docs/fast_test.rb +133 -13
  29. data/test/docs/guard_test.rb +28 -35
  30. data/test/docs/macro_test.rb +1 -1
  31. data/test/docs/model_test.rb +13 -13
  32. data/test/docs/nested_test.rb +54 -122
  33. data/test/docs/operation_test.rb +42 -43
  34. data/test/docs/pundit_test.rb +16 -16
  35. data/test/docs/representer_test.rb +18 -18
  36. data/test/docs/rescue_test.rb +29 -29
  37. data/test/docs/trace_test.rb +82 -0
  38. data/test/docs/wrap_test.rb +59 -26
  39. data/test/module_test.rb +75 -75
  40. data/test/nested_test.rb +293 -0
  41. data/test/operation/contract_test.rb +23 -153
  42. data/test/operation/dsl/contract_test.rb +9 -9
  43. data/test/operation/dsl/representer_test.rb +169 -169
  44. data/test/operation/model_test.rb +15 -21
  45. data/test/operation/persist_test.rb +18 -11
  46. data/test/operation/pundit_test.rb +25 -23
  47. data/test/operation/representer_test.rb +254 -254
  48. data/test/test_helper.rb +5 -2
  49. data/test/variables_test.rb +158 -0
  50. data/trailblazer.gemspec +1 -1
  51. data/untitled +33 -0
  52. metadata +25 -27
  53. data/lib/trailblazer/operation/callback.rb +0 -35
  54. data/lib/trailblazer/operation/procedural/contract.rb +0 -15
  55. data/lib/trailblazer/operation/procedural/validate.rb +0 -22
  56. data/test/operation/callback_test.rb +0 -70
  57. data/test/operation/dsl/callback_test.rb +0 -106
  58. data/test/operation/params_test.rb +0 -36
  59. data/test/operation/pipedream_test.rb +0 -59
  60. data/test/operation/pipetree_test.rb +0 -104
  61. data/test/operation/present_test.rb +0 -24
  62. data/test/operation/resolver_test.rb +0 -47
  63. data/test/operation_test.rb +0 -143
@@ -1,62 +1,14 @@
1
1
  require "test_helper"
2
2
 
3
- class ContractExtractMacroTest < Minitest::Spec
4
- class Create < Trailblazer::Operation
5
- step Contract::Validate::Extract( key: "song", params_path: "x" )
6
- end
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
- it { Update.( id: 1 ).success?.must_equal true }
58
- it { Update.( ).success?.must_equal false }
59
- end
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
- extend Contract::DSL
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 ) # FIXME.
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
- extend Contract::DSL
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" })["params"].must_equal({: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["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>contract.build,>contract.default.validate,>persist.save]} }
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["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>contract.build,>contract.default.validate,>persist.save]} }
337
- it { NewHit.(:hit => { title: "Hooray For Me" }).inspect("model").must_equal %{<Result:true [#<struct ContractTest::Song title=\"Hooray For Me\">] >} }
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")["model"].inspect.must_equal %{#<OpenStruct id=1>} }
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")["model"].inspect.must_equal %{#<OpenStruct id=1>} }
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")["model"].inspect.must_equal %{#<OpenStruct 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")["model"].inspect.must_equal %{#<OpenStruct title="Coaster", id=1>} }
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")["model"].inspect.must_equal %{#<OpenStruct 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")["model"].inspect.must_equal %{#<OpenStruct title=\"Coaster\", id=1>} }
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
- 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
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