polist 0.4.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +31 -0
- data/.gitignore +0 -1
- data/.rubocop.yml +4 -1
- data/CHANGELOG.md +53 -0
- data/Gemfile +0 -2
- data/Gemfile.lock +117 -0
- data/LICENSE.txt +1 -1
- data/README.md +172 -5
- data/Rakefile +14 -2
- data/bin/console +8 -0
- data/lib/polist.rb +3 -3
- data/lib/polist/builder.rb +54 -0
- data/lib/polist/service.rb +51 -9
- data/lib/polist/service/middleware.rb +20 -0
- data/lib/polist/struct.rb +20 -0
- data/lib/polist/version.rb +1 -1
- data/polist.gemspec +13 -11
- metadata +23 -17
- data/.travis.yml +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a66da4b33f58d9ed4ff5e449d0ee209793fd17477648a2ae37292ee856cb5a58
|
4
|
+
data.tar.gz: 6d0886005342b86c7b2dc75510f3ede54b108b1b17eb2fe1c5cc273f544cd20a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd47fe21f11eefac3483d886fba883c0ae5b5675eb93c5f2d0850826f09c5ba9ebef845ffa104b48e2f3da4aa5aa4aedacc5f7210df94b466084f52b89a278e4
|
7
|
+
data.tar.gz: 1f38095d7c52be31ba11f23e1fd0b92c50df90442ec506124d34cf10f5b5fb5a88aa71e10214baf81b86fe1ba2b4cdc41de2738e03e19f09b8ce0771b2d47a65
|
@@ -0,0 +1,31 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
test:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
|
9
|
+
# We want to run on external PRs, but not on our own internal PRs as they'll be run on push event
|
10
|
+
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'umbrellio/polist'
|
11
|
+
|
12
|
+
strategy:
|
13
|
+
fail-fast: false
|
14
|
+
matrix:
|
15
|
+
ruby: ["2.7", "3.0"]
|
16
|
+
|
17
|
+
name: ${{ matrix.ruby }}
|
18
|
+
|
19
|
+
steps:
|
20
|
+
- uses: actions/checkout@v2
|
21
|
+
|
22
|
+
- uses: ruby/setup-ruby@v1
|
23
|
+
with:
|
24
|
+
ruby-version: ${{ matrix.ruby }}
|
25
|
+
bundler-cache: true
|
26
|
+
|
27
|
+
- run: bundle exec rake
|
28
|
+
|
29
|
+
- uses: coverallsapp/github-action@v1.1.2
|
30
|
+
with:
|
31
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [1.4.0] - 2021-06-21
|
4
|
+
|
5
|
+
### Fixed
|
6
|
+
|
7
|
+
- Fixes for Ruby 3 ([@tycooon]) [#9]
|
8
|
+
|
9
|
+
## [1.2.0] - 2019-03-15
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- Block support for `#call` and `#run` ([@VanyaZ158]) [#5]
|
14
|
+
|
15
|
+
## [1.1.1] - 2019-02-09
|
16
|
+
|
17
|
+
### Fixed
|
18
|
+
|
19
|
+
- Fixed middlewares not being called properly in inherited classes ([@tycooon]) [#4]
|
20
|
+
|
21
|
+
## [1.1.0] - 2019-02-03
|
22
|
+
|
23
|
+
### Added
|
24
|
+
|
25
|
+
- Middlewares support ([@nesaulov]) [#2]
|
26
|
+
|
27
|
+
## [1.0.0] - 2019-02-03
|
28
|
+
|
29
|
+
### Added
|
30
|
+
|
31
|
+
- `Polist::Struct` and `Polist::Builder` ([@tycooon]) [#3]
|
32
|
+
|
33
|
+
## [0.4.0] - 2017-10-31
|
34
|
+
|
35
|
+
- Initial public version.
|
36
|
+
|
37
|
+
[0.4.0]: https://github.com/umbrellio/polist/tree/v0.4.0
|
38
|
+
[1.0.0]: https://github.com/umbrellio/polist/compare/v0.4.0...v1.0.0
|
39
|
+
[1.1.0]: https://github.com/umbrellio/polist/compare/v1.0.0...v1.1.0
|
40
|
+
[1.1.1]: https://github.com/umbrellio/polist/compare/v1.1.0...v1.1.1
|
41
|
+
[1.2.0]: https://github.com/umbrellio/polist/compare/v1.1.1...v1.2.0
|
42
|
+
[1.4.0]: https://github.com/umbrellio/polist/compare/v1.2.0...v1.4.0
|
43
|
+
[Unreleased]: https://github.com/umbrellio/polist/compare/v1.4.0...HEAD
|
44
|
+
|
45
|
+
[@nesaulov]: https://github.com/nesaulov
|
46
|
+
[@VanyaZ158]: https://github.com/VanyaZ158
|
47
|
+
[@tycooon]: https://github.com/tycooon
|
48
|
+
|
49
|
+
[#2]: https://github.com/umbrellio/polist/pull/2
|
50
|
+
[#3]: https://github.com/umbrellio/polist/pull/3
|
51
|
+
[#4]: https://github.com/umbrellio/polist/pull/4
|
52
|
+
[#5]: https://github.com/umbrellio/polist/pull/5
|
53
|
+
[#9]: https://github.com/umbrellio/polist/pull/9
|
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
polist (1.4.0)
|
5
|
+
activemodel (>= 3.0)
|
6
|
+
plissken (>= 0.3)
|
7
|
+
tainbox
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
activemodel (6.1.3.2)
|
13
|
+
activesupport (= 6.1.3.2)
|
14
|
+
activesupport (6.1.3.2)
|
15
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
16
|
+
i18n (>= 1.6, < 2)
|
17
|
+
minitest (>= 5.1)
|
18
|
+
tzinfo (~> 2.0)
|
19
|
+
zeitwerk (~> 2.3)
|
20
|
+
ast (2.4.2)
|
21
|
+
coderay (1.1.3)
|
22
|
+
concurrent-ruby (1.1.9)
|
23
|
+
diff-lcs (1.4.4)
|
24
|
+
docile (1.4.0)
|
25
|
+
i18n (1.8.10)
|
26
|
+
concurrent-ruby (~> 1.0)
|
27
|
+
method_source (1.0.0)
|
28
|
+
minitest (5.14.4)
|
29
|
+
parallel (1.20.1)
|
30
|
+
parser (3.0.1.1)
|
31
|
+
ast (~> 2.4.1)
|
32
|
+
plissken (1.4.1)
|
33
|
+
pry (0.14.1)
|
34
|
+
coderay (~> 1.1)
|
35
|
+
method_source (~> 1.0)
|
36
|
+
rack (2.2.3)
|
37
|
+
rainbow (3.0.0)
|
38
|
+
rake (13.0.3)
|
39
|
+
regexp_parser (2.1.1)
|
40
|
+
rexml (3.2.5)
|
41
|
+
rspec (3.10.0)
|
42
|
+
rspec-core (~> 3.10.0)
|
43
|
+
rspec-expectations (~> 3.10.0)
|
44
|
+
rspec-mocks (~> 3.10.0)
|
45
|
+
rspec-core (3.10.1)
|
46
|
+
rspec-support (~> 3.10.0)
|
47
|
+
rspec-expectations (3.10.1)
|
48
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
49
|
+
rspec-support (~> 3.10.0)
|
50
|
+
rspec-mocks (3.10.2)
|
51
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
52
|
+
rspec-support (~> 3.10.0)
|
53
|
+
rspec-support (3.10.2)
|
54
|
+
rubocop (1.11.0)
|
55
|
+
parallel (~> 1.10)
|
56
|
+
parser (>= 3.0.0.0)
|
57
|
+
rainbow (>= 2.2.2, < 4.0)
|
58
|
+
regexp_parser (>= 1.8, < 3.0)
|
59
|
+
rexml
|
60
|
+
rubocop-ast (>= 1.2.0, < 2.0)
|
61
|
+
ruby-progressbar (~> 1.7)
|
62
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
63
|
+
rubocop-ast (1.7.0)
|
64
|
+
parser (>= 3.0.1.1)
|
65
|
+
rubocop-config-umbrellio (1.11.0.51)
|
66
|
+
rubocop (= 1.11.0)
|
67
|
+
rubocop-performance (= 1.10.0)
|
68
|
+
rubocop-rails (= 2.9.1)
|
69
|
+
rubocop-rake (= 0.5.1)
|
70
|
+
rubocop-rspec (= 2.2.0)
|
71
|
+
rubocop-sequel (= 0.2.0)
|
72
|
+
rubocop-performance (1.10.0)
|
73
|
+
rubocop (>= 0.90.0, < 2.0)
|
74
|
+
rubocop-ast (>= 0.4.0)
|
75
|
+
rubocop-rails (2.9.1)
|
76
|
+
activesupport (>= 4.2.0)
|
77
|
+
rack (>= 1.1)
|
78
|
+
rubocop (>= 0.90.0, < 2.0)
|
79
|
+
rubocop-rake (0.5.1)
|
80
|
+
rubocop
|
81
|
+
rubocop-rspec (2.2.0)
|
82
|
+
rubocop (~> 1.0)
|
83
|
+
rubocop-ast (>= 1.1.0)
|
84
|
+
rubocop-sequel (0.2.0)
|
85
|
+
rubocop (~> 1.0)
|
86
|
+
ruby-progressbar (1.11.0)
|
87
|
+
simplecov (0.21.2)
|
88
|
+
docile (~> 1.1)
|
89
|
+
simplecov-html (~> 0.11)
|
90
|
+
simplecov_json_formatter (~> 0.1)
|
91
|
+
simplecov-html (0.12.3)
|
92
|
+
simplecov-lcov (0.8.0)
|
93
|
+
simplecov_json_formatter (0.1.3)
|
94
|
+
tainbox (2.1.2)
|
95
|
+
activesupport
|
96
|
+
tzinfo (2.0.4)
|
97
|
+
concurrent-ruby (~> 1.0)
|
98
|
+
unicode-display_width (2.0.0)
|
99
|
+
zeitwerk (2.4.2)
|
100
|
+
|
101
|
+
PLATFORMS
|
102
|
+
x86_64-darwin-19
|
103
|
+
x86_64-darwin-20
|
104
|
+
x86_64-linux
|
105
|
+
|
106
|
+
DEPENDENCIES
|
107
|
+
bundler
|
108
|
+
polist!
|
109
|
+
pry
|
110
|
+
rake
|
111
|
+
rspec
|
112
|
+
rubocop-config-umbrellio
|
113
|
+
simplecov
|
114
|
+
simplecov-lcov
|
115
|
+
|
116
|
+
BUNDLED WITH
|
117
|
+
2.2.20
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,11 +1,23 @@
|
|
1
1
|
# Polist [![Gem Version](https://badge.fury.io/rb/polist.svg)](https://badge.fury.io/rb/polist) [![Build Status](https://travis-ci.org/umbrellio/polist.svg?branch=master)](https://travis-ci.org/umbrellio/polist) [![Coverage Status](https://coveralls.io/repos/github/umbrellio/polist/badge.svg?branch=master)](https://coveralls.io/github/umbrellio/polist?branch=master)
|
2
2
|
|
3
|
-
|
3
|
+
## DEPRECATION NOTICE
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
This gem is no longer actively maintained. As a replacement, you can use [Resol](https://github.com/umbrellio/resol) as well as gems from [smart-rb](https://github.com/smart-rb) family.
|
6
|
+
|
7
|
+
## Description
|
8
|
+
|
9
|
+
Polist is a set of simple tools for creating business logic layer of your applications:
|
10
|
+
|
11
|
+
- `Polist::Service` is a simple class designed for creating service classes.
|
12
|
+
- `Polist::Builder` is a builder system based on `Uber::Builder`.
|
13
|
+
- `Polist::Struct` is a small utility that helps generating simple `Struct`-like object initializers.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Simply add `gem "polist"` to your Gemfile.
|
18
|
+
|
19
|
+
## Using Polist::Service
|
7
20
|
|
8
|
-
### Basic usage example
|
9
21
|
```ruby
|
10
22
|
class MyService < Polist::Service
|
11
23
|
def call
|
@@ -48,9 +60,47 @@ rescue Polist::Service::Failure => error
|
|
48
60
|
end
|
49
61
|
```
|
50
62
|
|
51
|
-
Note that `.run` and `.call` are just shortcuts for `MyService.new(...).run` and `MyService.new(...).call` with the only difference that they always return the service instance instead of the result of `#run` or `#call`. Unlike `#call` though, `#run` is not intended to be
|
63
|
+
Note that `.run` and `.call` are just shortcuts for `MyService.new(...).run` and `MyService.new(...).call` with the only difference that they always return the service instance instead of the result of `#run` or `#call`. Unlike `#call` though, `#run` is not intended to be overwritten in subclasses.
|
64
|
+
|
65
|
+
### Using blocks in #call and #run methods.
|
66
|
+
|
67
|
+
You can use yield in `#call`. And then call `::run` or `::call` class methods with block. For example, we have the class:
|
68
|
+
```ruby
|
69
|
+
class BlockFun < Polist::Service
|
70
|
+
def call
|
71
|
+
success!(yield(1, 2))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
Then we can use it like this:
|
77
|
+
```ruby
|
78
|
+
service = BlockFun.call { |a, b| a + b }
|
79
|
+
|
80
|
+
p service.response # => 3
|
81
|
+
```
|
82
|
+
|
83
|
+
Behind the scenes it just catches passed block in class methods `::run` and `::call`, converts it to proc and then passes proc to instance method `#call` and `#run` by converting it back to block. So, for example, if you want to pass this block to private methods, you can write code like this:
|
84
|
+
```ruby
|
85
|
+
class AnotherBlockFun < Polist::Service
|
86
|
+
def call(&block)
|
87
|
+
success!(block_caller(&block))
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def block_caller
|
93
|
+
yield 1, 2
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
service = AnotherBlockFun.call { |a, b| a + b }
|
98
|
+
|
99
|
+
p service.response # => 3
|
100
|
+
```
|
52
101
|
|
53
102
|
### Using Form objects
|
103
|
+
|
54
104
|
Sometimes you want to use some kind of params parsing and/or validation, and you can do that with the help of `Polist::Service::Form` class. It uses [tainbox](https://github.com/enthrops/tainbox) gem under the hood.
|
55
105
|
|
56
106
|
```ruby
|
@@ -83,12 +133,129 @@ end
|
|
83
133
|
|
84
134
|
MyService.call(param1: "1", param2: "2") # prints false and then ["1", 2, "smth"]
|
85
135
|
```
|
136
|
+
|
86
137
|
The `#form` method is there just for convinience and by default it uses what `#form_attributes` returns as the attributes for the default form class which is the services' `Form` class. You are free to use as many different form classes as you want in your service.
|
87
138
|
|
139
|
+
## Using Polist::Builder
|
140
|
+
|
141
|
+
The build logic is based on [Uber::Builder](https://github.com/apotonick/uber#builder) but it allows recursive builders. See the example:
|
142
|
+
|
143
|
+
Can be used with `Polist::Service` or any other Ruby class.
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
class User
|
147
|
+
include Polist::Builder
|
148
|
+
|
149
|
+
builds do |role|
|
150
|
+
case role
|
151
|
+
when /admin/
|
152
|
+
Admin
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
attr_accessor :role
|
157
|
+
|
158
|
+
def initialize(role)
|
159
|
+
self.role = role
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
class Admin < User
|
164
|
+
builds do |role|
|
165
|
+
SuperAdmin if role == "super_admin"
|
166
|
+
end
|
167
|
+
|
168
|
+
class SuperAdmin < Admin
|
169
|
+
def super?
|
170
|
+
true
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def super?
|
175
|
+
false
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
User.build("user") # => #<User:... @role="user">
|
180
|
+
|
181
|
+
User.build("admin") # => #<Admin:... @role="admin">
|
182
|
+
User.build("admin").super? # => false
|
183
|
+
|
184
|
+
User.build("super_admin") # => #<Admin::SuperAdmin:... @role="super_admin">
|
185
|
+
User.build("super_admin").super? # => true
|
186
|
+
|
187
|
+
Admin.build("smth") # => #<Admin:... @role="admin">
|
188
|
+
SuperAdmin.build("smth") # => #<Admin::SuperAdmin:... @role="admin">
|
189
|
+
```
|
190
|
+
|
191
|
+
## Using Polist::Struct
|
192
|
+
|
193
|
+
Works pretty much the same like Ruby `Struct` class, but you don't have to subclass it.
|
194
|
+
|
195
|
+
Can be used with `Polist::Service` or any other class that don't have initializer specified.
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
class Point
|
199
|
+
include Polist::Struct
|
200
|
+
|
201
|
+
struct :x, :y
|
202
|
+
end
|
203
|
+
|
204
|
+
a = Point.new(15, 25)
|
205
|
+
a.x # => 15
|
206
|
+
a.y # => 25
|
207
|
+
|
208
|
+
b = Point.new(15, 25, 35) # raises ArgumentError: struct size differs
|
209
|
+
|
210
|
+
c = Point.new(15)
|
211
|
+
c.x # => 15
|
212
|
+
c.y # => nil
|
213
|
+
```
|
214
|
+
|
215
|
+
### Using Middlewares
|
216
|
+
|
217
|
+
If you have some common things to be done in more than one service, you can define a middleware and register it inside the said services.
|
218
|
+
Every middleware takes the service into it's constructor and executes `#call`. Thus every middleware has to implement `#call` method and has a `#service` attribute reader.
|
219
|
+
Middlewares delegate `#success!`, `#fail!`, `#error!`, `#form`, `#form_attributes` to the service class they are registered in.
|
220
|
+
Every middleware should be a subclass of `Polist::Service::Middleware`. Middlewares are run before the service itself is run.
|
221
|
+
|
222
|
+
To register a middleware one should use `.register_middleware` class method on a service. More than one middleware can be registered for one service.
|
223
|
+
|
224
|
+
For example:
|
225
|
+
```ruby
|
226
|
+
class MyMiddleware < Polist::Service::Middleware
|
227
|
+
def call
|
228
|
+
fail!(code: :not_cool) if service.fail_on_middleware?
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
class MyService < Polist::Service
|
233
|
+
register_middleware MyMiddleware
|
234
|
+
|
235
|
+
def call
|
236
|
+
success!(code: :cool)
|
237
|
+
end
|
238
|
+
|
239
|
+
def fail_on_middleware?
|
240
|
+
true
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
service = MyService.run
|
245
|
+
service.success? #=> false
|
246
|
+
service.response #=> { code: :not_cool }
|
247
|
+
```
|
248
|
+
|
249
|
+
## Contributing
|
250
|
+
|
251
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/umbrellio/polist.
|
252
|
+
|
88
253
|
## License
|
254
|
+
|
89
255
|
Released under MIT License.
|
90
256
|
|
91
257
|
## Authors
|
258
|
+
|
92
259
|
Created by Yuri Smirnov.
|
93
260
|
|
94
261
|
<a href="https://github.com/umbrellio/">
|
data/Rakefile
CHANGED
@@ -2,7 +2,19 @@
|
|
2
2
|
|
3
3
|
require "bundler/gem_tasks"
|
4
4
|
require "rspec/core/rake_task"
|
5
|
+
require "rubocop"
|
6
|
+
require "rubocop-rspec"
|
7
|
+
require "rubocop-performance"
|
8
|
+
require "rubocop/rake_task"
|
5
9
|
|
6
|
-
|
10
|
+
RuboCop::RakeTask.new(:rubocop) do |t|
|
11
|
+
config_path = File.expand_path(File.join(".rubocop.yml"), __dir__)
|
7
12
|
|
8
|
-
|
13
|
+
t.options = ["--config", config_path]
|
14
|
+
t.requires << "rubocop-rspec"
|
15
|
+
t.requires << "rubocop-performance"
|
16
|
+
end
|
17
|
+
|
18
|
+
RSpec::Core::RakeTask.new(:rspec)
|
19
|
+
|
20
|
+
task default: %i[rubocop rspec]
|
data/bin/console
ADDED
data/lib/polist.rb
CHANGED
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Polist
|
4
|
+
# Most of the code here is borrowed from Uber::Builder code
|
5
|
+
# See https://github.com/apotonick/uber/blob/master/lib/uber/builder.rb
|
6
|
+
module Builder
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
class Builders < Array
|
12
|
+
def call(context, *args, **kwargs)
|
13
|
+
each do |block|
|
14
|
+
klass = block.call(context, *args, **kwargs) and return klass
|
15
|
+
end
|
16
|
+
|
17
|
+
context
|
18
|
+
end
|
19
|
+
|
20
|
+
def <<(proc)
|
21
|
+
wrapped_proc = -> (ctx, *args, **kwargs) { ctx.instance_exec(*args, **kwargs, &proc) }
|
22
|
+
super(wrapped_proc)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
def builders
|
28
|
+
@builders ||= Builders.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def builds(proc = nil, &block)
|
32
|
+
builders << (proc || block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def build(*args, **kwargs)
|
36
|
+
build_klass(*args, **kwargs).new(*args, **kwargs)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Recursively runs class builders on class until no builders on that class found
|
40
|
+
# or some builder returns the class itself
|
41
|
+
def build_klass(*args, **kwargs)
|
42
|
+
klass = self
|
43
|
+
|
44
|
+
loop do
|
45
|
+
new_klass = klass.builders.call(klass, *args, **kwargs)
|
46
|
+
break if new_klass == klass
|
47
|
+
klass = new_klass
|
48
|
+
end
|
49
|
+
|
50
|
+
klass
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/polist/service.rb
CHANGED
@@ -20,22 +20,38 @@ module Polist
|
|
20
20
|
include ActiveModel::Validations
|
21
21
|
end
|
22
22
|
|
23
|
+
module MiddlewareCaller
|
24
|
+
def call
|
25
|
+
unless @__polist_middlewares__called__
|
26
|
+
call_middlewares
|
27
|
+
@__polist_middlewares__called__ = true
|
28
|
+
end
|
29
|
+
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
MiddlewareError = Class.new(StandardError)
|
35
|
+
|
23
36
|
attr_accessor :params
|
24
37
|
|
25
38
|
def self.inherited(klass)
|
26
39
|
klass.const_set(:Failure, Class.new(klass::Failure))
|
40
|
+
klass.prepend(MiddlewareCaller)
|
41
|
+
klass.instance_variable_set(:@__polist_middlewares__, __polist_middlewares__.dup)
|
42
|
+
super
|
27
43
|
end
|
28
44
|
|
29
|
-
def self.build(*args)
|
30
|
-
new(*args)
|
45
|
+
def self.build(*args, **options)
|
46
|
+
new(*args, **options)
|
31
47
|
end
|
32
48
|
|
33
|
-
def self.call(*args)
|
34
|
-
build(*args).tap(
|
49
|
+
def self.call(*args, **options, &block)
|
50
|
+
build(*args, **options).tap { |service| service.call(&block) }
|
35
51
|
end
|
36
52
|
|
37
|
-
def self.run(*args)
|
38
|
-
build(*args).tap(
|
53
|
+
def self.run(*args, **options, &block)
|
54
|
+
build(*args, **options).tap { |service| service.run(&block) }
|
39
55
|
end
|
40
56
|
|
41
57
|
def self.param(*names)
|
@@ -44,6 +60,23 @@ module Polist
|
|
44
60
|
end
|
45
61
|
end
|
46
62
|
|
63
|
+
def self.__polist_middlewares__
|
64
|
+
@__polist_middlewares__ ||= []
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.register_middleware(klass)
|
68
|
+
unless klass < Polist::Service::Middleware
|
69
|
+
raise MiddlewareError,
|
70
|
+
"Middleware #{klass} should be a subclass of Polist::Service::Middleware"
|
71
|
+
end
|
72
|
+
|
73
|
+
__polist_middlewares__ << klass
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.__clear_middlewares__
|
77
|
+
@__polist_middlewares__ = []
|
78
|
+
end
|
79
|
+
|
47
80
|
def initialize(params = {})
|
48
81
|
self.params = params
|
49
82
|
end
|
@@ -51,8 +84,8 @@ module Polist
|
|
51
84
|
# Should be implemented in subclasses
|
52
85
|
def call; end
|
53
86
|
|
54
|
-
def run
|
55
|
-
call
|
87
|
+
def run(&block)
|
88
|
+
call(&block)
|
56
89
|
rescue self.class::Failure => error
|
57
90
|
@response = error.response
|
58
91
|
@failure = true
|
@@ -71,11 +104,20 @@ module Polist
|
|
71
104
|
end
|
72
105
|
|
73
106
|
def validate!
|
74
|
-
|
107
|
+
return if form.valid?
|
108
|
+
first_error = form.errors.to_hash.values.first
|
109
|
+
first_error = first_error.first if first_error.is_a?(Array)
|
110
|
+
error!(first_error)
|
75
111
|
end
|
76
112
|
|
77
113
|
private
|
78
114
|
|
115
|
+
def call_middlewares
|
116
|
+
self.class.__polist_middlewares__.each do |middleware|
|
117
|
+
middleware.new(self).call
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
79
121
|
def form
|
80
122
|
@form ||= self.class::Form.new(form_attributes.to_snake_keys)
|
81
123
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Polist::Service::Middleware
|
4
|
+
def initialize(service)
|
5
|
+
@service = service
|
6
|
+
end
|
7
|
+
|
8
|
+
# Should be implemented in subclasses
|
9
|
+
def call; end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
attr_reader :service
|
14
|
+
|
15
|
+
%i[fail! error! success! form form_attributes].each do |service_method|
|
16
|
+
define_method(service_method) do |*args, **options|
|
17
|
+
service.send(service_method, *args, **options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Polist
|
4
|
+
module Struct
|
5
|
+
module ClassMethods
|
6
|
+
def struct(*attrs)
|
7
|
+
attr_accessor(*attrs)
|
8
|
+
|
9
|
+
define_method(:initialize) do |*args|
|
10
|
+
raise ArgumentError, "struct size differs" if args.length > attrs.length
|
11
|
+
attrs.zip(args).each { |attr, val| public_send(:"#{attr}=", val) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.included(base)
|
17
|
+
base.extend(ClassMethods)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/polist/version.rb
CHANGED
data/polist.gemspec
CHANGED
@@ -1,21 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
lib = File.expand_path("
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
5
|
require "polist/version"
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
|
-
spec.
|
9
|
-
spec.version = Polist::VERSION
|
10
|
-
spec.authors = ["Yuri Smirnov"]
|
11
|
-
spec.email = ["tycooon@yandex.ru"]
|
8
|
+
spec.required_ruby_version = ">= 2.7.0"
|
12
9
|
|
13
|
-
spec.
|
14
|
-
spec.
|
15
|
-
spec.
|
16
|
-
spec.
|
10
|
+
spec.name = "polist"
|
11
|
+
spec.version = Polist::VERSION
|
12
|
+
spec.authors = ["Yuri Smirnov"]
|
13
|
+
spec.email = ["tycooon@yandex.ru", "oss@umbrellio.biz"]
|
17
14
|
|
18
|
-
spec.
|
15
|
+
spec.summary = "A gem for creating simple service classes and more."
|
16
|
+
spec.description = "Polist is a gem for creating simple service classes and more."
|
17
|
+
spec.homepage = "https://github.com/umbrellio/polist"
|
18
|
+
spec.license = "MIT"
|
19
|
+
|
20
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^spec/}) }
|
19
21
|
spec.require_paths = ["lib"]
|
20
22
|
|
21
23
|
spec.add_runtime_dependency "activemodel", ">= 3.0"
|
@@ -23,10 +25,10 @@ Gem::Specification.new do |spec|
|
|
23
25
|
spec.add_runtime_dependency "tainbox"
|
24
26
|
|
25
27
|
spec.add_development_dependency "bundler"
|
26
|
-
spec.add_development_dependency "coveralls"
|
27
28
|
spec.add_development_dependency "pry"
|
28
29
|
spec.add_development_dependency "rake"
|
29
30
|
spec.add_development_dependency "rspec"
|
30
31
|
spec.add_development_dependency "rubocop-config-umbrellio"
|
31
32
|
spec.add_development_dependency "simplecov"
|
33
|
+
spec.add_development_dependency "simplecov-lcov"
|
32
34
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: polist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuri Smirnov
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-06-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -67,7 +67,7 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: pry
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
@@ -81,7 +81,7 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rake
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - ">="
|
@@ -95,7 +95,7 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: rspec
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - ">="
|
@@ -109,7 +109,7 @@ dependencies:
|
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: rubocop-config-umbrellio
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - ">="
|
@@ -123,7 +123,7 @@ dependencies:
|
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: simplecov
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - ">="
|
@@ -137,7 +137,7 @@ dependencies:
|
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
|
-
name: simplecov
|
140
|
+
name: simplecov-lcov
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
143
|
- - ">="
|
@@ -150,30 +150,37 @@ dependencies:
|
|
150
150
|
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0'
|
153
|
-
description: Polist is a gem for creating simple service classes.
|
153
|
+
description: Polist is a gem for creating simple service classes and more.
|
154
154
|
email:
|
155
155
|
- tycooon@yandex.ru
|
156
|
+
- oss@umbrellio.biz
|
156
157
|
executables: []
|
157
158
|
extensions: []
|
158
159
|
extra_rdoc_files: []
|
159
160
|
files:
|
161
|
+
- ".github/workflows/ci.yml"
|
160
162
|
- ".gitignore"
|
161
163
|
- ".rspec"
|
162
164
|
- ".rubocop.yml"
|
163
|
-
-
|
165
|
+
- CHANGELOG.md
|
164
166
|
- Gemfile
|
167
|
+
- Gemfile.lock
|
165
168
|
- LICENSE.txt
|
166
169
|
- README.md
|
167
170
|
- Rakefile
|
171
|
+
- bin/console
|
168
172
|
- lib/polist.rb
|
173
|
+
- lib/polist/builder.rb
|
169
174
|
- lib/polist/service.rb
|
175
|
+
- lib/polist/service/middleware.rb
|
176
|
+
- lib/polist/struct.rb
|
170
177
|
- lib/polist/version.rb
|
171
178
|
- polist.gemspec
|
172
179
|
homepage: https://github.com/umbrellio/polist
|
173
180
|
licenses:
|
174
181
|
- MIT
|
175
182
|
metadata: {}
|
176
|
-
post_install_message:
|
183
|
+
post_install_message:
|
177
184
|
rdoc_options: []
|
178
185
|
require_paths:
|
179
186
|
- lib
|
@@ -181,16 +188,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
181
188
|
requirements:
|
182
189
|
- - ">="
|
183
190
|
- !ruby/object:Gem::Version
|
184
|
-
version:
|
191
|
+
version: 2.7.0
|
185
192
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
186
193
|
requirements:
|
187
194
|
- - ">="
|
188
195
|
- !ruby/object:Gem::Version
|
189
196
|
version: '0'
|
190
197
|
requirements: []
|
191
|
-
|
192
|
-
|
193
|
-
signing_key:
|
198
|
+
rubygems_version: 3.2.20
|
199
|
+
signing_key:
|
194
200
|
specification_version: 4
|
195
|
-
summary: A gem for creating simple service classes.
|
201
|
+
summary: A gem for creating simple service classes and more.
|
196
202
|
test_files: []
|
data/.travis.yml
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
|
3
|
-
sudo: false
|
4
|
-
|
5
|
-
rvm:
|
6
|
-
- 2.3
|
7
|
-
- 2.4
|
8
|
-
- 2.5
|
9
|
-
- ruby-head
|
10
|
-
|
11
|
-
before_install: gem install bundler
|
12
|
-
|
13
|
-
env: SUITE="rspec"
|
14
|
-
|
15
|
-
script: bundle exec $SUITE
|
16
|
-
|
17
|
-
matrix:
|
18
|
-
fast_finish: true
|
19
|
-
# Only run RuboCop on the latest Ruby
|
20
|
-
include:
|
21
|
-
- rvm: 2.5
|
22
|
-
env: SUITE="rubocop"
|
23
|
-
allow_failures:
|
24
|
-
- rvm: ruby-head
|