trailblazer 2.0.7 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.rubocop-https---raw-githubusercontent-com-trailblazer-meta-master-rubocop-yml +101 -0
  4. data/.rubocop.yml +20 -0
  5. data/.rubocop_todo.yml +556 -0
  6. data/.travis.yml +6 -10
  7. data/CHANGES.md +83 -1
  8. data/COMM-LICENSE +46 -75
  9. data/CONTRIBUTING.md +179 -0
  10. data/Gemfile +0 -27
  11. data/{LICENSE.txt → LICENSE} +4 -4
  12. data/README.md +39 -138
  13. data/Rakefile +2 -19
  14. data/lib/trailblazer.rb +3 -17
  15. data/lib/trailblazer/version.rb +3 -1
  16. data/test/test_helper.rb +12 -3
  17. data/trailblazer.gemspec +10 -14
  18. metadata +22 -147
  19. data/doc/Trb-The-Stack.png +0 -0
  20. data/doc/operation-2017.png +0 -0
  21. data/doc/trb.jpg +0 -0
  22. data/lib/trailblazer/dsl.rb +0 -47
  23. data/lib/trailblazer/operation/auto_inject.rb +0 -47
  24. data/lib/trailblazer/operation/callback.rb +0 -35
  25. data/lib/trailblazer/operation/contract.rb +0 -46
  26. data/lib/trailblazer/operation/guard.rb +0 -18
  27. data/lib/trailblazer/operation/model.rb +0 -60
  28. data/lib/trailblazer/operation/module.rb +0 -29
  29. data/lib/trailblazer/operation/nested.rb +0 -113
  30. data/lib/trailblazer/operation/persist.rb +0 -10
  31. data/lib/trailblazer/operation/policy.rb +0 -35
  32. data/lib/trailblazer/operation/procedural/contract.rb +0 -15
  33. data/lib/trailblazer/operation/procedural/validate.rb +0 -22
  34. data/lib/trailblazer/operation/pundit.rb +0 -38
  35. data/lib/trailblazer/operation/representer.rb +0 -31
  36. data/lib/trailblazer/operation/rescue.rb +0 -21
  37. data/lib/trailblazer/operation/test.rb +0 -17
  38. data/lib/trailblazer/operation/validate.rb +0 -68
  39. data/lib/trailblazer/operation/wrap.rb +0 -25
  40. data/test/docs/auto_inject_test.rb +0 -30
  41. data/test/docs/contract_test.rb +0 -525
  42. data/test/docs/dry_test.rb +0 -31
  43. data/test/docs/fast_test.rb +0 -164
  44. data/test/docs/guard_test.rb +0 -169
  45. data/test/docs/macro_test.rb +0 -36
  46. data/test/docs/model_test.rb +0 -75
  47. data/test/docs/nested_test.rb +0 -334
  48. data/test/docs/operation_test.rb +0 -408
  49. data/test/docs/policy_test.rb +0 -2
  50. data/test/docs/pundit_test.rb +0 -133
  51. data/test/docs/representer_test.rb +0 -268
  52. data/test/docs/rescue_test.rb +0 -154
  53. data/test/docs/wrap_test.rb +0 -183
  54. data/test/gemfiles/Gemfile.ruby-1.9 +0 -3
  55. data/test/gemfiles/Gemfile.ruby-2.0 +0 -12
  56. data/test/gemfiles/Gemfile.ruby-2.3 +0 -12
  57. data/test/module_test.rb +0 -100
  58. data/test/operation/callback_test.rb +0 -70
  59. data/test/operation/contract_test.rb +0 -420
  60. data/test/operation/dsl/callback_test.rb +0 -106
  61. data/test/operation/dsl/contract_test.rb +0 -294
  62. data/test/operation/dsl/representer_test.rb +0 -169
  63. data/test/operation/model_test.rb +0 -60
  64. data/test/operation/params_test.rb +0 -36
  65. data/test/operation/persist_test.rb +0 -44
  66. data/test/operation/pipedream_test.rb +0 -59
  67. data/test/operation/pipetree_test.rb +0 -104
  68. data/test/operation/present_test.rb +0 -24
  69. data/test/operation/pundit_test.rb +0 -104
  70. data/test/operation/representer_test.rb +0 -254
  71. data/test/operation/resolver_test.rb +0 -47
  72. data/test/operation_test.rb +0 -143
@@ -1,183 +0,0 @@
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 Contract::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
- success :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
- module MyNotifier
91
- def self.mail; true; end
92
- end
93
-
94
- #:sequel-transaction
95
- class Create < Trailblazer::Operation
96
- #~wrap-only
97
- class MyContract < Reform::Form
98
- property :title
99
- end
100
-
101
- #~wrap-only end
102
- step Wrap ->(*, &block) { Sequel.transaction do block.call end } {
103
- step Model( Song, :new )
104
- #~wrap-only
105
- step Contract::Build( constant: MyContract )
106
- step Contract::Validate( )
107
- step Contract::Persist( method: :sync )
108
- #~wrap-only end
109
- }
110
- failure :error! # handle all kinds of errors.
111
- #~wrap-only
112
- step :notify!
113
-
114
- def error!(options)
115
- # handle errors after the wrap
116
- end
117
-
118
- def notify!(options)
119
- MyNotifier.mail
120
- end
121
- #~wrap-only end
122
- end
123
- #:sequel-transaction end
124
-
125
- it { Create.( title: "Pie" ).inspect("model", "x", "err").must_equal %{<Result:true [#<struct WrapTest::Song id=nil, title=\"Pie\">, nil, nil] >} }
126
- end
127
-
128
- class WrapExampleCallableTest < Minitest::Spec
129
- module Sequel
130
- def self.transaction
131
- yield
132
- end
133
- end
134
-
135
- module MyNotifier
136
- def self.mail; true; end
137
- end
138
-
139
- #:callable-t
140
- class MyTransaction
141
- extend Uber::Callable
142
-
143
- def self.call(options, *)
144
- Sequel.transaction { yield } # yield runs the nested pipe.
145
- # return value decides about left or right track!
146
- end
147
- end
148
- #:callable-t end
149
- #:sequel-transaction-callable
150
- class Create < Trailblazer::Operation
151
- #~wrap-onlyy
152
- class MyContract < Reform::Form
153
- property :title
154
- end
155
-
156
- #~wrap-onlyy end
157
- step Wrap( MyTransaction ) {
158
- step Model( Song, :new )
159
- #~wrap-onlyy
160
- step Contract::Build( constant: MyContract )
161
- step Contract::Validate( )
162
- step Contract::Persist( method: :sync )
163
- #~wrap-onlyy end
164
- }
165
- failure :error! # handle all kinds of errors.
166
- #~wrap-onlyy
167
- step :notify!
168
-
169
- def error!(options)
170
- # handle errors after the wrap
171
- end
172
-
173
- def notify!(options)
174
- MyNotifier.mail # send emails, because success...
175
- end
176
- #~wrap-onlyy end
177
- end
178
- #:sequel-transaction-callable end
179
-
180
- it { Create.( title: "Pie" ).inspect("model", "x", "err").must_equal %{<Result:true [#<struct WrapTest::Song id=nil, title=\"Pie\">, nil, nil] >} }
181
- end
182
- end
183
-
@@ -1,3 +0,0 @@
1
- source 'https://rubygems.org'
2
- gemspec path: "../../"
3
-
@@ -1,12 +0,0 @@
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"
@@ -1,12 +0,0 @@
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"
@@ -1,100 +0,0 @@
1
- require 'test_helper'
2
- require "trailblazer/operation/module"
3
- require "trailblazer/operation/callback"
4
- require "trailblazer/operation/contract"
5
-
6
- class OperationModuleTest < MiniTest::Spec
7
- Song = Struct.new(:name, :artist)
8
- Artist = Struct.new(:id, :full_name)
9
-
10
- class Create < Trailblazer::Operation
11
- include Trailblazer::Operation::Callback
12
- include Contract::Explicit
13
-
14
- contract do
15
- property :name
16
- property :artist, populate_if_empty: Artist do
17
- property :id
18
- end
19
- end
20
-
21
- callback do
22
- on_change :notify_me!
23
- end
24
-
25
- attr_reader :model
26
- def call(params)
27
- self["model"] = Song.new
28
-
29
- validate(params, model: self["model"]) do
30
- contract.sync
31
-
32
- dispatch!
33
- end
34
-
35
- self
36
- end
37
-
38
- def dispatched
39
- self["dispatched"] ||= []
40
- end
41
-
42
- private
43
- def notify_me!(*)
44
- dispatched << :notify_me!
45
- end
46
- end
47
-
48
-
49
- module SignedIn
50
- include Trailblazer::Operation::Module
51
-
52
- contract do
53
- property :artist, inherit: true do
54
- property :full_name
55
-
56
- puts definitions.inspect
57
- end
58
- end
59
-
60
- callback do
61
- on_change :notify_you!
62
- end
63
-
64
- def notify_you!(*)
65
- dispatched << :notify_you!
66
- end
67
- end
68
-
69
-
70
- class Update < Create
71
- callback do
72
- on_change :notify_them!
73
- end
74
-
75
- include SignedIn
76
-
77
- def notify_them!(*)
78
- dispatched << :notify_them!
79
- end
80
- end
81
-
82
-
83
- it do
84
- op = Create.({name: "Feelings", artist: {id: 1, full_name: "The Offspring"}})
85
-
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.
90
- end
91
-
92
- it do
93
- op = Update.({name: "Feelings", artist: {id: 1, full_name: "The Offspring"}})
94
-
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.
99
- end
100
- end
@@ -1,70 +0,0 @@
1
- require 'test_helper'
2
-
3
- # callbacks are tested in Disposable::Callback::Group.
4
- class OperationCallbackTest < MiniTest::Spec
5
- Song = Struct.new(:name)
6
-
7
- #---
8
- # with contract and disposable semantics
9
- class Create < Trailblazer::Operation
10
- extend Contract::DSL
11
-
12
- contract do
13
- property :name
14
- end
15
-
16
- step Model( Song, :new )
17
- step Contract::Build()
18
- step Contract::Validate()
19
- step Callback( :default )
20
-
21
-
22
- extend Callback::DSL
23
-
24
- callback do
25
- on_change :notify_me!
26
- on_change :notify_you!
27
- end
28
-
29
-
30
- # TODO: always dispatch, pass params.
31
-
32
- def dispatched
33
- self["dispatched"] ||= []
34
- end
35
-
36
- private
37
- def notify_me!(*)
38
- dispatched << :notify_me!
39
- end
40
-
41
- def notify_you!(*)
42
- dispatched << :notify_you!
43
- end
44
- end
45
-
46
-
47
- class Update < Create
48
- # TODO: allow skipping groups.
49
- # skip_dispatch :notify_me!
50
-
51
- callback do
52
- remove! :on_change, :notify_me!
53
- end
54
- end
55
-
56
- #---
57
- #- inheritance
58
- it { Update["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>contract.build,>contract.default.validate,>callback.default]} }
59
-
60
-
61
- it "invokes all callbacks" do
62
- res = Create.({"name"=>"Keep On Running"})
63
- res["dispatched"].must_equal [:notify_me!, :notify_you!]
64
- end
65
-
66
- it "does not invoke removed callbacks" do
67
- res = Update.({"name"=>"Keep On Running"})
68
- res["dispatched"].must_equal [:notify_you!]
69
- end
70
- end
@@ -1,420 +0,0 @@
1
- require "test_helper"
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
-
14
-
15
- require "dry/validation"
16
-
17
- class DryValidationTest < Minitest::Spec
18
- class Create < Trailblazer::Operation
19
- extend Contract::DSL
20
-
21
- contract "params", (Dry::Validation.Schema do
22
- required(:id).filled
23
- end)
24
- # self["contract.params"] = Dry::Validation.Schema do
25
- # required(:id).filled
26
- # end
27
-
28
- step :process
29
-
30
- include Procedural::Validate
31
-
32
- def process(options)
33
- validate(options["params"], contract: self["contract.params"], path: "contract.params") { |f| puts f.inspect }
34
- end
35
- end
36
-
37
- #- result object, contract
38
- # success
39
- it { Create.(id: 1)["result.contract.params"].success?.must_equal true }
40
- it { Create.(id: 1)["result.contract.params"].errors.must_equal({}) }
41
- # failure
42
- it { Create.(id: nil)["result.contract.params"].success?.must_equal false }
43
- it { Create.(id: nil)["result.contract.params"].errors.must_equal({:id=>["must be filled"]}) }
44
-
45
- #---
46
- # with Contract::Validate, but before op even gets instantiated.
47
- class Update < Trailblazer::Operation #["contract"]
48
- extend Contract::DSL
49
-
50
- contract "params", (Dry::Validation.Schema do
51
- required(:id).filled
52
- end)
53
-
54
- step ->(options) { options["contract.params"].(options["params"]).success? }, before: "operation.new"
55
- end
56
-
57
- it { Update.( id: 1 ).success?.must_equal true }
58
- it { Update.( ).success?.must_equal false }
59
- end
60
-
61
- class ContractTest < Minitest::Spec
62
- Song = Struct.new(:title)
63
- # # generic form for testing.
64
- # class Form
65
- # def initialize(model, options={})
66
- # @inspect = "#{self.class}: #{model} #{options.inspect}"
67
- # end
68
-
69
- # def validate
70
- # @inspect
71
- # end
72
- # end
73
-
74
- #---
75
- # contract do..end (without constant)
76
- #- explicit validate call
77
- describe "contract do .. end" do
78
- class Index < Trailblazer::Operation
79
- extend Contract::DSL
80
-
81
- contract do
82
- property :title
83
- end
84
-
85
- success ->(options) { options["model"] = Song.new }
86
- # step Model( Song, :new )
87
- step Contract::Build()
88
- step :process
89
-
90
- include Procedural::Validate
91
- # TODO: get model automatically in validate!
92
-
93
- def process(options)
94
- # validate(params, model: Song.new) { |f| self["x"] = f.to_nested_hash }
95
- validate(options["params"]) { |f| self["x"] = f.to_nested_hash }
96
- end
97
- end
98
-
99
- # will create a Reform::Form for us.
100
- it { Index.(title: "Falling Down")["x"].must_equal({"title"=>"Falling Down"}) }
101
- end
102
-
103
- # # TODO: in all step tests.
104
- # describe "dependency injection" do
105
- # class Delete < Trailblazer::Operation
106
- # include Contract::Step
107
- # end
108
-
109
- # class Follow < Trailblazer::Operation
110
- # include Contract::Step
111
- # end
112
-
113
- # # inject contract instance via constructor.
114
- # it { Delete.({}, "contract" => "contract/instance")["contract"].must_equal "contract/instance" }
115
- # # inject contract class.
116
- # it { Follow.({}, "contract.default.class" => Form)["contract"].class.must_equal Form }
117
- # end
118
-
119
-
120
- # # contract(model, [admin: true]).validate
121
- # class Create < Trailblazer::Operation
122
- # include Test::ReturnProcess
123
- # include Contract::Explicit
124
-
125
- # def call(options:false)
126
- # return contract(model: Object, options: { admin: true }).validate if options
127
- # contract(model: Object).validate
128
- # end
129
- # end
130
-
131
- # # inject class, pass in model and options when constructing.
132
- # # contract(model)
133
- # it { Create.({}, "contract.default.class" => Form).must_equal "ContractTest::Form: Object {}" }
134
- # # contract(model, options)
135
- # it { Create.({ options: true }, "contract.default.class" => Form).must_equal "ContractTest::Form: Object {:admin=>true}" }
136
-
137
- # # ::contract Form
138
- # # contract(model).validate
139
- # class Update < Trailblazer::Operation
140
- # include Test::ReturnProcess
141
- # include Contract::Explicit
142
-
143
- # = Form
144
-
145
- # def call(*)
146
- # contract.validate
147
- # end
148
-
149
- # include Model( :Builder
150
- # ) def model!(*)
151
- # Object
152
- # end
153
- # end
154
-
155
- # # use the class contract.
156
- # it { Update.().must_equal "ContractTest::Form: Object {}" }
157
- # # injected contract overrides class.
158
- # it { Update.({}, "contract.default.class" => Injected = Class.new(Form)).must_equal "ContractTest::Injected: Object {}" }
159
-
160
- # # passing Constant into #contract
161
- # # contract(Object.new, { title: "Bad Feeling" }, Contract)
162
- # class Operation < Trailblazer::Operation
163
- # include Contract::Explicit
164
-
165
- # class Contract < Reform::Form
166
- # property :title, virtual: true
167
- # end
168
-
169
- # def process(params)
170
- # contract(model: Object.new, options: { title: "Bad Feeling" }, contract_class: Contract)
171
-
172
- # validate(params)
173
- # end
174
- # end
175
-
176
- # # allow using #contract to inject model, options and class.
177
- # it do
178
- # contract = Operation.(id: 1)["contract"]
179
- # contract.title.must_equal "Bad Feeling"
180
- # contract.must_be_instance_of Operation::Contract
181
- # end
182
-
183
- # # allow using #contract before #validate.
184
- # class Upsert < Trailblazer::Operation
185
- # include Contract::Explicit
186
-
187
- # contract do
188
- # property :id
189
- # property :title
190
- # property :length
191
- # end
192
-
193
- # def process(params)
194
- # self["model"] = Struct.new(:id, :title, :length).new
195
- # contract.id = 1
196
- # validate(params) { contract.length = 3 }
197
- # end
198
- # end
199
-
200
- # it do
201
- # contract = Upsert.(title: "Beethoven")["contract"]
202
- # contract.id.must_equal 1
203
- # contract.title.must_equal "Beethoven"
204
- # contract.length.must_equal 3
205
- # end
206
- # end
207
-
208
- #---
209
- #- validate
210
- class ValidateTest < Minitest::Spec
211
- class Create < Trailblazer::Operation
212
- extend Contract::DSL
213
- contract do
214
- property :title
215
- validates :title, presence: true
216
- end
217
-
218
-
219
- include Procedural::Validate
220
- def process(options)
221
- if validate(options["params"])
222
- self["x"] = "works!"
223
- true
224
- else
225
- self["x"] = "try again"
226
- false
227
- end
228
- end
229
-
230
- step Model( Song, :new ) # FIXME.
231
- step Contract::Build()
232
- step :process
233
- end
234
-
235
- # validate returns the #validate result
236
- it do
237
- Create.(title: nil)["x"].must_equal "try again"
238
- Create.(title: nil).success?.must_equal false
239
- end
240
- it { Create.(title: "SVG")["x"].must_equal "works!" }
241
- it { Create.(title: "SVG").success?.must_equal true }
242
-
243
- # result object from validation.
244
- #- result object, contract
245
- it { Create.(title: 1)["result.contract.default"].success?.must_equal true }
246
- it { Create.(title: 1)["result.contract.default"].errors.messages.must_equal({}) } # FIXME: change API with Fran.
247
- it { Create.(title: nil)["result.contract.default"].success?.must_equal false }
248
- it { Create.(title: nil)["result.contract.default"].errors.messages.must_equal({:title=>["can't be blank"]}) } # FIXME: change API with Fran.
249
- # #---
250
- # # validate with block returns result.
251
- # class Update < Trailblazer::Operation
252
- # include Contract::Explicit
253
- # contract Form
254
-
255
- # def process(params)
256
- # self["x"] = validate(params) { }
257
- # end
258
- # end
259
-
260
- # it { Update.(false)["x"].must_equal false}
261
- # it { Update.(true)["x"]. must_equal true}
262
-
263
- #---
264
- # Contract::Validate[]
265
- class Update < Trailblazer::Operation
266
- extend Contract::DSL
267
- contract do
268
- property :title
269
- validates :title, presence: true
270
- end
271
-
272
- step Model( Song, :new ) # FIXME.
273
- step Contract::Build()
274
- step Contract::Validate() # generic validate call for you.
275
-
276
- # include Procedural::Validate
277
- ->(*) { validate(options["params"][:song]) } # <-- TODO
278
- end
279
-
280
- # success
281
- it do
282
- result = Update.(title: "SVG")
283
- result.success?.must_equal true
284
- result["result.contract.default"].success?.must_equal true
285
- result["result.contract.default"].errors.messages.must_equal({})
286
- end
287
-
288
- # failure
289
- it do
290
- result = Update.(title: nil)
291
- result.success?.must_equal false
292
- result["result.contract.default"].success?.must_equal false
293
- result["result.contract.default"].errors.messages.must_equal({:title=>["can't be blank"]})
294
- end
295
-
296
- #---
297
- # Contract::Validate[key: :song]
298
- class Upsert < Trailblazer::Operation
299
- extend Contract::DSL
300
- contract do
301
- property :title
302
- validates :title, presence: true
303
- end
304
-
305
- step Model( Song, :new ) # FIXME.
306
- step Contract::Build()
307
- step Contract::Validate( key: :song) # generic validate call for you.
308
- # ->(*) { validate(options["params"][:song]) } # <-- TODO
309
- step Contract::Persist( method: :sync )
310
- end
311
-
312
- # success
313
- it { Upsert.(song: { title: "SVG" }).success?.must_equal true }
314
- # failure
315
- it { Upsert.(song: { title: nil }).success?.must_equal false }
316
- # key not found
317
- it { Upsert.().success?.must_equal false }
318
-
319
- #---
320
- # contract.default.params gets set (TODO: change in 2.1)
321
- it { Upsert.(song: { title: "SVG" })["params"].must_equal({:song=>{:title=>"SVG"}}) }
322
- it { Upsert.(song: { title: "SVG" })["contract.default.params"].must_equal({:title=>"SVG"}) }
323
-
324
- #---
325
- #- inheritance
326
- class New < Upsert
327
- end
328
-
329
- it { New["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>contract.build,>contract.default.validate,>persist.save]} }
330
-
331
- #- overwriting Validate
332
- class NewHit < Upsert
333
- step Contract::Validate( key: :hit ), override: true
334
- end
335
-
336
- it { NewHit["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>contract.build,>contract.default.validate,>persist.save]} }
337
- it { NewHit.(:hit => { title: "Hooray For Me" }).inspect("model").must_equal %{<Result:true [#<struct ContractTest::Song title=\"Hooray For Me\">] >} }
338
- end
339
-
340
- # #---
341
- # # allow using #contract to inject model and arguments.
342
- # class OperationContractWithOptionsTest < Minitest::Spec
343
- # # contract(model, title: "Bad Feeling")
344
- # class Operation < Trailblazer::Operation
345
- # include Contract::Explicit
346
- # contract do
347
- # property :id
348
- # property :title, virtual: true
349
- # end
350
-
351
- # def process(params)
352
- # model = Struct.new(:id).new
353
-
354
- # contract(model: model, options: { title: "Bad Feeling" })
355
-
356
- # validate(params)
357
- # end
358
- # end
359
-
360
- # it do
361
- # op = Operation.(id: 1)
362
- # op["contract"].id.must_equal 1
363
- # op["contract"].title.must_equal "Bad Feeling"
364
- # end
365
-
366
- # # contract({ song: song, album: album }, title: "Medicine Balls")
367
- # class CompositionOperation < Trailblazer::Operation
368
- # include Contract::Explicit
369
- # contract do
370
- # include Reform::Form::Composition
371
- # property :song_id, on: :song
372
- # property :album_name, on: :album
373
- # property :title, virtual: true
374
- # end
375
-
376
- # def process(params)
377
- # song = Struct.new(:song_id).new(1)
378
- # album = Struct.new(:album_name).new("Forever Malcom Young")
379
-
380
- # contract(model: { song: song, album: album }, options: { title: "Medicine Balls" })
381
-
382
- # validate(params)
383
- # end
384
- # end
385
-
386
- # it do
387
- # contract = CompositionOperation.({})["contract"]
388
- # contract.song_id.must_equal 1
389
- # contract.album_name.must_equal "Forever Malcom Young"
390
- # contract.title.must_equal "Medicine Balls"
391
- # end
392
-
393
- # # validate(params, { song: song, album: album }, title: "Medicine Balls")
394
- # class CompositionValidateOperation < Trailblazer::Operation
395
- # include Contract::Explicit
396
- # contract do
397
- # include Reform::Form::Composition
398
- # property :song_id, on: :song
399
- # property :album_name, on: :album
400
- # property :title, virtual: true
401
- # end
402
-
403
- # def process(params)
404
- # song = Struct.new(:song_id).new(1)
405
- # album = Struct.new(:album_name).new("Forever Malcom Young")
406
-
407
- # validate(params, model: { song: song, album: album }, options: { title: "Medicine Balls" })
408
- # end
409
- # end
410
-
411
- # it do
412
- # contract = CompositionValidateOperation.({})["contract"]
413
- # contract.song_id.must_equal 1
414
- # contract.album_name.must_equal "Forever Malcom Young"
415
- # contract.title.must_equal "Medicine Balls"
416
- # end
417
- end
418
-
419
- # TODO: full stack test with validate, process, save, etc.
420
- # with model!