trailblazer 2.0.3 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.md +5 -1
- data/Gemfile +1 -1
- data/lib/trailblazer/operation/nested.rb +11 -5
- data/lib/trailblazer/operation/rescue.rb +1 -5
- data/lib/trailblazer/operation/test.rb +17 -0
- data/lib/trailblazer/version.rb +1 -1
- data/test/docs/contract_test.rb +140 -46
- data/test/docs/nested_test.rb +17 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c715d450be489fd1d47e0e9543044dbecc683db8
|
4
|
+
data.tar.gz: 3212afa6969b12ed86cab5b73cb43a84ffa56ac0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ff9d994e6c2176ed8dd418ed49fd9214edb7251c44251b6a1afaa93803063bf599f10f0f08b2e726ec69d979e550bee18567e73ffdb822210db93744d2689b4
|
7
|
+
data.tar.gz: 4f7a37983d925bd4e6c8722422aa97d067a1dec4ba11c3018c4887d9b5ec8e2fab57aed6ce6a062ad4a0ea2713dbfdd15fda98401343e0c7e5d934783e3abf24
|
data/CHANGES.md
CHANGED
@@ -1,9 +1,13 @@
|
|
1
|
+
# 2.0.4
|
2
|
+
|
3
|
+
* When using `Nested(X)`, the automatic `:name` option is now `"Nested(X)"` instead of the cryptic proc string.
|
4
|
+
|
1
5
|
# 2.0.3
|
2
6
|
|
3
7
|
* `Guard` now allows kw args for its option.
|
4
8
|
* Fix a bug where `Nested( ->{} )` wouldn't `_call` the nested operation and did too much work on re-nested the already nested params. Thanks to @eliranf for spotting this.
|
5
9
|
* Add `Nested(..., input: )` to dynamically decide the input to the nested operation. http://trailblazer.to/gems/operation/2.0/api.html#nested-input
|
6
|
-
* Add `Nested(..., output: )`: http://
|
10
|
+
* Add `Nested(..., output: )`: http://trailblazer.to/gems/operation/2.0/api.html#nested-output
|
7
11
|
|
8
12
|
# 2.0.2
|
9
13
|
|
data/Gemfile
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
class Trailblazer::Operation
|
2
|
-
def self.Nested(
|
3
|
-
step = Nested.for(
|
2
|
+
def self.Nested(callable, input:nil, output:nil)
|
3
|
+
step = Nested.for(callable, input, output)
|
4
4
|
|
5
|
-
[ step, { name: "Nested(#{
|
5
|
+
[ step, { name: "Nested(#{callable})" } ]
|
6
6
|
end
|
7
7
|
|
8
8
|
# WARNING: this is experimental API, but it will end up with something like that.
|
@@ -22,9 +22,10 @@ class Trailblazer::Operation
|
|
22
22
|
module Nested
|
23
23
|
# Please note that the instance_variable_get are here on purpose since the
|
24
24
|
# superinternal API is not entirely decided, yet.
|
25
|
-
|
25
|
+
# @api private
|
26
|
+
def self.for(step, input, output, is_nestable_object=method(:nestable_object?)) # DISCUSS: use builders here?
|
26
27
|
invoker = Caller::Dynamic.new(step)
|
27
|
-
invoker = Caller.new(step) if
|
28
|
+
invoker = Caller.new(step) if is_nestable_object.(step)
|
28
29
|
|
29
30
|
options_for_nested = Options.new
|
30
31
|
options_for_nested = Options::Dynamic.new(input) if input
|
@@ -42,6 +43,11 @@ class Trailblazer::Operation
|
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
46
|
+
def self.nestable_object?(object)
|
47
|
+
# interestingly, with < we get a weird nil exception. bug in Ruby?
|
48
|
+
object.is_a?(Class) && object <= Trailblazer::Operation
|
49
|
+
end
|
50
|
+
|
45
51
|
# Is executed at runtime and calls the nested operation.
|
46
52
|
class Caller
|
47
53
|
include Element
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class Trailblazer::Operation
|
2
|
-
def self.Rescue(*exceptions, handler:
|
2
|
+
def self.Rescue(*exceptions, handler: lambda { |*| }, &block)
|
3
3
|
exceptions = [StandardError] unless exceptions.any?
|
4
4
|
handler = Option.(handler)
|
5
5
|
|
@@ -17,9 +17,5 @@ class Trailblazer::Operation
|
|
17
17
|
|
18
18
|
[ step, name: "Rescue:#{block.source_location.last}" ]
|
19
19
|
end
|
20
|
-
|
21
|
-
module Rescue
|
22
|
-
Noop = ->(*) {}
|
23
|
-
end
|
24
20
|
end
|
25
21
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
module Test
|
3
|
+
module Run
|
4
|
+
# DISCUSS: use Endpoint here?
|
5
|
+
# DISCUSS: use Controller code here?
|
6
|
+
module_function
|
7
|
+
def run(operation_class, *args)
|
8
|
+
result = operation_class.(*args)
|
9
|
+
|
10
|
+
raise "[Trailblazer] #{operation_class} wasn't run successfully. #{result.inspect}" if result.failure?
|
11
|
+
|
12
|
+
result
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/trailblazer/version.rb
CHANGED
data/test/docs/contract_test.rb
CHANGED
@@ -4,7 +4,7 @@ class DocsContractOverviewTest < Minitest::Spec
|
|
4
4
|
Song = Struct.new(:length, :title)
|
5
5
|
|
6
6
|
#:overv-reform
|
7
|
-
# app/concepts/
|
7
|
+
# app/concepts/song/create.rb
|
8
8
|
class Create < Trailblazer::Operation
|
9
9
|
#~bla
|
10
10
|
extend Contract::DSL
|
@@ -126,29 +126,6 @@ class DocsContractExplicitTest < Minitest::Spec
|
|
126
126
|
#:reform-inline-op end
|
127
127
|
end
|
128
128
|
|
129
|
-
#---
|
130
|
-
#- Validate[key: :song]
|
131
|
-
class DocsContractKeyTest < Minitest::Spec
|
132
|
-
Song = Struct.new(:id, :title)
|
133
|
-
#:key
|
134
|
-
class Create < Trailblazer::Operation
|
135
|
-
extend Contract::DSL
|
136
|
-
|
137
|
-
contract do
|
138
|
-
property :title
|
139
|
-
end
|
140
|
-
|
141
|
-
step Model( Song, :new )
|
142
|
-
step Contract::Build()
|
143
|
-
step Contract::Validate( key: "song" )
|
144
|
-
step Contract::Persist( method: :sync )
|
145
|
-
end
|
146
|
-
#:key end
|
147
|
-
|
148
|
-
it { Create.({}).inspect("model", "result.contract.default.extract").must_equal %{<Result:false [#<struct DocsContractKeyTest::Song id=nil, title=nil>, nil] >} }
|
149
|
-
it { Create.({"song" => { title: "SVG" }}).inspect("model").must_equal %{<Result:true [#<struct DocsContractKeyTest::Song id=nil, title="SVG">] >} }
|
150
|
-
end
|
151
|
-
|
152
129
|
#- Validate with manual key extraction
|
153
130
|
class DocsContractSeparateKeyTest < Minitest::Spec
|
154
131
|
Song = Struct.new(:id, :title)
|
@@ -181,48 +158,165 @@ class DocsContractSeparateKeyTest < Minitest::Spec
|
|
181
158
|
end
|
182
159
|
|
183
160
|
#---
|
184
|
-
#- Contract::Build
|
161
|
+
#- Contract::Build( constant: XXX )
|
185
162
|
class ContractConstantTest < Minitest::Spec
|
186
|
-
Song = Struct.new(:
|
187
|
-
|
188
|
-
|
189
|
-
class MyContract < Reform::Form
|
190
|
-
property :title
|
191
|
-
validates :title, length: 2..33
|
163
|
+
Song = Struct.new(:title, :length) do
|
164
|
+
def save
|
165
|
+
true
|
192
166
|
end
|
167
|
+
end
|
193
168
|
|
169
|
+
#:constant-contract
|
170
|
+
# app/concepts/song/contract/create.rb
|
171
|
+
module Song::Contract
|
172
|
+
class Create < Reform::Form
|
173
|
+
property :title
|
174
|
+
property :length
|
194
175
|
|
176
|
+
validates :title, length: 2..33
|
177
|
+
validates :length, numericality: true
|
178
|
+
end
|
179
|
+
end
|
180
|
+
#:constant-contract end
|
195
181
|
|
182
|
+
#:constant
|
183
|
+
class Song::Create < Trailblazer::Operation
|
196
184
|
step Model( Song, :new )
|
197
|
-
step Contract::Build( constant:
|
185
|
+
step Contract::Build( constant: Song::Contract::Create )
|
198
186
|
step Contract::Validate()
|
199
|
-
step Contract::Persist(
|
187
|
+
step Contract::Persist()
|
200
188
|
end
|
201
189
|
#:constant end
|
202
190
|
|
203
|
-
it { Create.({ title: "A" }).inspect("model").must_equal %{<Result:false [#<struct ContractConstantTest::Song
|
204
|
-
it { Create.({ title: "Anthony's Song" }).inspect("model").must_equal %{<Result:true [#<struct ContractConstantTest::Song
|
191
|
+
it { Song::Create.({ title: "A" }).inspect("model").must_equal %{<Result:false [#<struct ContractConstantTest::Song title=nil, length=nil>] >} }
|
192
|
+
it { Song::Create.({ title: "Anthony's Song", length: 12 }).inspect("model").must_equal %{<Result:true [#<struct ContractConstantTest::Song title="Anthony's Song", length=12>] >} }
|
193
|
+
it do
|
194
|
+
#:constant-result
|
195
|
+
result = Song::Create.( title: "A" )
|
196
|
+
result.success? #=> false
|
197
|
+
result["contract.default"].errors.messages
|
198
|
+
#=> {:title=>["is too short (minimum is 2 characters)"], :length=>["is not a number"]}
|
199
|
+
#:constant-result end
|
200
|
+
|
201
|
+
#:constant-result-true
|
202
|
+
result = Song::Create.( title: "Rising Force", length: 13 )
|
203
|
+
result.success? #=> true
|
204
|
+
result["model"] #=> #<Song title="Rising Force", length=13>
|
205
|
+
#:constant-result-true end
|
206
|
+
end
|
207
|
+
|
208
|
+
#---
|
209
|
+
# Song::New
|
210
|
+
#:constant-new
|
211
|
+
class Song::New < Trailblazer::Operation
|
212
|
+
step Model( Song, :new )
|
213
|
+
step Contract::Build( constant: Song::Contract::Create )
|
214
|
+
end
|
215
|
+
#:constant-new end
|
216
|
+
|
217
|
+
it { Song::New.().inspect("model").must_equal %{<Result:true [#<struct ContractConstantTest::Song title=nil, length=nil>] >} }
|
218
|
+
it { Song::New.()["contract.default"].model.inspect.must_equal %{#<struct ContractConstantTest::Song title=nil, length=nil>} }
|
219
|
+
it do
|
220
|
+
#:constant-new-result
|
221
|
+
result = Song::New.()
|
222
|
+
result["model"] #=> #<struct Song title=nil, length=nil>
|
223
|
+
result["contract.default"]
|
224
|
+
#=> #<Song::Contract::Create model=#<struct Song title=nil, length=nil>>
|
225
|
+
#:constant-new-result end
|
226
|
+
end
|
227
|
+
|
228
|
+
#---
|
229
|
+
#:validate-only
|
230
|
+
class Song::ValidateOnly < Trailblazer::Operation
|
231
|
+
step Model( Song, :new )
|
232
|
+
step Contract::Build( constant: Song::Contract::Create )
|
233
|
+
step Contract::Validate()
|
234
|
+
end
|
235
|
+
#:validate-only end
|
236
|
+
|
237
|
+
it { Song::ValidateOnly.().inspect("model").must_equal %{<Result:false [#<struct ContractConstantTest::Song title=nil, length=nil>] >} }
|
238
|
+
it do
|
239
|
+
result = Song::ValidateOnly.({ title: "Rising Forse", length: 13 })
|
240
|
+
result.inspect("model").must_equal %{<Result:true [#<struct ContractConstantTest::Song title=nil, length=nil>] >}
|
241
|
+
end
|
242
|
+
|
243
|
+
it do
|
244
|
+
#:validate-only-result-false
|
245
|
+
result = Song::ValidateOnly.({}) # empty params
|
246
|
+
result.success? #=> false
|
247
|
+
#:validate-only-result-false end
|
248
|
+
end
|
249
|
+
|
250
|
+
it do
|
251
|
+
#:validate-only-result
|
252
|
+
result = Song::ValidateOnly.({ title: "Rising Force", length: 13 })
|
253
|
+
|
254
|
+
result.success? #=> true
|
255
|
+
result["model"] #=> #<struct Song title=nil, length=nil>
|
256
|
+
result["contract.default"].title #=> "Rising Force"
|
257
|
+
#:validate-only-result end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
#---
|
262
|
+
#- Validate( key: :song )
|
263
|
+
class DocsContractKeyTest < Minitest::Spec
|
264
|
+
Song = Class.new(ContractConstantTest::Song)
|
265
|
+
|
266
|
+
module Song::Contract
|
267
|
+
Create = ContractConstantTest::Song::Contract::Create
|
268
|
+
end
|
269
|
+
|
270
|
+
#:key
|
271
|
+
class Song::Create < Trailblazer::Operation
|
272
|
+
step Model( Song, :new )
|
273
|
+
step Contract::Build( constant: Song::Contract::Create )
|
274
|
+
step Contract::Validate( key: "song" )
|
275
|
+
step Contract::Persist( )
|
276
|
+
end
|
277
|
+
#:key end
|
278
|
+
|
279
|
+
it { Song::Create.({}).inspect("model", "result.contract.default.extract").must_equal %{<Result:false [#<struct DocsContractKeyTest::Song title=nil, length=nil>, nil] >} }
|
280
|
+
it { Song::Create.({"song" => { title: "SVG", length: 13 }}).inspect("model").must_equal %{<Result:true [#<struct DocsContractKeyTest::Song title=\"SVG\", length=13>] >} }
|
281
|
+
it do
|
282
|
+
#:key-res
|
283
|
+
result = Song::Create.({ "song" => { title: "Rising Force", length: 13 } })
|
284
|
+
result.success? #=> true
|
285
|
+
#:key-res end
|
286
|
+
|
287
|
+
#:key-res-false
|
288
|
+
result = Song::Create.({ title: "Rising Force", length: 13 })
|
289
|
+
result.success? #=> false
|
290
|
+
#:key-res-false end
|
291
|
+
end
|
205
292
|
end
|
206
293
|
|
207
294
|
#- Contract::Build[ constant: XXX, name: AAA ]
|
208
295
|
class ContractNamedConstantTest < Minitest::Spec
|
209
|
-
Song =
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
validates :title, length: 2..33
|
215
|
-
end
|
296
|
+
Song = Class.new(ContractConstantTest::Song)
|
297
|
+
|
298
|
+
module Song::Contract
|
299
|
+
Create = ContractConstantTest::Song::Contract::Create
|
300
|
+
end
|
216
301
|
|
302
|
+
#:constant-name
|
303
|
+
class Song::Create < Trailblazer::Operation
|
217
304
|
step Model( Song, :new )
|
218
|
-
step Contract::Build(
|
305
|
+
step Contract::Build( name: "form", constant: Song::Contract::Create )
|
219
306
|
step Contract::Validate( name: "form" )
|
220
|
-
step Contract::Persist(
|
307
|
+
step Contract::Persist( name: "form" )
|
221
308
|
end
|
222
309
|
#:constant-name end
|
223
310
|
|
224
|
-
it { Create.({ title: "A" }).inspect("model").must_equal %{<Result:false [#<struct ContractNamedConstantTest::Song
|
225
|
-
it { Create.({ title: "Anthony's Song" }).inspect("model").must_equal %{<Result:true [#<struct ContractNamedConstantTest::Song
|
311
|
+
it { Song::Create.({ title: "A" }).inspect("model").must_equal %{<Result:false [#<struct ContractNamedConstantTest::Song title=nil, length=nil>] >} }
|
312
|
+
it { Song::Create.({ title: "Anthony's Song", length: 13 }).inspect("model").must_equal %{<Result:true [#<struct ContractNamedConstantTest::Song title="Anthony's Song", length=13>] >} }
|
313
|
+
|
314
|
+
it do
|
315
|
+
#:name-res
|
316
|
+
result = Song::Create.({ title: "A" })
|
317
|
+
result["contract.form"].errors.messages #=> {:title=>["is too short (minimum is 2 ch...
|
318
|
+
#:name-res end
|
319
|
+
end
|
226
320
|
end
|
227
321
|
|
228
322
|
#---
|
data/test/docs/nested_test.rb
CHANGED
@@ -76,6 +76,7 @@ class DocsNestedOperationTest < Minitest::Spec
|
|
76
76
|
class B < Trailblazer::Operation
|
77
77
|
success ->(options) { options["can.B.see.it?"] = options["this.should.not.be.visible.in.B"] }
|
78
78
|
success ->(options) { options["can.B.see.current_user?"] = options["current_user"] }
|
79
|
+
success ->(options) { options["can.B.see.params?"] = options["params"] }
|
79
80
|
success ->(options) { options["can.B.see.A.class.data?"] = options["A.class.data"] }
|
80
81
|
end
|
81
82
|
|
@@ -91,6 +92,7 @@ class DocsNestedOperationTest < Minitest::Spec
|
|
91
92
|
it { A.()["this.should.not.be.visible.in.B"].must_equal true }
|
92
93
|
# runtime dependencies are visible in B.
|
93
94
|
it { A.({}, "current_user" => Module)["can.B.see.current_user?"].must_equal Module }
|
95
|
+
it { A.({ a: 1 })["can.B.see.params?"].must_equal({ a: 1 }) }
|
94
96
|
# class data from A doesn't bleed into B.
|
95
97
|
it { A.()["can.B.see.A.class.data?"].must_equal nil }
|
96
98
|
|
@@ -315,3 +317,18 @@ class NestedWithCallableTest < Minitest::Spec
|
|
315
317
|
end
|
316
318
|
|
317
319
|
# builder: Nested + deviate to left if nil / skip_track if true
|
320
|
+
|
321
|
+
#---
|
322
|
+
# automatic :name
|
323
|
+
class NestedNameTest < Minitest::Spec
|
324
|
+
class Create < Trailblazer::Operation
|
325
|
+
class Present < Trailblazer::Operation
|
326
|
+
# ...
|
327
|
+
end
|
328
|
+
|
329
|
+
step Nested( Present )
|
330
|
+
# ...
|
331
|
+
end
|
332
|
+
|
333
|
+
it { Create["pipetree"].inspect.must_equal %{[>operation.new,>Nested(NestedNameTest::Create::Present)]} }
|
334
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trailblazer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-04-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: trailblazer-operation
|
@@ -184,6 +184,7 @@ files:
|
|
184
184
|
- lib/trailblazer/operation/pundit.rb
|
185
185
|
- lib/trailblazer/operation/representer.rb
|
186
186
|
- lib/trailblazer/operation/rescue.rb
|
187
|
+
- lib/trailblazer/operation/test.rb
|
187
188
|
- lib/trailblazer/operation/validate.rb
|
188
189
|
- lib/trailblazer/operation/wrap.rb
|
189
190
|
- lib/trailblazer/version.rb
|
@@ -242,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
242
243
|
version: '0'
|
243
244
|
requirements: []
|
244
245
|
rubyforge_project:
|
245
|
-
rubygems_version: 2.
|
246
|
+
rubygems_version: 2.5.2
|
246
247
|
signing_key:
|
247
248
|
specification_version: 4
|
248
249
|
summary: A high-level architecture for Ruby and Rails.
|