trailblazer 1.1.2 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +10 -7
  3. data/CHANGES.md +108 -0
  4. data/COMM-LICENSE +91 -0
  5. data/Gemfile +18 -4
  6. data/LICENSE.txt +7 -20
  7. data/README.md +55 -15
  8. data/Rakefile +21 -2
  9. data/draft-1.2.rb +7 -0
  10. data/lib/trailblazer.rb +17 -4
  11. data/lib/trailblazer/dsl.rb +47 -0
  12. data/lib/trailblazer/operation/auto_inject.rb +47 -0
  13. data/lib/trailblazer/operation/builder.rb +18 -18
  14. data/lib/trailblazer/operation/callback.rb +31 -38
  15. data/lib/trailblazer/operation/contract.rb +46 -0
  16. data/lib/trailblazer/operation/controller.rb +45 -27
  17. data/lib/trailblazer/operation/guard.rb +24 -0
  18. data/lib/trailblazer/operation/model.rb +41 -33
  19. data/lib/trailblazer/operation/nested.rb +43 -0
  20. data/lib/trailblazer/operation/params.rb +13 -0
  21. data/lib/trailblazer/operation/persist.rb +13 -0
  22. data/lib/trailblazer/operation/policy.rb +26 -72
  23. data/lib/trailblazer/operation/present.rb +19 -0
  24. data/lib/trailblazer/operation/procedural/contract.rb +15 -0
  25. data/lib/trailblazer/operation/procedural/validate.rb +22 -0
  26. data/lib/trailblazer/operation/pundit.rb +42 -0
  27. data/lib/trailblazer/operation/representer.rb +25 -92
  28. data/lib/trailblazer/operation/rescue.rb +23 -0
  29. data/lib/trailblazer/operation/resolver.rb +18 -24
  30. data/lib/trailblazer/operation/validate.rb +50 -0
  31. data/lib/trailblazer/operation/wrap.rb +37 -0
  32. data/lib/trailblazer/version.rb +1 -1
  33. data/test/{operation/controller_test.rb → controller_test.rb} +8 -4
  34. data/test/docs/auto_inject_test.rb +30 -0
  35. data/test/docs/contract_test.rb +429 -0
  36. data/test/docs/dry_test.rb +31 -0
  37. data/test/docs/guard_test.rb +143 -0
  38. data/test/docs/nested_test.rb +117 -0
  39. data/test/docs/policy_test.rb +2 -0
  40. data/test/docs/pundit_test.rb +109 -0
  41. data/test/docs/representer_test.rb +268 -0
  42. data/test/docs/rescue_test.rb +153 -0
  43. data/test/docs/wrap_test.rb +174 -0
  44. data/test/gemfiles/Gemfile.ruby-1.9 +3 -0
  45. data/test/gemfiles/Gemfile.ruby-2.0 +12 -0
  46. data/test/gemfiles/Gemfile.ruby-2.3 +12 -0
  47. data/test/module_test.rb +22 -15
  48. data/test/operation/builder_test.rb +66 -18
  49. data/test/operation/callback_test.rb +70 -0
  50. data/test/operation/contract_test.rb +385 -15
  51. data/test/operation/dsl/callback_test.rb +18 -30
  52. data/test/operation/dsl/contract_test.rb +209 -19
  53. data/test/operation/dsl/representer_test.rb +42 -15
  54. data/test/operation/guard_test.rb +1 -147
  55. data/test/operation/model_test.rb +105 -0
  56. data/test/operation/params_test.rb +36 -0
  57. data/test/operation/persist_test.rb +44 -0
  58. data/test/operation/pipedream_test.rb +59 -0
  59. data/test/operation/pipetree_test.rb +104 -0
  60. data/test/operation/present_test.rb +24 -0
  61. data/test/operation/pundit_test.rb +104 -0
  62. data/test/{representer_test.rb → operation/representer_test.rb} +58 -42
  63. data/test/operation/resolver_test.rb +34 -70
  64. data/test/operation_test.rb +57 -189
  65. data/test/test_helper.rb +23 -3
  66. data/trailblazer.gemspec +8 -7
  67. metadata +91 -59
  68. data/gemfiles/Gemfile.rails.lock +0 -130
  69. data/gemfiles/Gemfile.reform-2.0 +0 -6
  70. data/gemfiles/Gemfile.reform-2.1 +0 -7
  71. data/lib/trailblazer/autoloading.rb +0 -15
  72. data/lib/trailblazer/endpoint.rb +0 -31
  73. data/lib/trailblazer/operation.rb +0 -175
  74. data/lib/trailblazer/operation/collection.rb +0 -6
  75. data/lib/trailblazer/operation/dispatch.rb +0 -3
  76. data/lib/trailblazer/operation/model/dsl.rb +0 -29
  77. data/lib/trailblazer/operation/model/external.rb +0 -34
  78. data/lib/trailblazer/operation/policy/guard.rb +0 -35
  79. data/lib/trailblazer/operation/uploaded_file.rb +0 -77
  80. data/test/callback_test.rb +0 -104
  81. data/test/collection_test.rb +0 -57
  82. data/test/model_test.rb +0 -148
  83. data/test/operation/external_model_test.rb +0 -71
  84. data/test/operation/policy_test.rb +0 -97
  85. data/test/operation/reject_test.rb +0 -34
  86. data/test/rollback_test.rb +0 -47
@@ -0,0 +1,153 @@
1
+ require "test_helper"
2
+
3
+ class NestedRescueTest < Minitest::Spec
4
+ #---
5
+ # nested raise (i hope people won't use this but it works)
6
+ A = Class.new(RuntimeError)
7
+ Y = Class.new(RuntimeError)
8
+
9
+ class NestedInsanity < Trailblazer::Operation
10
+ step Rescue {
11
+ step ->(options) { options["a"] = true }
12
+ step Rescue {
13
+ step ->(options) { options["y"] = true }
14
+ step ->(options) { raise Y if options["raise-y"] }
15
+ step ->(options) { options["z"] = true }
16
+ }
17
+ step ->(options) { options["b"] = true }
18
+ step ->(options) { raise A if options["raise-a"] }
19
+ step ->(options) { options["c"] = true }
20
+ self.< ->(options) { options["inner-err"] = true }
21
+ }
22
+ step ->(options) { options["e"] = true }
23
+ self.< ->(options) { options["outer-err"] = true }
24
+ end
25
+
26
+ it { NestedInsanity["pipetree"].inspect.must_equal %{[>>operation.new,&Rescue:10,>:22,<NestedRescueTest::NestedInsanity:23]} }
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
+ 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
+ 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] >} }
30
+
31
+ #-
32
+ # inheritance
33
+ class UbernestedInsanity < NestedInsanity
34
+ end
35
+
36
+ it { UbernestedInsanity.({}).inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:true [true, true, true, true, true, true, nil, nil] >} }
37
+ it { UbernestedInsanity.({}, "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] >} }
38
+ end
39
+
40
+ class RescueTest < Minitest::Spec
41
+ RecordNotFound = Class.new(RuntimeError)
42
+
43
+ Song = Struct.new(:id, :title) do
44
+ def self.find(id)
45
+ raise if id == "RuntimeError!"
46
+ id.nil? ? raise(RecordNotFound) : new(id)
47
+ end
48
+
49
+ def lock!
50
+ end
51
+ end
52
+
53
+ #:simple
54
+ class Create < Trailblazer::Operation
55
+ class MyContract < Reform::Form
56
+ property :title
57
+ end
58
+
59
+ step Rescue {
60
+ step Model(Song, :find)
61
+ step Contract::Build( constant: MyContract )
62
+ }
63
+ step Contract::Validate()
64
+ step Persist( method: :sync )
65
+ end
66
+ #:simple end
67
+
68
+ it { Create.( id: 1, title: "Prodigal Son" )["contract.default"].model.inspect.must_equal %{#<struct RescueTest::Song id=1, title="Prodigal Son">} }
69
+ it { Create.( id: nil ).inspect("model").must_equal %{<Result:false [nil] >} }
70
+
71
+ #-
72
+ # Rescue ExceptionClass, handler: ->(*) { }
73
+ class WithExceptionNameTest < Minitest::Spec
74
+ #
75
+ class MyContract < Reform::Form
76
+ property :title
77
+ end
78
+ #:name
79
+ class Create < Trailblazer::Operation
80
+ step Rescue( RecordNotFound, KeyError, handler: :rollback! ) {
81
+ step Model( Song, :find )
82
+ step Contract::Build( constant: MyContract )
83
+ }
84
+ step Contract::Validate()
85
+ step Persist( method: :sync )
86
+
87
+ def rollback!(exception, options)
88
+ options["x"] = exception.class
89
+ end
90
+ end
91
+ #:name end
92
+
93
+ it { Create.( id: 1, title: "Prodigal Son" )["contract.default"].model.inspect.must_equal %{#<struct RescueTest::Song id=1, title="Prodigal Son">} }
94
+ it { Create.( id: 1, title: "Prodigal Son" ).inspect("x").must_equal %{<Result:true [nil] >} }
95
+ it { Create.( id: nil ).inspect("model", "x").must_equal %{<Result:false [nil, RescueTest::RecordNotFound] >} }
96
+ it { assert_raises(RuntimeError) { Create.( id: "RuntimeError!" ) } }
97
+ end
98
+
99
+
100
+ #-
101
+ # cdennl use-case
102
+ class CdennlRescueAndTransactionTest < Minitest::Spec
103
+ module Sequel
104
+ cattr_accessor :result
105
+
106
+ def self.transaction
107
+ yield.tap do |res|
108
+ self.result = res
109
+ end
110
+ end
111
+ end
112
+
113
+ #:example
114
+ class Create < Trailblazer::Operation
115
+ class MyContract < Reform::Form
116
+ property :title
117
+ end
118
+
119
+ step Rescue( RecordNotFound, handler: :rollback! ) {
120
+ step Wrap ->(*, &block) { Sequel.transaction do block.call end } {
121
+ step Model( Song, :find )
122
+ step ->(options) { options["model"].lock! } # lock the model.
123
+ step Contract::Build( constant: MyContract )
124
+ step Contract::Validate( )
125
+ step Persist( method: :sync )
126
+ }
127
+ }
128
+ failure :error! # handle all kinds of errors.
129
+
130
+ def rollback!(exception, options)
131
+ #~ex
132
+ options["x"] = exception.class
133
+ #~ex end
134
+ end
135
+
136
+ def error!(options)
137
+ #~ex
138
+ options["err"] = true
139
+ #~ex end
140
+ end
141
+ end
142
+ #:example end
143
+
144
+ it { Create.( id: 1, title: "Pie" ).inspect("model", "x", "err").must_equal %{<Result:true [#<struct RescueTest::Song id=1, title=\"Pie\">, nil, nil] >} }
145
+ # raise exceptions in Model:
146
+ it { Create.( id: nil ).inspect("model", "x").must_equal %{<Result:false [nil, RescueTest::RecordNotFound] >} }
147
+ it { assert_raises(RuntimeError) { Create.( id: "RuntimeError!" ) } }
148
+ it do
149
+ Create.( id: 1, title: "Pie" )
150
+ Sequel.result.first.must_equal Pipetree::Flow::Right
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,174 @@
1
+ require "test_helper"
2
+
3
+ class WrapTest < Minitest::Spec
4
+ Song = Struct.new(:id, :title) do
5
+ def self.find(id)
6
+ id.nil? ? raise : new(id)
7
+ end
8
+ end
9
+
10
+ class Create < Trailblazer::Operation
11
+ class MyContract < Reform::Form
12
+ property :title
13
+ end
14
+
15
+ step Wrap ->(options, *, &block) {
16
+ begin
17
+ block.call
18
+ rescue => exception
19
+ options["result.model.find"] = "argh! because #{exception.class}"
20
+ false
21
+ end } {
22
+ step Model( Song, :find )
23
+ step Contract::Build( constant: MyContract )
24
+ }
25
+ step Contract::Validate()
26
+ step Persist( method: :sync )
27
+ end
28
+
29
+ it { Create.( id: 1, title: "Prodigal Son" )["contract.default"].model.inspect.must_equal %{#<struct WrapTest::Song id=1, title="Prodigal Son">} }
30
+ it { Create.( id: nil ).inspect("result.model.find").must_equal %{<Result:false [\"argh! because RuntimeError\"] >} }
31
+
32
+ #-
33
+ # Wrap return
34
+ class WrapReturnTest < Minitest::Spec
35
+ class Create < Trailblazer::Operation
36
+ step Wrap ->(options, *, &block) { options["yield?"] ? block.call : false } {
37
+ step ->(options) { options["x"] = true }
38
+ step :noop!
39
+ # ...
40
+ }
41
+
42
+ def noop!(options)
43
+ end
44
+ end
45
+
46
+ it { Create.().inspect("x").must_equal %{<Result:false [nil] >} }
47
+ # returns falsey means deviate to left.
48
+ it { Create.({}, "yield?" => true).inspect("x").must_equal %{<Result:true [true] >} }
49
+ end
50
+
51
+ class WrapWithCallableTest < Minitest::Spec
52
+ class MyWrapper
53
+ extend Uber::Callable
54
+
55
+ def self.call(options, *, &block)
56
+ options["yield?"] ? yield : false
57
+ end
58
+ end
59
+
60
+ class Create < Trailblazer::Operation
61
+ step Wrap( MyWrapper ) {
62
+ step ->(options) { options["x"] = true }
63
+ # ...
64
+ }
65
+ end
66
+
67
+ it { Create.().inspect("x").must_equal %{<Result:false [nil] >} }
68
+ # returns falsey means deviate to left.
69
+ it { Create.({}, "yield?" => true).inspect("x").must_equal %{<Result:true [true] >} }
70
+ end
71
+
72
+ #-
73
+ # arguments for Wrap
74
+ class Update < Trailblazer::Operation
75
+ step Wrap ->(options, operation, pipe, &block) { operation["yield?"] ? block.call : false } {
76
+ step ->(options) { options["x"] = true }
77
+ }
78
+ end
79
+
80
+ it { Update.().inspect("x").must_equal %{<Result:false [nil] >} }
81
+ it { Update.({}, "yield?" => true).inspect("x").must_equal %{<Result:true [true] >} }
82
+
83
+ class WrapExampleProcTest < Minitest::Spec
84
+ module Sequel
85
+ def self.transaction
86
+ yield
87
+ end
88
+ end
89
+
90
+ #:sequel-transaction
91
+ class Create < Trailblazer::Operation
92
+ #~wrap-only
93
+ class MyContract < Reform::Form
94
+ property :title
95
+ end
96
+
97
+ #~wrap-only end
98
+ step Wrap ->(*, &block) { Sequel.transaction do block.call end } {
99
+ step Model( Song, :new )
100
+ #~wrap-only
101
+ step Contract::Build( constant: MyContract )
102
+ step Contract::Validate( )
103
+ step Persist( method: :sync )
104
+ #~wrap-only end
105
+ }
106
+ failure :error! # handle all kinds of errors.
107
+ #~wrap-only
108
+ step :notify!
109
+
110
+ def error!(options)
111
+ # handle errors after the wrap
112
+ end
113
+
114
+ def notify!(options)
115
+ # send emails, because success...
116
+ end
117
+ #~wrap-only end
118
+ end
119
+ #:sequel-transaction end
120
+
121
+ it { Create.( title: "Pie" ).inspect("model", "x", "err").must_equal %{<Result:true [#<struct WrapTest::Song id=nil, title=\"Pie\">, nil, nil] >} }
122
+ end
123
+
124
+ class WrapExampleCallableTest < Minitest::Spec
125
+ module Sequel
126
+ def self.transaction
127
+ yield
128
+ end
129
+ end
130
+
131
+ #:callable-t
132
+ class MyTransaction
133
+ extend Uber::Callable
134
+
135
+ def self.call(options, *)
136
+ Sequel.transaction { yield } # yield runs the nested pipe.
137
+ end
138
+ end
139
+ #:callable-t end
140
+ #:sequel-transaction-callable
141
+ class Create < Trailblazer::Operation
142
+ #~wrap-onlyy
143
+ class MyContract < Reform::Form
144
+ property :title
145
+ end
146
+
147
+ #~wrap-onlyy end
148
+ step Wrap( MyTransaction ) {
149
+ step Model( Song, :new )
150
+ #~wrap-onlyy
151
+ step Contract::Build( constant: MyContract )
152
+ step Contract::Validate( )
153
+ step Persist( method: :sync )
154
+ #~wrap-onlyy end
155
+ }
156
+ failure :error! # handle all kinds of errors.
157
+ #~wrap-onlyy
158
+ step :notify!
159
+
160
+ def error!(options)
161
+ # handle errors after the wrap
162
+ end
163
+
164
+ def notify!(options)
165
+ # send emails, because success...
166
+ end
167
+ #~wrap-onlyy end
168
+ end
169
+ #:sequel-transaction-callable end
170
+
171
+ it { Create.( title: "Pie" ).inspect("model", "x", "err").must_equal %{<Result:true [#<struct WrapTest::Song id=nil, title=\"Pie\">, nil, nil] >} }
172
+ end
173
+ end
174
+
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ gemspec path: "../../"
3
+
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in trailblazer.gemspec
4
+ gemspec path: "../../"
5
+
6
+ gem "multi_json"
7
+ gem "trailblazer-operation"
8
+ gem "minitest-line"
9
+ gem "benchmark-ips"
10
+ gem "activesupport", "~> 4.2.0"
11
+ gem "reform-rails"
12
+ gem "dry-validation"
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in trailblazer.gemspec
4
+ gemspec
5
+
6
+ gem "multi_json"
7
+
8
+ gem "dry-auto_inject"
9
+ gem "dry-matcher"
10
+
11
+ gem "minitest-line"
12
+ gem "benchmark-ips"
data/test/module_test.rb CHANGED
@@ -1,13 +1,15 @@
1
1
  require 'test_helper'
2
2
  require "trailblazer/operation/module"
3
- require "trailblazer/operation/dispatch"
3
+ require "trailblazer/operation/callback"
4
+ require "trailblazer/operation/contract"
4
5
 
5
6
  class OperationModuleTest < MiniTest::Spec
6
7
  Song = Struct.new(:name, :artist)
7
8
  Artist = Struct.new(:id, :full_name)
8
9
 
9
10
  class Create < Trailblazer::Operation
10
- include Trailblazer::Operation::Dispatch
11
+ include Trailblazer::Operation::Callback
12
+ include Contract::Explicit
11
13
 
12
14
  contract do
13
15
  property :name
@@ -20,18 +22,21 @@ class OperationModuleTest < MiniTest::Spec
20
22
  on_change :notify_me!
21
23
  end
22
24
 
23
- def process(params)
24
- @model = Song.new
25
+ attr_reader :model
26
+ def call(params)
27
+ self["model"] = Song.new
25
28
 
26
- validate(params, @model) do
29
+ validate(params, model: self["model"]) do
27
30
  contract.sync
28
31
 
29
32
  dispatch!
30
33
  end
34
+
35
+ self
31
36
  end
32
37
 
33
38
  def dispatched
34
- @dispatched ||= []
39
+ self["dispatched"] ||= []
35
40
  end
36
41
 
37
42
  private
@@ -47,6 +52,8 @@ class OperationModuleTest < MiniTest::Spec
47
52
  contract do
48
53
  property :artist, inherit: true do
49
54
  property :full_name
55
+
56
+ puts definitions.inspect
50
57
  end
51
58
  end
52
59
 
@@ -76,18 +83,18 @@ class OperationModuleTest < MiniTest::Spec
76
83
  it do
77
84
  op = Create.({name: "Feelings", artist: {id: 1, full_name: "The Offspring"}})
78
85
 
79
- op.dispatched.must_equal [:notify_me!]
80
- op.model.name.must_equal "Feelings"
81
- op.model.artist.id.must_equal 1
82
- op.model.artist.full_name.must_equal nil # property not declared.
86
+ op["dispatched"].must_equal [:notify_me!]
87
+ op["model"].name.must_equal "Feelings"
88
+ op["model"].artist.id.must_equal 1
89
+ op["model"].artist.full_name.must_equal nil # property not declared.
83
90
  end
84
91
 
85
92
  it do
86
93
  op = Update.({name: "Feelings", artist: {id: 1, full_name: "The Offspring"}})
87
94
 
88
- op.dispatched.must_equal [:notify_me!, :notify_them!, :notify_you!]
89
- op.model.name.must_equal "Feelings"
90
- op.model.artist.id.must_equal 1
91
- op.model.artist.full_name.must_equal "The Offspring" # property declared via Module.
95
+ op["dispatched"].must_equal [:notify_me!, :notify_them!, :notify_you!]
96
+ op["model"].name.must_equal "Feelings"
97
+ op["model"].artist.id.must_equal 1
98
+ op["model"].artist.full_name.must_equal "The Offspring" # property declared via Module.
92
99
  end
93
- end
100
+ end
@@ -1,41 +1,89 @@
1
1
  require "test_helper"
2
+ require "trailblazer/operation/builder"
2
3
 
3
- class OperationBuilderTest < MiniTest::Spec
4
- class ParentOperation < Trailblazer::Operation
5
- def process(params)
4
+ class BuilderTest < MiniTest::Spec
5
+ #---
6
+ # pass proc to Builder[]
7
+ # this is the puristic way.
8
+ class A < Trailblazer::Operation
9
+ builds = ->(klass, options) do
10
+ return B if options["params"][:sub]
11
+ klass
12
+ end
13
+
14
+ self.| Builder( builds )
15
+ self.| :process
16
+
17
+ class B < A
6
18
  end
7
19
 
20
+ def process(*); self["x"] = self.class end
21
+ end
22
+
23
+ it { A.()["x"].must_equal A }
24
+ it { A.({ sub: true })["x"].must_equal A::B }
25
+ it { A::B["builder"].must_equal nil }
26
+
27
+ #---
28
+ # use manual Builders object
29
+ MyBuilders = Uber::Builder::Builders.new
30
+ MyBuilders << ->(options) { return self::B if options["params"][:sub] }
31
+
32
+ class Create < Trailblazer::Operation
33
+ self.| Builder( MyBuilders )
34
+ self["pipetree"].> ->(input, options) { options["x"] = input.class }
35
+ end
36
+
37
+ it { Create.()["x"].must_equal Create }
38
+
39
+ #---
40
+ #- Builder inheritance
41
+ class B < A
42
+ end
43
+
44
+ it { B["pipetree"].inspect.must_equal %{[>>operation.new,>process]} }
45
+
46
+ #---
47
+ # use Builder DSL
48
+ # you don't need to include Builder in the pipetree
49
+ class ParentOperation < Trailblazer::Operation
8
50
  class Sub < self
9
51
  end
10
52
 
11
- builds do |params|
12
- Sub if params[:sub]
53
+ include Builder
54
+ builds -> (options) do
55
+ return Sub if options["params"][:sub]
13
56
  end
57
+
58
+ def process(*); self["x"] = self.class end
59
+ self.| :process
14
60
  end
15
61
 
16
- it { ParentOperation.run({}).last.class.must_equal ParentOperation }
17
- it { ParentOperation.run({sub: true}).last.class.must_equal ParentOperation::Sub }
18
- it { ParentOperation.({}).class.must_equal ParentOperation }
19
- it { ParentOperation.({sub: true}).class.must_equal ParentOperation::Sub }
62
+ it { ParentOperation.({})["x"].must_equal ParentOperation }
63
+ it { ParentOperation.({ sub: true })["x"].must_equal ParentOperation::Sub }
20
64
  end
21
65
 
66
+ #---
67
+ # copying via Operation.builders
22
68
  class OperationBuilderClassTest < MiniTest::Spec
23
69
  class SuperOperation < Trailblazer::Operation
24
- builds do |params|
25
- self::Sub if params[:sub] # Sub is defined in ParentOperation.
70
+ include Builder
71
+
72
+ builds do |options|
73
+ self::Sub if options["params"][:sub] # Sub is defined in ParentOperation.
26
74
  end
27
75
  end
28
76
 
29
77
  class ParentOperation < Trailblazer::Operation
30
- def process(params)
31
- end
32
-
33
78
  class Sub < self
34
79
  end
35
80
 
36
- self.builder_class = SuperOperation.builder_class
81
+ self.| Builder( SuperOperation.builders )
82
+
83
+ def process(*); self["x"] = self.class end
84
+ self.| :process
37
85
  end
38
86
 
39
- it { ParentOperation.({}).class.must_equal ParentOperation }
40
- it { ParentOperation.({sub: true}).class.must_equal ParentOperation::Sub }
41
- end
87
+ it { ParentOperation.({})["x"].must_equal ParentOperation }
88
+ it { ParentOperation.({ sub: true })["x"].must_equal ParentOperation::Sub }
89
+ end