polist 0.4.0 → 1.4.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/.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 [](https://badge.fury.io/rb/polist) [](https://travis-ci.org/umbrellio/polist) [](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
|