trailblazer 2.0.0 → 2.0.1

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +13 -0
  3. data/COMM-LICENSE +21 -21
  4. data/Gemfile +3 -0
  5. data/README.md +15 -77
  6. data/Rakefile +1 -2
  7. data/doc/operation-2017.png +0 -0
  8. data/lib/trailblazer.rb +0 -1
  9. data/lib/trailblazer/operation/callback.rb +10 -19
  10. data/lib/trailblazer/operation/contract.rb +7 -8
  11. data/lib/trailblazer/operation/guard.rb +4 -13
  12. data/lib/trailblazer/operation/model.rb +30 -32
  13. data/lib/trailblazer/operation/nested.rb +21 -32
  14. data/lib/trailblazer/operation/persist.rb +4 -8
  15. data/lib/trailblazer/operation/policy.rb +5 -15
  16. data/lib/trailblazer/operation/pundit.rb +8 -17
  17. data/lib/trailblazer/operation/rescue.rb +17 -17
  18. data/lib/trailblazer/operation/validate.rb +34 -21
  19. data/lib/trailblazer/operation/wrap.rb +12 -25
  20. data/lib/trailblazer/version.rb +1 -1
  21. data/test/docs/dry_test.rb +4 -4
  22. data/test/docs/fast_test.rb +164 -0
  23. data/test/docs/guard_test.rb +2 -2
  24. data/test/docs/macro_test.rb +36 -0
  25. data/test/docs/model_test.rb +52 -0
  26. data/test/docs/nested_test.rb +101 -4
  27. data/test/docs/operation_test.rb +225 -1
  28. data/test/docs/pundit_test.rb +18 -17
  29. data/test/docs/rescue_test.rb +3 -3
  30. data/test/docs/wrap_test.rb +1 -0
  31. data/test/operation/callback_test.rb +5 -5
  32. data/test/operation/contract_test.rb +30 -19
  33. data/test/operation/dsl/callback_test.rb +2 -2
  34. data/test/operation/dsl/contract_test.rb +4 -4
  35. data/test/operation/model_test.rb +12 -57
  36. data/test/operation/persist_test.rb +7 -7
  37. data/test/operation/pipedream_test.rb +9 -9
  38. data/test/operation/pipetree_test.rb +5 -5
  39. data/test/operation/pundit_test.rb +7 -7
  40. data/test/operation/resolver_test.rb +47 -47
  41. data/trailblazer.gemspec +1 -1
  42. metadata +18 -11
  43. data/lib/trailblazer/operation/builder.rb +0 -24
  44. data/lib/trailblazer/operation/resolver.rb +0 -22
  45. data/test/controller_test.rb +0 -115
  46. data/test/operation/builder_test.rb +0 -89
@@ -1,20 +1,20 @@
1
1
  require "test_helper"
2
2
 
3
- #:policy
4
- class MyPolicy
5
- def initialize(user, model)
6
- @user, @model = user, model
7
- end
3
+ #:policy
4
+ class MyPolicy
5
+ def initialize(user, model)
6
+ @user, @model = user, model
7
+ end
8
8
 
9
- def create?
10
- @user == Module && @model.id.nil?
11
- end
9
+ def create?
10
+ @user == Module && @model.id.nil?
11
+ end
12
12
 
13
- def new?
14
- @user == Class
13
+ def new?
14
+ @user == Class
15
+ end
15
16
  end
16
- end
17
- #:policy end
17
+ #:policy end
18
18
 
19
19
  #--
20
20
  # with policy
@@ -29,6 +29,7 @@ class DocsPunditProcTest < Minitest::Spec
29
29
  end
30
30
  #:pundit end
31
31
 
32
+ it { Create["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>policy.default.eval]} }
32
33
  it { Create.({}, "current_user" => Module).inspect("model").must_equal %{<Result:true [#<struct DocsPunditProcTest::Song id=nil>] >} }
33
34
  it { Create.({} ).inspect("model").must_equal %{<Result:false [#<struct DocsPunditProcTest::Song id=nil>] >} }
34
35
 
@@ -45,10 +46,10 @@ class DocsPunditProcTest < Minitest::Spec
45
46
  #---
46
47
  #- override
47
48
  class New < Create
48
- override Policy::Pundit( MyPolicy, :new? )
49
+ step Policy::Pundit( MyPolicy, :new? ), override: true
49
50
  end
50
51
 
51
- it { New["pipetree"].inspect.must_equal %{[>>operation.new,&model.build,&policy.default.eval]} }
52
+ it { New["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>policy.default.eval]} }
52
53
  it { New.({}, "current_user" => Class ).inspect("model").must_equal %{<Result:true [#<struct DocsPunditProcTest::Song id=nil>] >} }
53
54
  it { New.({}, "current_user" => nil ).inspect("model").must_equal %{<Result:false [#<struct DocsPunditProcTest::Song id=nil>] >} }
54
55
 
@@ -60,12 +61,12 @@ class DocsPunditProcTest < Minitest::Spec
60
61
  end
61
62
 
62
63
  class Update < Edit
63
- override Policy::Pundit( MyPolicy, :new?, name: "first" )
64
+ step Policy::Pundit( MyPolicy, :new?, name: "first" ), override: true
64
65
  end
65
66
 
66
- it { Edit["pipetree"].inspect.must_equal %{[>>operation.new,&policy.first.eval,&policy.second.eval]} }
67
+ it { Edit["pipetree"].inspect.must_equal %{[>operation.new,>policy.first.eval,>policy.second.eval]} }
67
68
  it { Edit.({}, "current_user" => Class).inspect("model").must_equal %{<Result:false [nil] >} }
68
- it { Update["pipetree"].inspect.must_equal %{[>>operation.new,&policy.first.eval,&policy.second.eval]} }
69
+ it { Update["pipetree"].inspect.must_equal %{[>operation.new,>policy.first.eval,>policy.second.eval]} }
69
70
  it { Update.({}, "current_user" => Class).inspect("model").must_equal %{<Result:true [nil] >} }
70
71
 
71
72
  #---
@@ -17,13 +17,13 @@ class NestedRescueTest < Minitest::Spec
17
17
  step ->(options) { options["b"] = true }
18
18
  success ->(options) { raise A if options["raise-a"] }
19
19
  step ->(options) { options["c"] = true }
20
- self.< ->(options) { options["inner-err"] = true }
20
+ failure ->(options) { options["inner-err"] = true }
21
21
  }
22
22
  step ->(options) { options["e"] = true }
23
23
  failure ->(options) { options["outer-err"] = true }
24
24
  end
25
25
 
26
- it { NestedInsanity["pipetree"].inspect.must_equal %{[>>operation.new,&Rescue:10,&:22,<NestedRescueTest::NestedInsanity:23]} }
26
+ it { NestedInsanity["pipetree"].inspect.must_equal %{[>operation.new,>Rescue:10,>rescue_test.rb:22,<rescue_test.rb:23]} }
27
27
  it { NestedInsanity.({}).inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:true [true, true, true, true, true, true, nil, nil] >} }
28
28
  it { NestedInsanity.({}, "raise-y" => true).inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:false [true, true, nil, nil, nil, nil, true, true] >} }
29
29
  it { NestedInsanity.({}, "raise-a" => true).inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:false [true, true, true, true, nil, nil, nil, true] >} }
@@ -148,7 +148,7 @@ class RescueTest < Minitest::Spec
148
148
  it { assert_raises(RuntimeError) { Create.( id: "RuntimeError!" ) } }
149
149
  it do
150
150
  Create.( id: 1, title: "Pie" )
151
- Sequel.result.first.must_equal Pipetree::Flow::Right
151
+ Sequel.result.first.must_equal Pipetree::Railway::Right
152
152
  end
153
153
  end
154
154
  end
@@ -142,6 +142,7 @@ class WrapTest < Minitest::Spec
142
142
 
143
143
  def self.call(options, *)
144
144
  Sequel.transaction { yield } # yield runs the nested pipe.
145
+ # return value decides about left or right track!
145
146
  end
146
147
  end
147
148
  #:callable-t end
@@ -13,10 +13,10 @@ class OperationCallbackTest < MiniTest::Spec
13
13
  property :name
14
14
  end
15
15
 
16
- self.| Model( Song, :new )
17
- self.| Contract::Build()
18
- self.| Contract::Validate()
19
- self.| Callback( :default )
16
+ step Model( Song, :new )
17
+ step Contract::Build()
18
+ step Contract::Validate()
19
+ step Callback( :default )
20
20
 
21
21
 
22
22
  extend Callback::DSL
@@ -55,7 +55,7 @@ class OperationCallbackTest < MiniTest::Spec
55
55
 
56
56
  #---
57
57
  #- inheritance
58
- it { Update["pipetree"].inspect.must_equal %{[>>operation.new,&model.build,>contract.build,&contract.default.params,&contract.default.validate,&callback.default]} }
58
+ it { Update["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>contract.build,>contract.default.validate,>callback.default]} }
59
59
 
60
60
 
61
61
  it "invokes all callbacks" do
@@ -1,5 +1,16 @@
1
1
  require "test_helper"
2
- require "trailblazer/operation/contract"
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
+
3
14
 
4
15
  require "dry/validation"
5
16
 
@@ -14,7 +25,7 @@ class DryValidationTest < Minitest::Spec
14
25
  # required(:id).filled
15
26
  # end
16
27
 
17
- self.| :process
28
+ step :process
18
29
 
19
30
  include Procedural::Validate
20
31
 
@@ -40,7 +51,7 @@ class DryValidationTest < Minitest::Spec
40
51
  required(:id).filled
41
52
  end)
42
53
 
43
- self.& ->(options) { options["contract.params"].(options["params"]).success? }, before: "operation.new"
54
+ step ->(options) { options["contract.params"].(options["params"]).success? }, before: "operation.new"
44
55
  end
45
56
 
46
57
  it { Update.( id: 1 ).success?.must_equal true }
@@ -71,10 +82,10 @@ class ContractTest < Minitest::Spec
71
82
  property :title
72
83
  end
73
84
 
74
- self.> ->(options) { options["model"] = Song.new }
75
- # self.| Model( Song, :new )
76
- self.| Contract::Build()
77
- self.| :process
85
+ success ->(options) { options["model"] = Song.new }
86
+ # step Model( Song, :new )
87
+ step Contract::Build()
88
+ step :process
78
89
 
79
90
  include Procedural::Validate
80
91
  # TODO: get model automatically in validate!
@@ -216,9 +227,9 @@ class ValidateTest < Minitest::Spec
216
227
  end
217
228
  end
218
229
 
219
- self.| Model( Song, :new ) # FIXME.
220
- self.| Contract::Build()
221
- self.& :process
230
+ step Model( Song, :new ) # FIXME.
231
+ step Contract::Build()
232
+ step :process
222
233
  end
223
234
 
224
235
  # validate returns the #validate result
@@ -258,9 +269,9 @@ class ValidateTest < Minitest::Spec
258
269
  validates :title, presence: true
259
270
  end
260
271
 
261
- self.| Model( Song, :new ) # FIXME.
262
- self.| Contract::Build()
263
- self.| Contract::Validate() # generic validate call for you.
272
+ step Model( Song, :new ) # FIXME.
273
+ step Contract::Build()
274
+ step Contract::Validate() # generic validate call for you.
264
275
 
265
276
  # include Procedural::Validate
266
277
  ->(*) { validate(options["params"][:song]) } # <-- TODO
@@ -291,9 +302,9 @@ class ValidateTest < Minitest::Spec
291
302
  validates :title, presence: true
292
303
  end
293
304
 
294
- self.| Model( Song, :new ) # FIXME.
295
- self.| Contract::Build()
296
- self.| Contract::Validate( key: :song) # generic validate call for you.
305
+ step Model( Song, :new ) # FIXME.
306
+ step Contract::Build()
307
+ step Contract::Validate( key: :song) # generic validate call for you.
297
308
  # ->(*) { validate(options["params"][:song]) } # <-- TODO
298
309
  step Contract::Persist( method: :sync )
299
310
  end
@@ -315,14 +326,14 @@ class ValidateTest < Minitest::Spec
315
326
  class New < Upsert
316
327
  end
317
328
 
318
- it { New["pipetree"].inspect.must_equal %{[>>operation.new,&model.build,>contract.build,&contract.default.params,&contract.default.validate,&persist.save]} }
329
+ it { New["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>contract.build,>contract.default.validate,>persist.save]} }
319
330
 
320
331
  #- overwriting Validate
321
332
  class NewHit < Upsert
322
- override Contract::Validate( key: :hit )
333
+ step Contract::Validate( key: :hit ), override: true
323
334
  end
324
335
 
325
- it { NewHit["pipetree"].inspect.must_equal %{[>>operation.new,&model.build,>contract.build,&contract.default.params,&contract.default.validate,&persist.save]} }
336
+ it { NewHit["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>contract.build,>contract.default.validate,>persist.save]} }
326
337
  it { NewHit.(:hit => { title: "Hooray For Me" }).inspect("model").must_equal %{<Result:true [#<struct ContractTest::Song title=\"Hooray For Me\">] >} }
327
338
  end
328
339
 
@@ -37,7 +37,7 @@ class DslCallbackTest < MiniTest::Spec
37
37
  def admin_default!(*); _invocations << :admin_default!; end
38
38
  def after_save!(*); _invocations << :after_save!; end
39
39
 
40
- self.| Trailblazer::Operation::Callback[:after_save]
40
+ step Trailblazer::Operation::Callback[:after_save]
41
41
  end
42
42
 
43
43
  def default!(*); _invocations << :default!; end
@@ -61,7 +61,7 @@ class DslCallbackTest < MiniTest::Spec
61
61
  extend Callback::DSL
62
62
  callback :after_save, AfterSaveCallback
63
63
 
64
- self.| Callback[:after_save]
64
+ step Callback[:after_save]
65
65
  end
66
66
 
67
67
  it { OpWithExternalCallback.("title"=>"Thunder Rising").must_equal([:after_save!]) }
@@ -14,10 +14,10 @@ class DslContractTest < MiniTest::Spec
14
14
  end
15
15
 
16
16
  def self.included(includer)
17
- includer.| Trailblazer::Operation::Model( OpenStruct, :new )
18
- includer.| Trailblazer::Operation::Contract::Build()
19
- includer.| Trailblazer::Operation::Contract::Validate()
20
- includer.| Trailblazer::Operation::Contract::Persist( method: :sync )
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
21
  # includer.> ->(op, *) { op["x"] = [] }
22
22
  end
23
23
  end
@@ -9,34 +9,27 @@ class ModelTest < Minitest::Spec
9
9
  #---
10
10
  # use Model semantics, no customizations.
11
11
  class Create < Trailblazer::Operation
12
- self.| Model Song, :new
12
+ step Model Song, :new
13
13
  end
14
14
 
15
15
  # :new new.
16
16
  it { Create.({})["model"].inspect.must_equal %{#<struct ModelTest::Song id=nil>} }
17
17
 
18
18
  class Update < Create
19
- override Model(Song, :update)
19
+ step Model( Song, :find ), override: true
20
20
  end
21
21
 
22
22
  # :find it
23
23
  it { Update.({ id: 1 })["model"].inspect.must_equal %{#<struct ModelTest::Song id=1>} }
24
24
 
25
25
  #- inheritance
26
- it { Update["pipetree"].inspect.must_equal %{[>>operation.new,&model.build]} }
27
-
28
- # override #model with Model included.
29
- class Upsert < Create
30
- def model!(params); params.to_s end
31
- end
32
-
33
- it { Upsert.(id: 9)["model"].must_equal %{{:id=>9}} }
26
+ it { Update["pipetree"].inspect.must_equal %{[>operation.new,>model.build]} }
34
27
 
35
28
  #---
36
29
  # :find_by, exceptionless.
37
30
  class Find < Trailblazer::Operation
38
- self.| Model Song, :find_by
39
- self.| :process
31
+ step Model Song, :find_by
32
+ step :process
40
33
 
41
34
  def process(*); self["x"] = true end
42
35
  end
@@ -56,50 +49,12 @@ class ModelTest < Minitest::Spec
56
49
  Find.(id: 9)["model"].inspect.must_equal %{#<struct ModelTest::Song id=9>}
57
50
  end
58
51
 
59
- #---
60
- # override #model!, without any Model inclusions.
61
- class Delete < Trailblazer::Operation
62
- self.| Model :model!
63
- def model!(params); params.to_s end
64
- end
65
-
66
- it { Delete.(id: 1)["model"].must_equal %{{:id=>1}} }
67
-
68
- #---
69
- # creating the model before operation instantiation (ex Model::External)
70
- class Show < Create
71
- extend Model::BuildMethods # FIXME: how do we communicate that and prevent the include from Model[] ?
72
- self.| Model( Song, :update ), before: "operation.new"
73
- end
74
-
75
- it { Show.({id: 1})["model"].inspect.must_equal %{#<struct ModelTest::Song id=1>} }
76
-
77
-
78
- # TODO: with builder!
79
-
80
- #---
81
- # provide your own object ModelBuilder that includes BuilderMethods.
82
- # this tests that BuildMethods is interchangable and acts as an example how to decouple
83
- # the model building from the operation.
84
- class Index < Trailblazer::Operation
85
- # DISCUSS: help user to do this kind of behavior?
86
- # model Song, :find # ModelBuilder can read this via skills that we pass to it.
87
- self["model.class"] = Song
88
- self["model.action"] = :find
89
-
90
- # this is to be able to use BuildModel.
91
- class ModelBuilder
92
- include Trailblazer::Operation::Model::BuildMethods # #instantiate_model and so on.
93
- alias_method :call, :model!
94
-
95
- def initialize(skills); @delegator = skills end
96
-
97
- extend Uber::Delegates
98
- delegates :@delegator, :[]
99
- end
100
-
101
- self.> ->(options) { options["model"] = ModelBuilder.new(options).(options["params"]) }, after: "operation.new"
102
- end
52
+ # #---
53
+ # # creating the model before operation instantiation (ex Model::External)
54
+ # class Show < Create
55
+ # extend Model::BuildMethods # FIXME: how do we communicate that and prevent the include from Model[] ?
56
+ # step Model( Song, :update ), before: "operation.new"
57
+ # end
103
58
 
104
- it { Index.(id: 1)["model"].inspect.must_equal %{#<struct ModelTest::Song id=1>} }
59
+ # it { Show.({id: 1})["model"].inspect.must_equal %{#<struct ModelTest::Song id=1>} }
105
60
  end
@@ -11,12 +11,12 @@ class PersistTest < Minitest::Spec
11
11
  property :title
12
12
  end
13
13
 
14
- self.| Model( Song, :new )
15
- self.| Contract::Build()
16
- self.| Contract::Validate()
17
- self.< ->(options) { options["1. fail"] = "Validate" }
18
- self.| Contract::Persist()
19
- self.< ->(options) { options["2. fail"] = "Persist" }
14
+ step Model( Song, :new )
15
+ step Contract::Build()
16
+ step Contract::Validate()
17
+ failure ->(options) { options["1. fail"] = "Validate" }
18
+ step Contract::Persist()
19
+ failure ->(options) { options["2. fail"] = "Persist" }
20
20
  end
21
21
 
22
22
  it { Create.(title: "In Recital")["model"].title.must_equal "In Recital" }
@@ -35,7 +35,7 @@ class PersistTest < Minitest::Spec
35
35
  class Update < Create
36
36
  end
37
37
 
38
- it { Update["pipetree"].inspect.must_equal %{[>>operation.new,&model.build,>contract.build,&contract.default.params,&contract.default.validate,<PersistTest::Update:17,&persist.save,<PersistTest::Update:19]} }
38
+ it { Update["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>contract.build,>contract.default.validate,<persist_test.rb:17,>persist.save,<persist_test.rb:19]} }
39
39
 
40
40
  #---
41
41
  it do
@@ -20,22 +20,22 @@ class PipedreamTest < Minitest::Spec
20
20
 
21
21
 
22
22
 
23
- self.| Model[ Song, :new ] # model!)
24
- self.| Policy::Guard[ ->(options){ options["current_user"] == ::Module } ]
25
- self.| Contract[ MyContract]
26
- self.| Policy[ Auth, :user_and_model?]
27
- self.< Contract[ MyContract]
23
+ step Model[ Song, :new ] # model!)
24
+ step Policy::Guard[ ->(options){ options["current_user"] == ::Module } ]
25
+ step Contract[ MyContract]
26
+ step Policy[ Auth, :user_and_model?]
27
+ failure Contract[ MyContract]
28
28
 
29
- # self.| :model
30
- # self.| :guard
31
- # self.| :contract
29
+ # step :model
30
+ # step :guard
31
+ # step :contract
32
32
 
33
33
 
34
34
  # ok Model[Song, :new] # model!)
35
35
  # ok Policy::Guard[ ->(options){ options["current_user"] == ::Module } ]
36
36
  # ok Contract[MyContract]
37
37
  # fail Contract[MyContract]
38
- # self.|> "contract"
38
+ # step> "contract"
39
39
 
40
40
  # | :bla
41
41
  # | ->
@@ -42,21 +42,21 @@ class PipetreeTest < Minitest::Spec
42
42
 
43
43
  MyValidate = ->(input, options) { res= input.validate(options["params"]) { |f| f.sync } }
44
44
  # we can have a separate persist step and wrap in transaction. where do we pass contract, though?
45
- self.& MyValidate, before: Call #replace: Contract::ValidLegacySwitch
45
+ step MyValidate, before: Call #replace: Contract::ValidLegacySwitch
46
46
  #
47
47
  MyAfterSave = ->(input, options) { input["after_save"] = true }
48
- self.> MyAfterSave, after: MyValidate
48
+ success MyAfterSave, after: MyValidate
49
49
 
50
50
  ValidateFailureLogger = ->(input, options) { input["validate fail"] = true }
51
- self.< ValidateFailureLogger, after: MyValidate
51
+ failure ValidateFailureLogger, after: MyValidate
52
52
 
53
- self.> ->(input, options) { input.process(options["params"]) }, replace: Call, name: "my.params"
53
+ success ->(input, options) { input.process(options["params"]) }, replace: Call, name: "my.params"
54
54
 
55
55
  include Model
56
56
 
57
57
  LogBreach = ->(input, options) { input.log_breach! }
58
58
 
59
- self.< LogBreach, after: Policy::Evaluate
59
+ failure LogBreach, after: Policy::Evaluate
60
60
 
61
61
  model Song
62
62
  policy ->(*) { self["current_user"] }