trailblazer 2.0.7 → 2.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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