simple_endpoint 1.0.3 → 2.0.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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +13 -7
- data/.gitignore +1 -0
- data/.rubocop.yml +10 -1
- data/.ruby-version +1 -1
- data/.simplecov +9 -0
- data/README.md +189 -11
- data/lib/simple_endpoint/controller/builder.rb +24 -0
- data/lib/simple_endpoint/controller/class_methods.rb +33 -0
- data/lib/simple_endpoint/controller.rb +46 -0
- data/lib/simple_endpoint/endpoint/endpoint_options.rb +37 -0
- data/lib/simple_endpoint/endpoint.rb +34 -0
- data/lib/simple_endpoint/errors.rb +53 -0
- data/lib/simple_endpoint/version.rb +1 -1
- data/lib/simple_endpoint.rb +8 -124
- data/simple_endpoint.gemspec +4 -2
- metadata +45 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 99f6f5255ecaf8fbdfd30fc756282fa89d7220ef3ff553e66a842bf6ef505e4c
|
4
|
+
data.tar.gz: 38adcb88665a20c53988e47c71a807b4b2766f61a500ff379766284ca7786d83
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42c0d99541de7832ae6154f4ddfc78bea04d1b76482009fd066e940010857243cbcc7befd85cf7304f5341ad9bd36620dd736453d83083347d2d60cb955ac0dc
|
7
|
+
data.tar.gz: 0ed33327f7de2ea71479216bc1eaba274c45c3e5d5c44f5ce24d18630e9c00f837e70fab4662b26b72a4433d2c187037805abbcd5bb583eb20c6696e63ae78d6
|
data/.circleci/config.yml
CHANGED
@@ -3,7 +3,10 @@ version: 2.1
|
|
3
3
|
executors:
|
4
4
|
test_executor:
|
5
5
|
docker:
|
6
|
-
- image:
|
6
|
+
- image: ${RUBY_VERSION}
|
7
|
+
auth:
|
8
|
+
username: $DOCKERHUB_USERNAME
|
9
|
+
password: $DOCKERHUB_PASSWORD
|
7
10
|
working_directory: ~/simple_endpoint
|
8
11
|
|
9
12
|
jobs:
|
@@ -26,9 +29,9 @@ jobs:
|
|
26
29
|
name: Install dependencies
|
27
30
|
command: bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs 4 --retry 3
|
28
31
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
+
- run:
|
33
|
+
name: Run Rubocop
|
34
|
+
command: bundle exec rubocop
|
32
35
|
|
33
36
|
- run:
|
34
37
|
name: Run Tests
|
@@ -39,10 +42,13 @@ workflows:
|
|
39
42
|
jobs:
|
40
43
|
- build:
|
41
44
|
name: 'ruby 2.6.8'
|
42
|
-
ruby_version: 2.6.8
|
45
|
+
ruby_version: circleci/ruby:2.6.8
|
43
46
|
- build:
|
44
47
|
name: 'ruby 2.7.0'
|
45
|
-
ruby_version: 2.7.0
|
48
|
+
ruby_version: circleci/ruby:2.7.0
|
46
49
|
- build:
|
47
50
|
name: 'ruby 3.0.3'
|
48
|
-
ruby_version: 3.0.3
|
51
|
+
ruby_version: circleci/ruby:3.0.3
|
52
|
+
- build:
|
53
|
+
name: 'ruby 3.1.2'
|
54
|
+
ruby_version: cimg/ruby:3.1.2
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
require:
|
1
|
+
require:
|
2
|
+
- rubocop-performance
|
3
|
+
- rubocop-rspec
|
2
4
|
|
3
5
|
AllCops:
|
4
6
|
TargetRubyVersion: 3.1
|
@@ -6,6 +8,7 @@ AllCops:
|
|
6
8
|
SuggestExtensions: false
|
7
9
|
Exclude:
|
8
10
|
- 'simple_endpoint.gemspec'
|
11
|
+
- 'vendor/**/*'
|
9
12
|
|
10
13
|
Style/Documentation:
|
11
14
|
Enabled: false
|
@@ -24,3 +27,9 @@ RSpec/DescribeClass:
|
|
24
27
|
|
25
28
|
RSpec/NestedGroups:
|
26
29
|
Max: 4
|
30
|
+
|
31
|
+
Style/HashSyntax:
|
32
|
+
EnforcedShorthandSyntax: never
|
33
|
+
|
34
|
+
Naming/BlockForwarding:
|
35
|
+
EnforcedStyle: explicit
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.1.
|
1
|
+
3.1.2
|
data/.simplecov
ADDED
data/README.md
CHANGED
@@ -8,7 +8,7 @@ Dry-matcher free implementation of trailblazer endpoint.
|
|
8
8
|
Add this to your Gemfile:
|
9
9
|
|
10
10
|
```ruby
|
11
|
-
gem 'simple_endpoint', '~>
|
11
|
+
gem 'simple_endpoint', '~> 2.0.0'
|
12
12
|
```
|
13
13
|
|
14
14
|
## Getting Started
|
@@ -21,8 +21,19 @@ class ApplicationController < ActionController::Base
|
|
21
21
|
end
|
22
22
|
```
|
23
23
|
|
24
|
-
Define `
|
24
|
+
Define `cases` to specify trailblazer operation result handling.
|
25
25
|
|
26
|
+
```ruby
|
27
|
+
class ApplicationController < ActionController::Base
|
28
|
+
include SimpleEndpoint::Controller
|
29
|
+
|
30
|
+
cases do
|
31
|
+
match(:success) { |result| result.success? }
|
32
|
+
match(:invalid) { |result| result.failure? }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
```
|
36
|
+
or
|
26
37
|
```ruby
|
27
38
|
class ApplicationController < ActionController::Base
|
28
39
|
include SimpleEndpoint::Controller
|
@@ -38,8 +49,19 @@ class ApplicationController < ActionController::Base
|
|
38
49
|
end
|
39
50
|
```
|
40
51
|
|
41
|
-
Define `
|
52
|
+
Define `handler` to specify how to handle each case
|
42
53
|
|
54
|
+
```ruby
|
55
|
+
class ApplicationController < ActionController::Base
|
56
|
+
include SimpleEndpoint::Controller
|
57
|
+
|
58
|
+
handler do
|
59
|
+
on(:success) { |result, **opts| render json: result['model'], **opts, status: 200 }
|
60
|
+
on(:invalid) { |result, **| render json: result['contract.default'].errors, serializer: ErrorSerializer, status: :unprocessable_entity }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
```
|
64
|
+
or `default_handler` method
|
43
65
|
```ruby
|
44
66
|
class ApplicationController < ActionController::Base
|
45
67
|
include SimpleEndpoint::Controller
|
@@ -55,11 +77,11 @@ class ApplicationController < ActionController::Base
|
|
55
77
|
end
|
56
78
|
```
|
57
79
|
|
58
|
-
`OperationIsNotHandled` error will be raised if
|
80
|
+
`OperationIsNotHandled` error will be raised if `cases`/`#default_cases` doesn't contain case for specific operation result.
|
59
81
|
|
60
|
-
`UnhandledResultError` will be raised if
|
82
|
+
`UnhandledResultError` will be raised if `handler`/`#default_hadnler` doesn't contain for some cases.
|
61
83
|
|
62
|
-
`NotImplementedError` will be raised if `default_cases` or `
|
84
|
+
`NotImplementedError` will be raised if `cases`/`#default_cases` or `handler`/`#default_hadnler` aren't defined.
|
63
85
|
|
64
86
|
|
65
87
|
### #endpoint method
|
@@ -91,6 +113,54 @@ end
|
|
91
113
|
|
92
114
|
If you need to redefine operation result handling for specific controller you can do next
|
93
115
|
|
116
|
+
It will redefine or add only **these** cases
|
117
|
+
```ruby
|
118
|
+
class PostsController < ApplicationController
|
119
|
+
cases do
|
120
|
+
match(:success) { |result| result.success? && is_it_raining? }
|
121
|
+
match(:invalid) { |result| result.failure? && is_vasya_in_the_house? }
|
122
|
+
end
|
123
|
+
|
124
|
+
def create
|
125
|
+
endpoint operation: Post::Create
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def is_it_raining?
|
131
|
+
WeatherForecast.for_today.raining?
|
132
|
+
end
|
133
|
+
|
134
|
+
def is_vasya_in_the_house?
|
135
|
+
User.find_by(login: 'vasya').signed_in?
|
136
|
+
end
|
137
|
+
end
|
138
|
+
```
|
139
|
+
If you want to remove parent cases use `inherit` option
|
140
|
+
It will remove cases and add new ones
|
141
|
+
```ruby
|
142
|
+
class PostsController < ApplicationController
|
143
|
+
cases(inherit: false) do
|
144
|
+
match(:success) { |result| result.success? && is_it_raining? }
|
145
|
+
match(:invalid) { |result| result.failure? && is_vasya_in_the_house? }
|
146
|
+
end
|
147
|
+
|
148
|
+
def create
|
149
|
+
endpoint operation: Post::Create
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def is_it_raining?
|
155
|
+
WeatherForecast.for_today.raining?
|
156
|
+
end
|
157
|
+
|
158
|
+
def is_vasya_in_the_house?
|
159
|
+
User.find_by(login: 'vasya').signed_in?
|
160
|
+
end
|
161
|
+
end
|
162
|
+
```
|
163
|
+
or manually create `default_cases` method. Note that it'll override `ApplicationController#default_cases`
|
94
164
|
```ruby
|
95
165
|
class PostsController < ApplicationController
|
96
166
|
def create
|
@@ -117,12 +187,25 @@ class PostsController < ApplicationController
|
|
117
187
|
end
|
118
188
|
```
|
119
189
|
|
120
|
-
Note that it'll override `ApplicationController#default_cases`
|
121
|
-
|
122
190
|
#### Redefining cases for specific controller action
|
123
191
|
|
124
|
-
Code below will redefine only `success` operation handling logic of
|
192
|
+
Code below will redefine only `success` operation handling logic of `cases`/`#default_cases`, it doesn't matter where `cases`/`#default_cases` was defined, at `ApplicationController` or `PostsController`
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
class PostsController < ApplicationController
|
196
|
+
def create
|
197
|
+
cases { on(:success) { |result| result.success? && is_vasya_in_the_house? } }
|
198
|
+
endpoint operation: Post::Create
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
125
202
|
|
203
|
+
def is_vasya_in_the_house?
|
204
|
+
User.find_by(login: 'vasya').signed_in?
|
205
|
+
end
|
206
|
+
end
|
207
|
+
```
|
208
|
+
or
|
126
209
|
```ruby
|
127
210
|
class PostsController < ApplicationController
|
128
211
|
def create
|
@@ -146,8 +229,31 @@ end
|
|
146
229
|
|
147
230
|
#### Redefining handler for specific controller
|
148
231
|
|
149
|
-
If you need to redefine handler logic, simply redefine
|
232
|
+
If you need to redefine handler logic, simply redefine `handler`. It'll redefine only `success` handler
|
233
|
+
```ruby
|
234
|
+
class PostsController < ApplicationController
|
235
|
+
handler do
|
236
|
+
on(:success) { |result, **| head :ok }
|
237
|
+
end
|
150
238
|
|
239
|
+
def create
|
240
|
+
endpoint operation: Post::Create
|
241
|
+
end
|
242
|
+
end
|
243
|
+
```
|
244
|
+
If you want remove parent handler settings you can use `inherit` option. It'll remove all other settings.
|
245
|
+
```ruby
|
246
|
+
class PostsController < ApplicationController
|
247
|
+
handler(inherit: false) do
|
248
|
+
on(:success) { |result, **| head :ok }
|
249
|
+
end
|
250
|
+
|
251
|
+
def create
|
252
|
+
endpoint operation: Post::Create
|
253
|
+
end
|
254
|
+
end
|
255
|
+
```
|
256
|
+
or redefine `default_handler` method
|
151
257
|
```ruby
|
152
258
|
class PostsController < ApplicationController
|
153
259
|
def create
|
@@ -166,6 +272,17 @@ end
|
|
166
272
|
|
167
273
|
#### Redefining handler for specific controller action
|
168
274
|
|
275
|
+
```ruby
|
276
|
+
class PostsController < ApplicationController
|
277
|
+
def create
|
278
|
+
handler { on(:success) { |result, **| render json: { message: 'Nice!' }, status: :created } }
|
279
|
+
endpoint operation: Post::Create,
|
280
|
+
different_handler: different_handler
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
```
|
285
|
+
or
|
169
286
|
```ruby
|
170
287
|
class PostsController < ApplicationController
|
171
288
|
def create
|
@@ -181,7 +298,6 @@ class PostsController < ApplicationController
|
|
181
298
|
}
|
182
299
|
end
|
183
300
|
end
|
184
|
-
|
185
301
|
```
|
186
302
|
|
187
303
|
#### Defining default params for trailblazer operation
|
@@ -195,7 +311,27 @@ Default `#endpoint_options` method implementation
|
|
195
311
|
```
|
196
312
|
|
197
313
|
Redefining `endpoint_options`
|
314
|
+
It will extend existing options
|
315
|
+
```ruby
|
316
|
+
class PostsController < ApplicationController
|
317
|
+
endpoint_options { { params: permitted_params } }
|
198
318
|
|
319
|
+
def permitted_params
|
320
|
+
params.permit(:some, :attributes)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
```
|
324
|
+
If you want to remove previously defined options you can use `inherit` option
|
325
|
+
```ruby
|
326
|
+
class PostsController < ApplicationController
|
327
|
+
endpoint_options(inherit: false) { { params: permitted_params } }
|
328
|
+
|
329
|
+
def permitted_params
|
330
|
+
params.permit(:some, :attributes)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
```
|
334
|
+
Or redefine `endpoint_options` method
|
199
335
|
```ruby
|
200
336
|
class PostsController < ApplicationController
|
201
337
|
|
@@ -228,6 +364,20 @@ end
|
|
228
364
|
|
229
365
|
You can do some actions before `#default_handler` execution
|
230
366
|
|
367
|
+
```ruby
|
368
|
+
class PostsController < ApplicationController
|
369
|
+
def create
|
370
|
+
before_response do
|
371
|
+
on(:success) do |result, **|
|
372
|
+
response.headers['Some-header'] = result[:some_data]
|
373
|
+
end
|
374
|
+
end
|
375
|
+
endpoint operation: Post::Create
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
```
|
380
|
+
or
|
231
381
|
```ruby
|
232
382
|
class PostsController < ApplicationController
|
233
383
|
def create
|
@@ -270,3 +420,31 @@ class PostsController < ApplicationController
|
|
270
420
|
end
|
271
421
|
end
|
272
422
|
```
|
423
|
+
### Migration from v1 to v2
|
424
|
+
:warning::warning::warning:
|
425
|
+
**Be cautious while using v2.x.x. We executing blocks and lambdas explicitly on the instance of the class where `SimpleEndpoint::Controller` module were included. That's related to `handler`/`#default_handler`, `cases`/`#default_cases` and `before_response`.
|
426
|
+
If you are creating lambdas or blocks for handler and cases in different class with internal methods usage it won't work unlike the v1**
|
427
|
+
:warning::warning::warning:
|
428
|
+
|
429
|
+
```ruby
|
430
|
+
class Handler
|
431
|
+
def self.call
|
432
|
+
{
|
433
|
+
success: ->(result, **) { result.success? && some_method? }
|
434
|
+
}
|
435
|
+
end
|
436
|
+
|
437
|
+
private
|
438
|
+
|
439
|
+
def some_method?
|
440
|
+
true
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
class ApplicationController
|
445
|
+
def default_handler
|
446
|
+
# works with v1 but will cause an error in v2
|
447
|
+
Handler.call
|
448
|
+
end
|
449
|
+
end
|
450
|
+
```
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleEndpoint
|
4
|
+
module Controller
|
5
|
+
class Builder
|
6
|
+
def initialize(&block)
|
7
|
+
@config = {}
|
8
|
+
instance_exec(&block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_h
|
12
|
+
@config
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def on(key, &block)
|
18
|
+
@config[key] = block
|
19
|
+
end
|
20
|
+
|
21
|
+
alias match on
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleEndpoint
|
4
|
+
module Controller
|
5
|
+
module ClassMethods
|
6
|
+
attr_accessor :default_handler, :default_cases
|
7
|
+
|
8
|
+
def inherited(subclass)
|
9
|
+
super
|
10
|
+
subclass.default_handler = default_handler.dup
|
11
|
+
subclass.default_cases = default_cases.dup
|
12
|
+
end
|
13
|
+
|
14
|
+
def handler(inherit: true, &block)
|
15
|
+
builder = Builder.new(&block)
|
16
|
+
self.default_handler = ((inherit && default_handler) || {}).merge(builder.to_h)
|
17
|
+
define_method(:default_handler) { self.class.default_handler }
|
18
|
+
end
|
19
|
+
|
20
|
+
def cases(inherit: true, &block)
|
21
|
+
builder = Builder.new(&block)
|
22
|
+
self.default_cases = ((inherit && default_cases) || {}).merge(builder.to_h)
|
23
|
+
define_method(:default_cases) { self.class.default_cases }
|
24
|
+
end
|
25
|
+
|
26
|
+
def endpoint_options(inherit: true, &block)
|
27
|
+
define_method(:endpoint_options) do
|
28
|
+
(inherit ? super() : {}).merge(instance_exec(&block))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleEndpoint
|
4
|
+
module Controller
|
5
|
+
attr_accessor :__different_cases, :__different_handler, :__before_response
|
6
|
+
|
7
|
+
def self.included(object)
|
8
|
+
super
|
9
|
+
object.extend(ClassMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
def endpoint(operation:, options: {}, **kwargs)
|
13
|
+
result = operation.call(**endpoint_options, **options)
|
14
|
+
options = Endpoint::EndpointOptions.new(
|
15
|
+
result: result, default_handler: default_handler, default_cases: default_cases, invoker: self, **kwargs
|
16
|
+
)
|
17
|
+
Endpoint.call(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def endpoint_options
|
23
|
+
{ params: params }
|
24
|
+
end
|
25
|
+
|
26
|
+
def default_handler
|
27
|
+
raise NotImplementedError, HANDLER_ERROR_MESSAGE
|
28
|
+
end
|
29
|
+
|
30
|
+
def default_cases
|
31
|
+
raise NotImplementedError, CASES_ERROR_MESSAGE
|
32
|
+
end
|
33
|
+
|
34
|
+
def cases(&block)
|
35
|
+
self.__different_cases = Builder.new(&block).to_h
|
36
|
+
end
|
37
|
+
|
38
|
+
def handler(&block)
|
39
|
+
self.__different_handler = Builder.new(&block).to_h
|
40
|
+
end
|
41
|
+
|
42
|
+
def before_response(&block)
|
43
|
+
self.__before_response = Builder.new(&block).to_h
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleEndpoint
|
4
|
+
class Endpoint
|
5
|
+
class EndpointOptions
|
6
|
+
attr_reader :options
|
7
|
+
|
8
|
+
def initialize(**options)
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def invoker
|
13
|
+
options[:invoker]
|
14
|
+
end
|
15
|
+
|
16
|
+
def result
|
17
|
+
options[:result]
|
18
|
+
end
|
19
|
+
|
20
|
+
def renderer_options
|
21
|
+
@renderer_options ||= options[:renderer_options] || {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def before_response
|
25
|
+
@before_response ||= (options[:before_response] || invoker.__before_response) || {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def handler
|
29
|
+
@handler ||= options[:default_handler].merge(options[:different_handler] || invoker.__different_handler || {})
|
30
|
+
end
|
31
|
+
|
32
|
+
def cases
|
33
|
+
@cases ||= options[:default_cases].merge(options[:different_cases] || invoker.__different_cases || {})
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleEndpoint
|
4
|
+
class Endpoint
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader :options
|
8
|
+
|
9
|
+
def_delegators :options, :result, :invoker, :renderer_options, :before_response, :handler, :cases
|
10
|
+
|
11
|
+
def self.call(options)
|
12
|
+
new(options).call
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(options)
|
16
|
+
@options = options
|
17
|
+
end
|
18
|
+
|
19
|
+
def call
|
20
|
+
procees_handler(before_response)
|
21
|
+
procees_handler(handler, strict: true)
|
22
|
+
end
|
23
|
+
|
24
|
+
def matched_case
|
25
|
+
@matched_case ||= cases.detect { |_kase, condition| invoker.instance_exec(result, &condition) }&.first
|
26
|
+
@matched_case || raise(OperationIsNotHandled)
|
27
|
+
end
|
28
|
+
|
29
|
+
def procees_handler(handler, strict: false)
|
30
|
+
return invoker.instance_exec(result, **renderer_options, &handler[matched_case]) if handler.key?(matched_case)
|
31
|
+
raise UnhandledResultError.new(matched_case, handler) if strict
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleEndpoint
|
4
|
+
class OperationIsNotHandled < StandardError
|
5
|
+
OPERATION_IS_NOT_HANDLED_ERROR = 'Current operation result is not handled at specified cases'
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
super(OPERATION_IS_NOT_HANDLED_ERROR)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class UnhandledResultError < StandardError
|
13
|
+
def initialize(matched_case, handler)
|
14
|
+
super("Key: #{matched_case} is not present at #{handler}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
HANDLER_ERROR_MESSAGE = <<-LARGE_ERROR
|
19
|
+
Please specify handler
|
20
|
+
|
21
|
+
EXAMPLE:
|
22
|
+
###############################################
|
23
|
+
|
24
|
+
# Can be put into ApplicationController and redefined in subclasses
|
25
|
+
|
26
|
+
class Controller
|
27
|
+
handler
|
28
|
+
on(:<your case name>) { |result, **| <your code goes here> }
|
29
|
+
...
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
###############################################
|
34
|
+
LARGE_ERROR
|
35
|
+
|
36
|
+
CASES_ERROR_MESSAGE = <<-LARGE_ERROR
|
37
|
+
Please define cases
|
38
|
+
|
39
|
+
EXAMPLE:
|
40
|
+
###############################################
|
41
|
+
# default trailblazer-endpoint logic, you can change it
|
42
|
+
# Can be put into ApplicationController and redefined in subclasses
|
43
|
+
|
44
|
+
class Controller
|
45
|
+
cases do
|
46
|
+
match(:<your case name>) { |result| <your matching code goes here> }
|
47
|
+
...
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
###############################################
|
52
|
+
LARGE_ERROR
|
53
|
+
end
|
data/lib/simple_endpoint.rb
CHANGED
@@ -1,129 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
module SimpleEndpoint
|
5
|
-
module Controller
|
6
|
-
def endpoint(operation:,
|
7
|
-
different_cases: {},
|
8
|
-
different_handler: {},
|
9
|
-
options: {},
|
10
|
-
before_response: {},
|
11
|
-
renderer_options: {})
|
12
|
-
Endpoint.call(
|
13
|
-
operation,
|
14
|
-
default_handler.merge(different_handler),
|
15
|
-
default_cases.merge(different_cases),
|
16
|
-
renderer_options,
|
17
|
-
before_response,
|
18
|
-
**endpoint_options.merge(options)
|
19
|
-
)
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def endpoint_options
|
25
|
-
{ params: params }
|
26
|
-
end
|
27
|
-
|
28
|
-
def default_handler
|
29
|
-
raise NotImplementedError, HANDLER_ERROR_MESSAGE
|
30
|
-
end
|
31
|
-
|
32
|
-
def default_cases
|
33
|
-
raise NotImplementedError, CASES_ERROR_MESSAGE
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
class Endpoint
|
38
|
-
def self.call(operation, handler, cases, renderer_options = {}, before_response = {}, **args)
|
39
|
-
result = operation.call(**args)
|
40
|
-
new.call(result, cases, renderer_options, handler, before_response)
|
41
|
-
end
|
42
|
-
|
43
|
-
def call(result, cases, renderer_options = {}, handler = {}, before_response = {})
|
44
|
-
matched_case = matched_case(cases, result)
|
45
|
-
procees_handler(matched_case, before_response, result, renderer_options) unless before_response.empty?
|
46
|
-
procees_handler(matched_case, handler, result, renderer_options, UnhandledResultError)
|
47
|
-
end
|
48
|
-
|
49
|
-
def matched_case(cases, result)
|
50
|
-
matched_case = obtain_matched_case(cases, result)
|
51
|
-
raise OperationIsNotHandled, OPERATION_IS_NOT_HANDLER_ERROR unless matched_case
|
52
|
-
|
53
|
-
matched_case
|
54
|
-
end
|
55
|
-
|
56
|
-
def procees_handler(matched_case, handler, result, renderer_options, exception_class = nil)
|
57
|
-
if handler.key?(matched_case)
|
58
|
-
handler[matched_case]&.(result, **renderer_options)
|
59
|
-
elsif exception_class
|
60
|
-
raise exception_class, "Key: #{matched_case} is not present at #{handler}"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def obtain_matched_case(cases, result)
|
65
|
-
cases.detect { |_kase, condition| condition.call(result) }&.first
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
class OperationIsNotHandled < StandardError; end
|
70
|
-
class UnhandledResultError < StandardError; end
|
71
|
-
|
72
|
-
OPERATION_IS_NOT_HANDLER_ERROR = 'Current operation result is not handled at #default_cases method'
|
73
|
-
HANDLER_ERROR_MESSAGE = <<-LARGE_ERROR
|
74
|
-
Please implement default_handler via case statement
|
3
|
+
require 'forwardable'
|
75
4
|
|
76
|
-
|
77
|
-
|
5
|
+
require_relative 'simple_endpoint/errors'
|
6
|
+
require_relative 'simple_endpoint/endpoint/endpoint_options'
|
7
|
+
require_relative 'simple_endpoint/endpoint'
|
8
|
+
require_relative 'simple_endpoint/controller/builder'
|
9
|
+
require_relative 'simple_endpoint/controller/class_methods'
|
10
|
+
require_relative 'simple_endpoint/controller'
|
78
11
|
|
79
|
-
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def default_handler
|
84
|
-
-> (kase, result) do
|
85
|
-
case kase
|
86
|
-
when :success then render :json ...
|
87
|
-
else
|
88
|
-
# just in case you forgot to add handler for some of case
|
89
|
-
SimpleEndpoint::UnhandledResultError, 'Oh nooooo!!! Really???!!'
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
###############################################
|
95
|
-
|
96
|
-
OR
|
97
|
-
|
98
|
-
You can move this logic to separate singleton class
|
99
|
-
LARGE_ERROR
|
100
|
-
|
101
|
-
CASES_ERROR_MESSAGE = <<-LARGE_ERROR
|
102
|
-
Please implement default cases conditions via hash
|
103
|
-
|
104
|
-
EXAMPLE:
|
105
|
-
###############################################
|
106
|
-
# default trailblazer-endpoint logic, you can change it
|
107
|
-
# Can be put into ApplicationController and redefined in subclasses
|
108
|
-
|
109
|
-
private
|
110
|
-
|
111
|
-
def default_cases
|
112
|
-
{
|
113
|
-
present: -> (result) { result.success? && result["present"] }
|
114
|
-
success: -> (result) { result.success? },
|
115
|
-
created: -> (result) { result.success? && result["model.action"] == :new }
|
116
|
-
invalid: -> (result) { result.failure? },
|
117
|
-
not_found: -> (result) { result.failure? && result["result.model"] && result["result.model"].failure? },
|
118
|
-
unauthenticated: -> (result) { result.failure? && result["result.policy.default"] && result["result.policy.default"].failure? }
|
119
|
-
}
|
120
|
-
end
|
121
|
-
|
122
|
-
###############################################
|
123
|
-
|
124
|
-
OR
|
125
|
-
|
126
|
-
You can move this to separate singleton class
|
127
|
-
LARGE_ERROR
|
12
|
+
module SimpleEndpoint
|
128
13
|
end
|
129
|
-
# rubocop:enable Metrics/ParameterLists
|
data/simple_endpoint.gemspec
CHANGED
@@ -33,9 +33,11 @@ Gem::Specification.new do |spec|
|
|
33
33
|
spec.require_paths = ['lib']
|
34
34
|
|
35
35
|
spec.add_development_dependency 'bundler', '>= 2.1.0'
|
36
|
-
spec.add_development_dependency 'pry-byebug'
|
36
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.9.0'
|
37
37
|
spec.add_development_dependency 'rake', '~> 13.0'
|
38
38
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
39
39
|
spec.add_development_dependency 'rubocop', '~> 1.25.1'
|
40
|
-
spec.add_development_dependency 'rubocop-rspec'
|
40
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 2.10.0'
|
41
|
+
spec.add_development_dependency 'rubocop-performance', '~> 1.14.0'
|
42
|
+
spec.add_development_dependency 'simplecov', '~> 0.21.2'
|
41
43
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_endpoint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Bal
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: pry-byebug
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 3.9.0
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 3.9.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -84,16 +84,44 @@ dependencies:
|
|
84
84
|
name: rubocop-rspec
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 2.10.0
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.10.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-performance
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.14.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.14.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.21.2
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
95
123
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
124
|
+
version: 0.21.2
|
97
125
|
description: Dry-matcher free implementation of trailblazer endpoint, with ability
|
98
126
|
to redefine matchers and handlers behavior for separate controllers or actions.
|
99
127
|
email:
|
@@ -108,6 +136,7 @@ files:
|
|
108
136
|
- ".rubocop.yml"
|
109
137
|
- ".ruby-gemset"
|
110
138
|
- ".ruby-version"
|
139
|
+
- ".simplecov"
|
111
140
|
- CHANGES.md
|
112
141
|
- Gemfile
|
113
142
|
- LICENSE
|
@@ -116,6 +145,12 @@ files:
|
|
116
145
|
- bin/console
|
117
146
|
- bin/setup
|
118
147
|
- lib/simple_endpoint.rb
|
148
|
+
- lib/simple_endpoint/controller.rb
|
149
|
+
- lib/simple_endpoint/controller/builder.rb
|
150
|
+
- lib/simple_endpoint/controller/class_methods.rb
|
151
|
+
- lib/simple_endpoint/endpoint.rb
|
152
|
+
- lib/simple_endpoint/endpoint/endpoint_options.rb
|
153
|
+
- lib/simple_endpoint/errors.rb
|
119
154
|
- lib/simple_endpoint/version.rb
|
120
155
|
- simple_endpoint.gemspec
|
121
156
|
homepage: https://github.com/differencialx/simple_endpoint
|