trailblazer 2.0.0 → 2.0.1

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