trailblazer 1.1.2 → 2.0.0.beta1

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 (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