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,2 +0,0 @@
1
- # add anything to result
2
- # insert any options into constructor
@@ -1,133 +0,0 @@
1
- require "test_helper"
2
-
3
- #:policy
4
- class MyPolicy
5
- def initialize(user, model)
6
- @user, @model = user, model
7
- end
8
-
9
- def create?
10
- @user == Module && @model.id.nil?
11
- end
12
-
13
- def new?
14
- @user == Class
15
- end
16
- end
17
- #:policy end
18
-
19
- #--
20
- # with policy
21
- class DocsPunditProcTest < Minitest::Spec
22
- Song = Struct.new(:id)
23
-
24
- #:pundit
25
- class Create < Trailblazer::Operation
26
- step Model( Song, :new )
27
- step Policy::Pundit( MyPolicy, :create? )
28
- # ...
29
- end
30
- #:pundit end
31
-
32
- it { Create["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>policy.default.eval]} }
33
- it { Create.({}, "current_user" => Module).inspect("model").must_equal %{<Result:true [#<struct DocsPunditProcTest::Song id=nil>] >} }
34
- it { Create.({} ).inspect("model").must_equal %{<Result:false [#<struct DocsPunditProcTest::Song id=nil>] >} }
35
-
36
- it do
37
- #:pundit-result
38
- result = Create.({}, "current_user" => Module)
39
- result["result.policy.default"].success? #=> true
40
- result["result.policy.default"]["policy"] #=> #<MyPolicy ...>
41
- #:pundit-result end
42
- result["result.policy.default"].success?.must_equal true
43
- result["result.policy.default"]["policy"].is_a?(MyPolicy).must_equal true
44
- end
45
-
46
- #---
47
- #- override
48
- class New < Create
49
- step Policy::Pundit( MyPolicy, :new? ), override: true
50
- end
51
-
52
- it { New["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>policy.default.eval]} }
53
- it { New.({}, "current_user" => Class ).inspect("model").must_equal %{<Result:true [#<struct DocsPunditProcTest::Song id=nil>] >} }
54
- it { New.({}, "current_user" => nil ).inspect("model").must_equal %{<Result:false [#<struct DocsPunditProcTest::Song id=nil>] >} }
55
-
56
- #---
57
- #- override with :name
58
- class Edit < Trailblazer::Operation
59
- step Policy::Pundit( MyPolicy, :create?, name: "first" )
60
- step Policy::Pundit( MyPolicy, :new?, name: "second" )
61
- end
62
-
63
- class Update < Edit
64
- step Policy::Pundit( MyPolicy, :new?, name: "first" ), override: true
65
- end
66
-
67
- it { Edit["pipetree"].inspect.must_equal %{[>operation.new,>policy.first.eval,>policy.second.eval]} }
68
- it { Edit.({}, "current_user" => Class).inspect("model").must_equal %{<Result:false [nil] >} }
69
- it { Update["pipetree"].inspect.must_equal %{[>operation.new,>policy.first.eval,>policy.second.eval]} }
70
- it { Update.({}, "current_user" => Class).inspect("model").must_equal %{<Result:true [nil] >} }
71
-
72
- #---
73
- # dependency injection
74
- class AnotherPolicy < MyPolicy
75
- def create?
76
- true
77
- end
78
- end
79
-
80
- it {
81
- result =
82
- #:di-call
83
- Create.({},
84
- "current_user" => Module,
85
- "policy.default.eval" => Trailblazer::Operation::Policy::Pundit.build(AnotherPolicy, :create?)
86
- )
87
- #:di-call end
88
- result.inspect("").must_equal %{<Result:true [nil] >} }
89
- end
90
-
91
- #-
92
- # with name:
93
- class PunditWithNameTest < Minitest::Spec
94
- Song = Struct.new(:id)
95
-
96
- #:name
97
- class Create < Trailblazer::Operation
98
- step Model( Song, :new )
99
- step Policy::Pundit( MyPolicy, :create?, name: "after_model" )
100
- # ...
101
- end
102
- #:name end
103
-
104
- it {
105
- #:name-call
106
- result = Create.({}, "current_user" => Module)
107
- result["result.policy.after_model"].success? #=> true
108
- #:name-call end
109
- result["result.policy.after_model"].success?.must_equal true }
110
- end
111
-
112
- #---
113
- # class-level guard
114
- # class DocsGuardClassLevelTest < Minitest::Spec
115
- # #:class-level
116
- # class Create < Trailblazer::Operation
117
- # step Policy::Guard[ ->(options) { options["current_user"] == Module } ],
118
- # before: "operation.new"
119
- # #~pipe--only
120
- # step ->(options) { options["x"] = true }
121
- # #~pipe--only end
122
- # end
123
- # #:class-level end
124
-
125
- # it { Create.(); Create["result.policy"].must_equal nil }
126
- # it { Create.({}, "current_user" => Module)["x"].must_equal true }
127
- # it { Create.({} )["x"].must_equal nil }
128
- # end
129
-
130
-
131
-
132
- # TODO:
133
- #policy.default
@@ -1,268 +0,0 @@
1
- require "test_helper"
2
- require "representable/json"
3
-
4
- #---
5
- # infer
6
- class DocsRepresenterInferTest < Minitest::Spec
7
- Song = Struct.new(:id, :title)
8
-
9
- #:infer
10
- class Create < Trailblazer::Operation
11
- class MyContract < Reform::Form
12
- property :id
13
- end
14
-
15
- step Model( Song, :new )
16
- step Contract::Build( constant: MyContract )
17
- step Contract::Validate( representer: Representer.infer(MyContract, format: Representable::JSON) )
18
- step Contract::Persist( method: :sync )
19
- end
20
- #:infer end
21
-
22
- let (:json) { MultiJson.dump(id: 1) }
23
- it { Create.({}, "document" => json).inspect("model").must_equal %{<Result:true [#<struct DocsRepresenterInferTest::Song id=1, title=nil>] >} }
24
- end
25
-
26
- #---
27
- # explicit
28
- class DocsRepresenterExplicitTest < Minitest::Spec
29
- Song = Struct.new(:id, :title)
30
-
31
- #:explicit-rep
32
- class MyRepresenter < Representable::Decorator
33
- include Representable::JSON
34
- property :id
35
- end
36
- #:explicit-rep end
37
-
38
- #:explicit-op
39
- class Create < Trailblazer::Operation
40
- class MyContract < Reform::Form
41
- property :id
42
- end
43
-
44
- step Model( Song, :new )
45
- step Contract::Build( constant: MyContract )
46
- step Contract::Validate( representer: MyRepresenter ) # :representer
47
- step Contract::Persist( method: :sync )
48
- end
49
- #:explicit-op end
50
-
51
- let (:json) { MultiJson.dump(id: 1) }
52
- it { Create.({}, "document" => json).inspect("model").must_equal %{<Result:true [#<struct DocsRepresenterExplicitTest::Song id=1, title=nil>] >} }
53
- it do
54
- #:explicit-call
55
- Create.({}, "document" => '{"id": 1}')
56
- #:explicit-call end
57
- end
58
-
59
- #- render
60
- it do
61
- #:render
62
- result = Create.({}, "document" => '{"id": 1}')
63
- json = result["representer.default.class"].new(result["model"]).to_json
64
- json #=> '{"id":1}'
65
- #:render end
66
- json.must_equal '{"id":1}'
67
- end
68
-
69
- #-
70
- # with dependency injection
71
- # overriding the JSON representer with an XML one.
72
- #:di-rep
73
- require "representable/xml"
74
-
75
- class MyXMLRepresenter < Representable::Decorator
76
- include Representable::XML
77
- property :id
78
- end
79
- #:di-rep end
80
-
81
- let (:xml) { %{<body><id>1</id></body>} }
82
- it do
83
- #:di-call
84
- result = Create.({},
85
- "document" => '<body><id>1</id></body>',
86
- "representer.default.class" => MyXMLRepresenter # injection
87
- )
88
- #:di-call end
89
- result.inspect("model").must_equal %{<Result:true [#<struct DocsRepresenterExplicitTest::Song id="1", title=nil>] >}
90
- end
91
- end
92
-
93
- #---
94
- # dependency injection
95
- class DocsRepresenterDITest < Minitest::Spec
96
- Song = Struct.new(:id, :title)
97
-
98
- class MyRepresenter < Representable::Decorator
99
- include Representable::JSON
100
- property :id
101
- end
102
-
103
- class Create < Trailblazer::Operation
104
- class MyContract < Reform::Form
105
- property :id
106
- end
107
-
108
- step Model( Song, :new )
109
- step Contract::Build( constant: MyContract )
110
- step Contract::Validate()
111
- step Contract::Persist( method: :sync )
112
- end
113
-
114
- let (:json) { MultiJson.dump(id: 1) }
115
- it { Create.({}, "document" => json,
116
- "representer.default.class" => MyRepresenter).inspect("model").must_equal %{<Result:true [#<struct DocsRepresenterDITest::Song id=1, title=nil>] >} }
117
- end
118
-
119
- #---
120
- # inline
121
- class DocsRepresenterInlineTest < Minitest::Spec
122
- Song = Struct.new(:id, :title)
123
-
124
- #:inline
125
- class Create < Trailblazer::Operation
126
- class MyContract < Reform::Form
127
- property :id
128
- end
129
-
130
- extend Representer::DSL
131
-
132
- representer do
133
- property :id
134
- end
135
-
136
- step Model( Song, :new )
137
- step Contract::Build( constant: MyContract )
138
- step Contract::Validate( representer: self["representer.default.class"] )
139
- step Contract::Persist( method: :sync )
140
- end
141
- #:inline end
142
-
143
- let (:json) { MultiJson.dump(id: 1) }
144
- it { Create.({}, "document" => json).inspect("model").must_equal %{<Result:true [#<struct DocsRepresenterInlineTest::Song id=1, title=nil>] >} }
145
- end
146
-
147
- #---
148
- # rendering
149
- class DocsRepresenterManualRenderTest < Minitest::Spec
150
- Song = Struct.new(:id, :title) do
151
- def self.find(id)
152
- new(id)
153
- end
154
- end
155
-
156
- class Show < Trailblazer::Operation
157
- extend Representer::DSL
158
- representer do
159
- property :id
160
- end
161
-
162
- step Model( Song, :find )
163
- end
164
-
165
- it do
166
- result =Show.({ id: 1 })
167
- json = result["representer.default.class"].new(result["model"]).to_json
168
- json.must_equal %{{"id":1}}
169
- end
170
- end
171
-
172
- #---
173
- # naming
174
-
175
- class DocsRepresenterNamingTest < Minitest::Spec
176
- MyRepresenter = Object
177
-
178
- #:naming
179
- class Create < Trailblazer::Operation
180
- extend Representer::DSL
181
- representer MyRepresenter
182
- end
183
-
184
- Create["representer.default.class"] #=> MyRepresenter
185
- #:naming end
186
- it { Create["representer.default.class"].must_be_kind_of MyRepresenter }
187
- end
188
-
189
- #---
190
- # rendering
191
- require "roar/json/hal"
192
-
193
- class DocsRepresenterFullExampleTest < Minitest::Spec
194
- Song = Struct.new(:id, :title) do
195
- def initialize(*)
196
- self.id = 1
197
- end
198
- end
199
-
200
- #:errors-rep
201
- class ErrorsRepresenter < Representable::Decorator
202
- include Representable::JSON
203
- collection :errors
204
- end
205
- #:errors-rep end
206
-
207
- #:full
208
- class Create < Trailblazer::Operation
209
- extend Contract::DSL
210
- extend Representer::DSL
211
-
212
- contract do
213
- property :title
214
- validates :title, presence: true
215
- end
216
-
217
- representer :parse do
218
- property :title
219
- end
220
-
221
- representer :render do
222
- include Roar::JSON::HAL
223
-
224
- property :id
225
- property :title
226
- link(:self) { "/songs/#{represented.id}" }
227
- end
228
-
229
- representer :errors, ErrorsRepresenter # explicit reference.
230
-
231
- step Model( Song, :new )
232
- step Contract::Build()
233
- step Contract::Validate( representer: self["representer.parse.class"] )
234
- step Contract::Persist( method: :sync )
235
- end
236
- #:full end
237
-
238
- it do
239
- result =Create.({}, "document" => '{"title": "Tested"}')
240
-
241
- json = result["representer.render.class"].new(result["model"]).to_json
242
-
243
- json.must_equal %{{"id":1,"title":"Tested","_links":{"self":{"href":"/songs/1"}}}}
244
-
245
-
246
- #:full-call
247
- def create
248
- result = Create.(params, "document" => request.body.read)
249
-
250
- if result.success?
251
- result["representer.render.class"].new(result["model"]).to_json
252
- else
253
- result["representer.errors.class"].new(result["result.contract.default"]).to_json
254
- end
255
- end
256
- #:full-call end
257
- end
258
-
259
- it do
260
- result =Create.({}, "document" => '{"title": ""}')
261
-
262
- if result.failure?
263
- json = result["representer.errors.class"].new(result["result.contract.default"]).to_json
264
- end
265
-
266
- json.must_equal %{{"errors":[["title","can't be blank"]]}}
267
- end
268
- end
@@ -1,154 +0,0 @@
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
- success ->(options) { raise Y if options["raise-y"] }
15
- step ->(options) { options["z"] = true }
16
- }
17
- step ->(options) { options["b"] = true }
18
- success ->(options) { raise A if options["raise-a"] }
19
- step ->(options) { options["c"] = true }
20
- failure ->(options) { options["inner-err"] = true }
21
- }
22
- step ->(options) { options["e"] = true }
23
- failure ->(options) { options["outer-err"] = true }
24
- end
25
-
26
- it { NestedInsanity["pipetree"].inspect.must_equal %{[>operation.new,>Rescue:10,>rescue_test.rb:22,<rescue_test.rb: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
- true
51
- end
52
- end
53
-
54
- #:simple
55
- class Create < Trailblazer::Operation
56
- class MyContract < Reform::Form
57
- property :title
58
- end
59
-
60
- step Rescue {
61
- step Model(Song, :find)
62
- step Contract::Build( constant: MyContract )
63
- }
64
- step Contract::Validate()
65
- step Contract::Persist( method: :sync )
66
- end
67
- #:simple end
68
-
69
- it { Create.( id: 1, title: "Prodigal Son" )["contract.default"].model.inspect.must_equal %{#<struct RescueTest::Song id=1, title="Prodigal Son">} }
70
- it { Create.( id: nil ).inspect("model").must_equal %{<Result:false [nil] >} }
71
-
72
- #-
73
- # Rescue ExceptionClass, handler: ->(*) { }
74
- class WithExceptionNameTest < Minitest::Spec
75
- #
76
- class MyContract < Reform::Form
77
- property :title
78
- end
79
- #:name
80
- class Create < Trailblazer::Operation
81
- step Rescue( RecordNotFound, KeyError, handler: :rollback! ) {
82
- step Model( Song, :find )
83
- step Contract::Build( constant: MyContract )
84
- }
85
- step Contract::Validate()
86
- step Contract::Persist( method: :sync )
87
-
88
- def rollback!(exception, options)
89
- options["x"] = exception.class
90
- end
91
- end
92
- #:name end
93
-
94
- it { Create.( id: 1, title: "Prodigal Son" )["contract.default"].model.inspect.must_equal %{#<struct RescueTest::Song id=1, title="Prodigal Son">} }
95
- it { Create.( id: 1, title: "Prodigal Son" ).inspect("x").must_equal %{<Result:true [nil] >} }
96
- it { Create.( id: nil ).inspect("model", "x").must_equal %{<Result:false [nil, RescueTest::RecordNotFound] >} }
97
- it { assert_raises(RuntimeError) { Create.( id: "RuntimeError!" ) } }
98
- end
99
-
100
-
101
- #-
102
- # cdennl use-case
103
- class CdennlRescueAndTransactionTest < Minitest::Spec
104
- module Sequel
105
- cattr_accessor :result
106
-
107
- def self.transaction
108
- yield.tap do |res|
109
- self.result = res
110
- end
111
- end
112
- end
113
-
114
- #:example
115
- class Create < Trailblazer::Operation
116
- class MyContract < Reform::Form
117
- property :title
118
- end
119
-
120
- step Rescue( RecordNotFound, handler: :rollback! ) {
121
- step Wrap ->(*, &block) { Sequel.transaction do block.call end } {
122
- step Model( Song, :find )
123
- step ->(options) { options["model"].lock! } # lock the model.
124
- step Contract::Build( constant: MyContract )
125
- step Contract::Validate( )
126
- step Contract::Persist( method: :sync )
127
- }
128
- }
129
- failure :error! # handle all kinds of errors.
130
-
131
- def rollback!(exception, options)
132
- #~ex
133
- options["x"] = exception.class
134
- #~ex end
135
- end
136
-
137
- def error!(options)
138
- #~ex
139
- options["err"] = true
140
- #~ex end
141
- end
142
- end
143
- #:example end
144
-
145
- it { Create.( id: 1, title: "Pie" ).inspect("model", "x", "err").must_equal %{<Result:true [#<struct RescueTest::Song id=1, title=\"Pie\">, nil, nil] >} }
146
- # raise exceptions in Model:
147
- it { Create.( id: nil ).inspect("model", "x").must_equal %{<Result:false [nil, RescueTest::RecordNotFound] >} }
148
- it { assert_raises(RuntimeError) { Create.( id: "RuntimeError!" ) } }
149
- it do
150
- Create.( id: 1, title: "Pie" )
151
- Sequel.result.first.must_equal Pipetree::Railway::Right
152
- end
153
- end
154
- end