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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 73c8e323de7cb71b6b39441dd95abf7fbccb8de9cb391790faf2fde7f7f4a20a
4
- data.tar.gz: a281f80553b3db467785bd1a2c04701a428c8dac372658369b8ddae8b5cc22b3
3
+ metadata.gz: a66da4b33f58d9ed4ff5e449d0ee209793fd17477648a2ae37292ee856cb5a58
4
+ data.tar.gz: 6d0886005342b86c7b2dc75510f3ede54b108b1b17eb2fe1c5cc273f544cd20a
5
5
  SHA512:
6
- metadata.gz: 6af97512d4e3f8b135369ce37dedc444758c3d060af1df1839b80a296d2305469b4399138312f31b437d993681b726e01d54054b61d753da99ce4130a085fbef
7
- data.tar.gz: 99420ff893d32b0eb1b02b498e0f51dede79054e24ef4f6282bdbb029f329e7fdebf19c6263e03c0298c44f42a8613d87b564c99e71dc88270fb07d40baaf4ec
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
@@ -1,6 +1,5 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
4
3
  /_yardoc/
5
4
  /coverage/
6
5
  /doc/
data/.rubocop.yml CHANGED
@@ -3,4 +3,7 @@ inherit_gem:
3
3
 
4
4
  AllCops:
5
5
  DisplayCopNames: true
6
- TargetRubyVersion: 2.5
6
+ TargetRubyVersion: 2.7
7
+
8
+ Naming/MemoizedInstanceVariableName:
9
+ Enabled: false
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
@@ -1,6 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  source "https://rubygems.org"
4
-
5
- # Specify your gem's dependencies in polist.gemspec
6
4
  gemspec
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
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2017 Yuri Smirnov
3
+ Copyright (c) 2017-2019 Yuri Smirnov
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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
- `Polist::Service` is a simple class designed for creating service classes.
3
+ ## DEPRECATION NOTICE
4
4
 
5
- ### Installation
6
- Juts add `gem "polist"` to your Gemfile.
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 owerwritten in subclasses.
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
- RSpec::Core::RakeTask.new(:spec)
10
+ RuboCop::RakeTask.new(:rubocop) do |t|
11
+ config_path = File.expand_path(File.join(".rubocop.yml"), __dir__)
7
12
 
8
- task default: :spec
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'polist'
6
+
7
+ require 'pry'
8
+ Pry.start
data/lib/polist.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "polist/builder"
3
4
  require "polist/service"
5
+ require "polist/service/middleware"
6
+ require "polist/struct"
4
7
  require "polist/version"
5
-
6
- module Polist
7
- end
@@ -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
@@ -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(&:call)
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(&:run)
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
- error!(form.errors.to_h.values.first) unless form.valid?
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polist
4
- VERSION = "0.4.0"
4
+ VERSION = "1.4.0"
5
5
  end
data/polist.gemspec CHANGED
@@ -1,21 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path("../lib", __FILE__)
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.name = "polist"
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.summary = "A gem for creating simple service classes."
14
- spec.description = "Polist is a gem for creating simple service classes."
15
- spec.homepage = "https://github.com/umbrellio/polist"
16
- spec.license = "MIT"
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.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^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: 0.4.0
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: 2018-10-31 00:00:00.000000000 Z
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: coveralls
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: pry
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: rake
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: rspec
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: rubocop-config-umbrellio
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
- - ".travis.yml"
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: '0'
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
- rubyforge_project:
192
- rubygems_version: 2.7.6
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