trailblazer 2.0.7 → 2.1.0

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