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