trailblazer 1.1.1 → 2.1.0

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