trailblazer 2.0.7 → 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.
Files changed (72) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.rubocop-https---raw-githubusercontent-com-trailblazer-meta-master-rubocop-yml +101 -0
  4. data/.rubocop.yml +20 -0
  5. data/.rubocop_todo.yml +556 -0
  6. data/.travis.yml +6 -10
  7. data/CHANGES.md +83 -1
  8. data/COMM-LICENSE +46 -75
  9. data/CONTRIBUTING.md +179 -0
  10. data/Gemfile +0 -27
  11. data/{LICENSE.txt → LICENSE} +4 -4
  12. data/README.md +39 -138
  13. data/Rakefile +2 -19
  14. data/lib/trailblazer.rb +3 -17
  15. data/lib/trailblazer/version.rb +3 -1
  16. data/test/test_helper.rb +12 -3
  17. data/trailblazer.gemspec +10 -14
  18. metadata +22 -147
  19. data/doc/Trb-The-Stack.png +0 -0
  20. data/doc/operation-2017.png +0 -0
  21. data/doc/trb.jpg +0 -0
  22. data/lib/trailblazer/dsl.rb +0 -47
  23. data/lib/trailblazer/operation/auto_inject.rb +0 -47
  24. data/lib/trailblazer/operation/callback.rb +0 -35
  25. data/lib/trailblazer/operation/contract.rb +0 -46
  26. data/lib/trailblazer/operation/guard.rb +0 -18
  27. data/lib/trailblazer/operation/model.rb +0 -60
  28. data/lib/trailblazer/operation/module.rb +0 -29
  29. data/lib/trailblazer/operation/nested.rb +0 -113
  30. data/lib/trailblazer/operation/persist.rb +0 -10
  31. data/lib/trailblazer/operation/policy.rb +0 -35
  32. data/lib/trailblazer/operation/procedural/contract.rb +0 -15
  33. data/lib/trailblazer/operation/procedural/validate.rb +0 -22
  34. data/lib/trailblazer/operation/pundit.rb +0 -38
  35. data/lib/trailblazer/operation/representer.rb +0 -31
  36. data/lib/trailblazer/operation/rescue.rb +0 -21
  37. data/lib/trailblazer/operation/test.rb +0 -17
  38. data/lib/trailblazer/operation/validate.rb +0 -68
  39. data/lib/trailblazer/operation/wrap.rb +0 -25
  40. data/test/docs/auto_inject_test.rb +0 -30
  41. data/test/docs/contract_test.rb +0 -525
  42. data/test/docs/dry_test.rb +0 -31
  43. data/test/docs/fast_test.rb +0 -164
  44. data/test/docs/guard_test.rb +0 -169
  45. data/test/docs/macro_test.rb +0 -36
  46. data/test/docs/model_test.rb +0 -75
  47. data/test/docs/nested_test.rb +0 -334
  48. data/test/docs/operation_test.rb +0 -408
  49. data/test/docs/policy_test.rb +0 -2
  50. data/test/docs/pundit_test.rb +0 -133
  51. data/test/docs/representer_test.rb +0 -268
  52. data/test/docs/rescue_test.rb +0 -154
  53. data/test/docs/wrap_test.rb +0 -183
  54. data/test/gemfiles/Gemfile.ruby-1.9 +0 -3
  55. data/test/gemfiles/Gemfile.ruby-2.0 +0 -12
  56. data/test/gemfiles/Gemfile.ruby-2.3 +0 -12
  57. data/test/module_test.rb +0 -100
  58. data/test/operation/callback_test.rb +0 -70
  59. data/test/operation/contract_test.rb +0 -420
  60. data/test/operation/dsl/callback_test.rb +0 -106
  61. data/test/operation/dsl/contract_test.rb +0 -294
  62. data/test/operation/dsl/representer_test.rb +0 -169
  63. data/test/operation/model_test.rb +0 -60
  64. data/test/operation/params_test.rb +0 -36
  65. data/test/operation/persist_test.rb +0 -44
  66. data/test/operation/pipedream_test.rb +0 -59
  67. data/test/operation/pipetree_test.rb +0 -104
  68. data/test/operation/present_test.rb +0 -24
  69. data/test/operation/pundit_test.rb +0 -104
  70. data/test/operation/representer_test.rb +0 -254
  71. data/test/operation/resolver_test.rb +0 -47
  72. data/test/operation_test.rb +0 -143
@@ -1,106 +0,0 @@
1
- require "test_helper"
2
-
3
- class DslCallbackTest < MiniTest::Spec
4
- module SongProcess
5
- def _invocations
6
- self["x"] ||= []
7
- end
8
-
9
- def self.included(includer)
10
- includer.extend Trailblazer::Operation::Contract::DSL
11
- includer.contract do
12
- property :title
13
- end
14
- includer.| Trailblazer::Operation::Model[OpenStruct, :new]
15
- includer.| Trailblazer::Operation::Contract::Build[includer["contract.default.class"]]
16
- includer.| Trailblazer::Operation::Contract::Validate[]
17
- includer.| Trailblazer::Operation::Callback[:default]
18
- end
19
- end
20
-
21
- describe "inheritance across operations" do
22
- class Operation < Trailblazer::Operation
23
- extend Callback::DSL
24
- include SongProcess
25
-
26
- callback do
27
- on_change :default!
28
- end
29
-
30
- class Admin < self
31
- callback do
32
- on_change :admin_default!
33
- end
34
-
35
- callback(:after_save) { on_change :after_save! }
36
-
37
- def admin_default!(*); _invocations << :admin_default!; end
38
- def after_save!(*); _invocations << :after_save!; end
39
-
40
- step Trailblazer::Operation::Callback[:after_save]
41
- end
42
-
43
- def default!(*); _invocations << :default!; end
44
- end
45
-
46
- it { Operation.({"title"=> "Love-less"})["x"].must_equal([:default!]) }
47
- it { Operation::Admin.({"title"=> "Love-less"})["x"].must_equal([:default!, :admin_default!, :after_save!]) }
48
- end
49
-
50
- describe "Op.callback :after_save, AfterSaveCallback" do
51
- class AfterSaveCallback < Disposable::Callback::Group
52
- on_change :after_save!
53
-
54
- def after_save!(twin, options)
55
- options[:operation]._invocations << :after_save!
56
- end
57
- end
58
-
59
- class OpWithExternalCallback < Trailblazer::Operation
60
- include SongProcess
61
- extend Callback::DSL
62
- callback :after_save, AfterSaveCallback
63
-
64
- step Callback[:after_save]
65
- end
66
-
67
- it { OpWithExternalCallback.("title"=>"Thunder Rising").must_equal([:after_save!]) }
68
- end
69
-
70
- describe "Op.callback :after_save, AfterSaveCallback do .. end" do
71
- class DefaultCallback < Disposable::Callback::Group
72
- on_change :default!
73
-
74
- def default!(twin, options)
75
- options[:operation]._invocations << :default!
76
- end
77
- end
78
-
79
- class OpUsingCallback < Trailblazer::Operation
80
- extend Callback::DSL
81
- include SongProcess
82
- callback :default, DefaultCallback
83
- end
84
-
85
- class OpExtendingCallback < Trailblazer::Operation
86
- extend Callback::DSL
87
- include SongProcess
88
- callback :default, DefaultCallback do
89
- on_change :after_save!
90
-
91
- def default!(twin, options)
92
- options[:operation]._invocations << :extended_default!
93
- end
94
-
95
- def after_save!(twin, options)
96
- options[:operation]._invocations << :after_save!
97
- end
98
- end
99
- end
100
-
101
- # this operation copies DefaultCallback and shouldn't run #after_save!.
102
- it { OpUsingCallback.(title: "Thunder Rising")["x"].must_equal([:default!]) }
103
- # this operation copies DefaultCallback, extends it and runs #after_save!.
104
- it { OpExtendingCallback.(title: "Thunder Rising")["x"].must_equal([:extended_default!, :after_save!]) }
105
- end
106
- end
@@ -1,294 +0,0 @@
1
- require "test_helper"
2
- require "trailblazer/operation/contract"
3
-
4
- # contract Constant # new
5
- # contract Constant, inherit: true # extend existing
6
- # contract do end # extend existing || new
7
- # contract Constant do .. end # new, extend new
8
-
9
- class DslContractTest < MiniTest::Spec
10
- module Call
11
- def call(params)
12
- validate(params, model: model=OpenStruct.new) { contract.sync }
13
- model
14
- end
15
-
16
- def self.included(includer)
17
- includer.step Trailblazer::Operation::Model( OpenStruct, :new )
18
- includer.step Trailblazer::Operation::Contract::Build()
19
- includer.step Trailblazer::Operation::Contract::Validate()
20
- includer.step Trailblazer::Operation::Contract::Persist( method: :sync )
21
- # includer.> ->(op, *) { op["x"] = [] }
22
- end
23
- end
24
-
25
- # ---
26
- # Operation::["contract.default.class"]
27
- # Operation::["contract.default.class"]=
28
- class Create < Trailblazer::Operation
29
- include Contract
30
- self["contract.default.class"] = String
31
- end
32
-
33
- # reader method.
34
- # no subclassing.
35
- it { Create["contract.default.class"].must_equal String }
36
-
37
- class CreateOrFind < Create
38
- end
39
-
40
- # no inheritance with setter.
41
- it { CreateOrFind["contract.default.class"].must_equal nil }
42
-
43
- # ---
44
- # Op::contract Constant
45
- class Update < Trailblazer::Operation
46
- class IdContract < Reform::Form
47
- property :id
48
- end
49
-
50
- extend Contract::DSL
51
- contract IdContract
52
-
53
- include Call
54
- end
55
-
56
- # UT: subclasses contract.
57
- it { Update["contract.default.class"].superclass.must_equal Update::IdContract }
58
- # IT: only knows `id`.
59
- it { Update.(id: 1, title: "Coaster")["model"].inspect.must_equal %{#<OpenStruct id=1>} }
60
-
61
- # Op::contract with inheritance
62
- # no ::contract call.
63
- class Upgrade < Update
64
- end
65
-
66
- # UT: subclasses contract but doesn't share with parent.
67
- it { Upgrade["contract.default.class"].superclass.must_equal Update::IdContract }
68
- it { Upgrade["contract.default.class"].wont_equal Update["contract.default.class"] }
69
- # IT: only knows `id`.
70
- it { Upgrade.(id: 1, title: "Coaster")["model"].inspect.must_equal %{#<OpenStruct id=1>} }
71
-
72
- # ::contract B overrides old A contract.
73
- # this makes sure when calling contract(Constant), the old class gets wiped and is replaced with the new constant.
74
- class Upsert < Update
75
- class TitleContract < Reform::Form
76
- property :title
77
- end
78
-
79
- contract TitleContract
80
- end
81
-
82
- # UT: subclasses contract.
83
- it { Upsert["contract.default.class"].superclass.must_equal Upsert::TitleContract }
84
- # IT: only knows `title`.
85
- it { Upsert.(id: 1, title: "Coaster")["model"].inspect.must_equal %{#<OpenStruct title="Coaster">} }
86
-
87
- # ::contract B do ..end overrides and extends new.
88
- # using a constant will wipe out the existing class.
89
- class Upside < Update
90
- contract Upsert::TitleContract do
91
- property :id
92
- end
93
- end
94
-
95
- # UT: subclasses contract.
96
- it { Upside["contract.default.class"].superclass.must_equal Upsert::TitleContract }
97
- # IT: only knows `title`.
98
- it { Upside.(id: 1, title: "Coaster")["model"].inspect.must_equal %{#<OpenStruct title="Coaster", id=1>} }
99
-
100
-
101
-
102
- #---
103
- # contract do .. end
104
- # (with block)
105
- class Delete < Trailblazer::Operation
106
- include Call
107
- extend Contract::DSL
108
- contract do
109
- property :title
110
- end
111
- end
112
-
113
- # UT: contract path is "contract.default.class"
114
- it { Delete["contract.default.class"].definitions.keys.must_equal ["title"] }
115
- # IT: knows `title`.
116
- it { Delete.(id: 1, title: "Coaster")["model"].inspect.must_equal %{#<OpenStruct title=\"Coaster\">} }
117
-
118
- class Wipe < Trailblazer::Operation
119
- extend Contract::DSL
120
-
121
- self["x"] = contract do end
122
- end
123
- # UT: ::contract returns form class
124
- it { Wipe["x"].superclass.must_equal Reform::Form }
125
-
126
- # subsequent calls merge.
127
- class Remove < Trailblazer::Operation
128
- extend Contract::DSL
129
- include Call
130
-
131
- contract do
132
- property :title
133
- end
134
-
135
- contract do
136
- property :id
137
- end
138
- end
139
-
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>} }
142
-
143
-
144
-
145
-
146
-
147
-
148
- # Operation::["contract.default.class"]
149
- # Operation::["contract.default.class"]=
150
- describe %{Operation::["contract.default.class"]} do
151
-
152
- class Update2 < Trailblazer::Operation
153
- self["contract.default.class"] = String
154
- end
155
-
156
- it { Update2["contract.default.class"].must_equal String }
157
- end
158
-
159
- describe "inheritance across operations" do
160
- # inheritance
161
- class Operation < Trailblazer::Operation
162
- extend Contract::DSL
163
- contract do
164
- property :title
165
- property :band
166
- end
167
-
168
- class JSON < self
169
- contract do # inherit Contract
170
- property :genre, validates: {presence: true}
171
- property :band, virtual: true
172
- end
173
- end
174
-
175
- class XML < self
176
- end
177
- end
178
-
179
- # inherits subclassed Contract.
180
- it { Operation["contract.default.class"].wont_equal Operation::JSON["contract.default.class"] }
181
- it { Operation::XML["contract.default.class"].superclass.must_equal Reform::Form }
182
-
183
- it do
184
- form = Operation["contract.default.class"].new(OpenStruct.new)
185
- form.validate({})#.must_equal true
186
- form.errors.to_s.must_equal "{}"
187
-
188
- form = Operation::JSON["contract.default.class"].new(OpenStruct.new)
189
- form.validate({})#.must_equal true
190
- form.errors.to_s.must_equal "{:genre=>[\"can't be blank\"]}"
191
- end
192
-
193
- # allows overriding options
194
- it do
195
- form = Operation::JSON["contract.default.class"].new(song = OpenStruct.new)
196
- form.validate({genre: "Punkrock", band: "Osker"}).must_equal true
197
- form.sync
198
-
199
- song.genre.must_equal "Punkrock"
200
- song.band.must_equal nil
201
- end
202
- end
203
-
204
- describe "Op.contract CommentForm" do
205
- class SongForm < Reform::Form
206
- property :songTitle, validates: {presence: true}
207
- end
208
-
209
- class OpWithExternalContract < Trailblazer::Operation
210
- extend Contract::DSL
211
- contract SongForm
212
- include Call
213
- end
214
-
215
- it { OpWithExternalContract.("songTitle"=> "Monsterparty")["contract.default"].songTitle.must_equal "Monsterparty" }
216
- end
217
-
218
- describe "Op.contract CommentForm do .. end" do
219
- class DifferentSongForm < Reform::Form
220
- property :songTitle, validates: {presence: true}
221
- end
222
-
223
- class OpNotExtendingContract < Trailblazer::Operation
224
- extend Contract::DSL
225
- contract DifferentSongForm
226
- include Call
227
- end
228
-
229
- class OpExtendingContract < Trailblazer::Operation
230
- extend Contract::DSL
231
- contract DifferentSongForm do
232
- property :genre
233
- end
234
- include Call
235
- end
236
-
237
- # this operation copies DifferentSongForm and shouldn't have `genre`.
238
- it do
239
- contract = OpNotExtendingContract.("songTitle"=>"Monsterparty", "genre"=>"Punk")["contract.default"]
240
- contract.songTitle.must_equal "Monsterparty"
241
- assert_raises(NoMethodError) { contract.genre }
242
- end
243
-
244
- # this operation copies DifferentSongForm and extends it with the property `genre`.
245
- it do
246
- contract = OpExtendingContract.("songTitle"=>"Monsterparty", "genre"=>"Punk")["contract.default"]
247
- contract.songTitle.must_equal "Monsterparty"
248
- contract.genre.must_equal "Punk"
249
- end
250
-
251
- # of course, the original contract wasn't modified, either.
252
- it do
253
- assert_raises(NoMethodError) { DifferentSongForm.new(OpenStruct.new).genre }
254
- end
255
- end
256
-
257
- describe "Op.contract :name, Form" do
258
- class Follow < Trailblazer::Operation
259
- ParamsForm = Class.new
260
-
261
- extend Contract::DSL
262
- contract :params, ParamsForm
263
- end
264
-
265
- it { Follow["contract.params.class"].superclass.must_equal Follow::ParamsForm }
266
- end
267
-
268
- describe "Op.contract :name do..end" do
269
- class Unfollow < Trailblazer::Operation
270
- extend Contract::DSL
271
- contract :params do
272
- property :title
273
- end
274
- end
275
-
276
- it { Unfollow["contract.params.class"].superclass.must_equal Reform::Form }
277
- end
278
-
279
- # multiple ::contract calls.
280
- describe "multiple ::contract calls" do
281
- class Star < Trailblazer::Operation
282
- extend Contract::DSL
283
- contract do
284
- property :title
285
- end
286
-
287
- contract do
288
- property :id
289
- end
290
- end
291
-
292
- it { Star["contract.default.class"].definitions.keys.must_equal ["title", "id"] }
293
- end
294
- end
@@ -1,169 +0,0 @@
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