light-services 2.0.0.rc7 → 2.0.0.rc8
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/Gemfile +6 -6
- data/Gemfile.lock +48 -51
- data/README.md +327 -3
- data/lib/light/services/base.rb +3 -3
- data/lib/light/services/collection/base.rb +5 -1
- data/lib/light/services/messages.rb +5 -5
- data/lib/light/services/settings/argument.rb +6 -5
- data/lib/light/services/version.rb +1 -1
- data/light-services.gemspec +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 033745e056484b3cb06dd7047bde7aa8f57d3e7a831f5f9eb2495aec0f9f96e9
|
|
4
|
+
data.tar.gz: 6927b650f82197c0ee0403487f18f71015096d244785ae947d0b8ea4dd56373f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c87c5af17cccc1cce0514a65dd8f87b84d21599fcafec91ea92fa9f5d068842bfb01f0055b98c62340809a47e4eb3721a974b5958c7cd6b6410249251cf4cdca
|
|
7
|
+
data.tar.gz: a48e79125c81939dd5967ea08e6fcafcb6474f9a13edbc07f1e1d4ec69fc83b23e2bc43a660bf978d999d4b97a57c4f543f5f14b65219c555f2266ce3d06b51f
|
data/Gemfile
CHANGED
|
@@ -9,12 +9,12 @@ group :test do
|
|
|
9
9
|
gem "database_cleaner-active_record", "~> 1.8"
|
|
10
10
|
gem "sqlite3", "~> 1.4"
|
|
11
11
|
|
|
12
|
-
gem "codecov", "~> 0.
|
|
12
|
+
gem "codecov", "~> 0.6.0"
|
|
13
13
|
gem "rake", "~> 13.0"
|
|
14
|
-
gem "rspec", "~> 3.
|
|
15
|
-
gem "simplecov", "~> 0.
|
|
14
|
+
gem "rspec", "~> 3.11"
|
|
15
|
+
gem "simplecov", "~> 0.21"
|
|
16
16
|
|
|
17
|
-
gem "rubocop", "~>
|
|
18
|
-
gem "rubocop-performance", "~> 1.
|
|
19
|
-
gem "rubocop-rspec", "~>
|
|
17
|
+
gem "rubocop", "~> 1.27", require: false
|
|
18
|
+
gem "rubocop-performance", "~> 1.13", require: false
|
|
19
|
+
gem "rubocop-rspec", "~> 2.9", require: false
|
|
20
20
|
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,101 +1,98 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
light-services (2.0.0.
|
|
4
|
+
light-services (2.0.0.rc8)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
8
8
|
specs:
|
|
9
|
-
activemodel (6.1.
|
|
10
|
-
activesupport (= 6.1.
|
|
11
|
-
activerecord (6.1.
|
|
12
|
-
activemodel (= 6.1.
|
|
13
|
-
activesupport (= 6.1.
|
|
14
|
-
activesupport (6.1.
|
|
9
|
+
activemodel (6.1.5)
|
|
10
|
+
activesupport (= 6.1.5)
|
|
11
|
+
activerecord (6.1.5)
|
|
12
|
+
activemodel (= 6.1.5)
|
|
13
|
+
activesupport (= 6.1.5)
|
|
14
|
+
activesupport (6.1.5)
|
|
15
15
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
16
16
|
i18n (>= 1.6, < 2)
|
|
17
17
|
minitest (>= 5.1)
|
|
18
18
|
tzinfo (~> 2.0)
|
|
19
19
|
zeitwerk (~> 2.3)
|
|
20
20
|
ast (2.4.2)
|
|
21
|
-
codecov (0.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
concurrent-ruby (1.1.9)
|
|
21
|
+
codecov (0.6.0)
|
|
22
|
+
simplecov (>= 0.15, < 0.22)
|
|
23
|
+
concurrent-ruby (1.1.10)
|
|
25
24
|
database_cleaner (1.99.0)
|
|
26
25
|
database_cleaner-active_record (1.99.0)
|
|
27
26
|
activerecord
|
|
28
27
|
database_cleaner (~> 1.99.0)
|
|
29
|
-
diff-lcs (1.
|
|
28
|
+
diff-lcs (1.5.0)
|
|
30
29
|
docile (1.4.0)
|
|
31
|
-
i18n (1.
|
|
30
|
+
i18n (1.10.0)
|
|
32
31
|
concurrent-ruby (~> 1.0)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
parser (3.0.2.0)
|
|
32
|
+
minitest (5.15.0)
|
|
33
|
+
parallel (1.22.1)
|
|
34
|
+
parser (3.1.2.0)
|
|
37
35
|
ast (~> 2.4.1)
|
|
38
|
-
rainbow (3.
|
|
36
|
+
rainbow (3.1.1)
|
|
39
37
|
rake (13.0.6)
|
|
40
|
-
regexp_parser (2.
|
|
38
|
+
regexp_parser (2.3.0)
|
|
41
39
|
rexml (3.2.5)
|
|
42
|
-
rspec (3.
|
|
43
|
-
rspec-core (~> 3.
|
|
44
|
-
rspec-expectations (~> 3.
|
|
45
|
-
rspec-mocks (~> 3.
|
|
46
|
-
rspec-core (3.
|
|
47
|
-
rspec-support (~> 3.
|
|
48
|
-
rspec-expectations (3.
|
|
40
|
+
rspec (3.11.0)
|
|
41
|
+
rspec-core (~> 3.11.0)
|
|
42
|
+
rspec-expectations (~> 3.11.0)
|
|
43
|
+
rspec-mocks (~> 3.11.0)
|
|
44
|
+
rspec-core (3.11.0)
|
|
45
|
+
rspec-support (~> 3.11.0)
|
|
46
|
+
rspec-expectations (3.11.0)
|
|
49
47
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
50
|
-
rspec-support (~> 3.
|
|
51
|
-
rspec-mocks (3.
|
|
48
|
+
rspec-support (~> 3.11.0)
|
|
49
|
+
rspec-mocks (3.11.1)
|
|
52
50
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
53
|
-
rspec-support (~> 3.
|
|
54
|
-
rspec-support (3.
|
|
55
|
-
rubocop (
|
|
51
|
+
rspec-support (~> 3.11.0)
|
|
52
|
+
rspec-support (3.11.0)
|
|
53
|
+
rubocop (1.27.0)
|
|
56
54
|
parallel (~> 1.10)
|
|
57
|
-
parser (>=
|
|
55
|
+
parser (>= 3.1.0.0)
|
|
58
56
|
rainbow (>= 2.2.2, < 4.0)
|
|
59
|
-
regexp_parser (>= 1.8)
|
|
57
|
+
regexp_parser (>= 1.8, < 3.0)
|
|
60
58
|
rexml
|
|
61
|
-
rubocop-ast (>=
|
|
59
|
+
rubocop-ast (>= 1.16.0, < 2.0)
|
|
62
60
|
ruby-progressbar (~> 1.7)
|
|
63
|
-
unicode-display_width (>= 1.4.0, <
|
|
64
|
-
rubocop-ast (1.
|
|
65
|
-
parser (>= 3.
|
|
66
|
-
rubocop-performance (1.
|
|
67
|
-
rubocop (>=
|
|
61
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
|
62
|
+
rubocop-ast (1.17.0)
|
|
63
|
+
parser (>= 3.1.1.0)
|
|
64
|
+
rubocop-performance (1.13.3)
|
|
65
|
+
rubocop (>= 1.7.0, < 2.0)
|
|
68
66
|
rubocop-ast (>= 0.4.0)
|
|
69
|
-
rubocop-rspec (
|
|
70
|
-
rubocop (~>
|
|
71
|
-
rubocop-ast (>= 0.7.1)
|
|
67
|
+
rubocop-rspec (2.9.0)
|
|
68
|
+
rubocop (~> 1.19)
|
|
72
69
|
ruby-progressbar (1.11.0)
|
|
73
70
|
simplecov (0.21.2)
|
|
74
71
|
docile (~> 1.1)
|
|
75
72
|
simplecov-html (~> 0.11)
|
|
76
73
|
simplecov_json_formatter (~> 0.1)
|
|
77
74
|
simplecov-html (0.12.3)
|
|
78
|
-
simplecov_json_formatter (0.1.
|
|
75
|
+
simplecov_json_formatter (0.1.4)
|
|
79
76
|
sqlite3 (1.4.2)
|
|
80
77
|
tzinfo (2.0.4)
|
|
81
78
|
concurrent-ruby (~> 1.0)
|
|
82
|
-
unicode-display_width (1.
|
|
83
|
-
zeitwerk (2.4
|
|
79
|
+
unicode-display_width (2.1.0)
|
|
80
|
+
zeitwerk (2.5.4)
|
|
84
81
|
|
|
85
82
|
PLATFORMS
|
|
86
83
|
ruby
|
|
87
84
|
|
|
88
85
|
DEPENDENCIES
|
|
89
86
|
activerecord (~> 6.0)
|
|
90
|
-
codecov (~> 0.
|
|
87
|
+
codecov (~> 0.6.0)
|
|
91
88
|
database_cleaner-active_record (~> 1.8)
|
|
92
89
|
light-services!
|
|
93
90
|
rake (~> 13.0)
|
|
94
|
-
rspec (~> 3.
|
|
95
|
-
rubocop (~>
|
|
96
|
-
rubocop-performance (~> 1.
|
|
97
|
-
rubocop-rspec (~>
|
|
98
|
-
simplecov (~> 0.
|
|
91
|
+
rspec (~> 3.11)
|
|
92
|
+
rubocop (~> 1.27)
|
|
93
|
+
rubocop-performance (~> 1.13)
|
|
94
|
+
rubocop-rspec (~> 2.9)
|
|
95
|
+
simplecov (~> 0.21)
|
|
99
96
|
sqlite3 (~> 1.4)
|
|
100
97
|
|
|
101
98
|
BUNDLED WITH
|
data/README.md
CHANGED
|
@@ -1,4 +1,328 @@
|
|
|
1
|
-
# Light Services
|
|
2
|
-

|
|
3
|
-
[](https://codecov.io/gh/light-ruby/light-services)
|
|
1
|
+
# 🚀 Light Services <sup>BETA</sup>
|
|
4
2
|
|
|
3
|
+
Implementation of Service Object pattern for Ruby/Rails applications.
|
|
4
|
+
|
|
5
|
+
## 👀 Table of Contents
|
|
6
|
+
1. [Simple Example](#simple-example)
|
|
7
|
+
2. [Usage](#usage)
|
|
8
|
+
1. [Arguments](#arguments)
|
|
9
|
+
2. [Steps](#steps)
|
|
10
|
+
3. [Outputs](#outputs)
|
|
11
|
+
4. [Context](#context)
|
|
12
|
+
3. [Complex Example](#complex-example)
|
|
13
|
+
4. [More Examples](#more-examples)
|
|
14
|
+
|
|
15
|
+
## 💪 Features
|
|
16
|
+
|
|
17
|
+
1. Ability to define `arguments`, `steps` and `outputs`
|
|
18
|
+
2. Isolated behaviour of each service object
|
|
19
|
+
3. Raising of errors to stop processing next steps
|
|
20
|
+
4. Wrapping actions into database transactions
|
|
21
|
+
5. Ability to pass context to child service object
|
|
22
|
+
6. Framework agnostic
|
|
23
|
+
7. 100% test coverage
|
|
24
|
+
|
|
25
|
+
## ❌ Problems
|
|
26
|
+
|
|
27
|
+
As this gem was just for internal usage, it has some problems:
|
|
28
|
+
|
|
29
|
+
1. Gem isn't documented well
|
|
30
|
+
2. Code doesn't have any comments
|
|
31
|
+
3. Repo doesn't have any CI/CD
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
Add this line to your application's Gemfile:
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
gem 'light-services', '~> 2.0.0.rc7'
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Simple Example
|
|
42
|
+
|
|
43
|
+
### Send notification
|
|
44
|
+
|
|
45
|
+
Let's create an elementary service object that sends a notification to the user.
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
class User::SendNotification < ApplicationService
|
|
49
|
+
# Arguments
|
|
50
|
+
arg :user, type: User
|
|
51
|
+
arg :text, type: :string
|
|
52
|
+
|
|
53
|
+
# Steps
|
|
54
|
+
step :validate_user
|
|
55
|
+
step :validate_text
|
|
56
|
+
step :send_notification
|
|
57
|
+
|
|
58
|
+
# Outputs
|
|
59
|
+
output :response
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def validate_user
|
|
64
|
+
return if user.active?
|
|
65
|
+
|
|
66
|
+
errors.add(:user, "isn't active")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def validate_text
|
|
70
|
+
return if text.present?
|
|
71
|
+
|
|
72
|
+
errors.add(:text, "must be present")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def send_notification
|
|
76
|
+
self.response = ExternalAPI.send_message(...)
|
|
77
|
+
rescue ExternalAPI::Error
|
|
78
|
+
errors.add(:base, "External API doesn't work")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Usage
|
|
84
|
+
|
|
85
|
+
### Arguments
|
|
86
|
+
|
|
87
|
+
You may send some arguments into the service object.
|
|
88
|
+
|
|
89
|
+
**How to define arguments:**
|
|
90
|
+
```ruby
|
|
91
|
+
class User::SendNotification < ApplicationService
|
|
92
|
+
# Required argument
|
|
93
|
+
arg :user, type: User
|
|
94
|
+
|
|
95
|
+
# Optional argument
|
|
96
|
+
arg :device, type: Device, optional: true
|
|
97
|
+
|
|
98
|
+
# Argument with default value
|
|
99
|
+
arg :text, type: :string, default: "Hello, how are you?"
|
|
100
|
+
|
|
101
|
+
# Argument with multiple allowed types
|
|
102
|
+
arg :retry, type: [TrueClass, FalseClass], default: false
|
|
103
|
+
|
|
104
|
+
# Argument which will be automatically passed into child components
|
|
105
|
+
arg :provider, type: Provider, context: true
|
|
106
|
+
end
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**How to pass arguments in controller:**
|
|
110
|
+
```ruby
|
|
111
|
+
class UsersController
|
|
112
|
+
def send_notification
|
|
113
|
+
service = User::SendNotification.run(user: User.first, provider: Provider.first)
|
|
114
|
+
# ...
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**How to pass arguments and context from parent to child service object:**
|
|
120
|
+
```ruby
|
|
121
|
+
class User::Update
|
|
122
|
+
# Arguments
|
|
123
|
+
arg :user, type: User, context: true
|
|
124
|
+
|
|
125
|
+
# Steps
|
|
126
|
+
# ...
|
|
127
|
+
step :send_notification
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
# ...
|
|
132
|
+
|
|
133
|
+
def send_notification
|
|
134
|
+
User::SendNotification
|
|
135
|
+
.with(self) # This line specifies the current service object as a parent and passes all context arguments into a child service object
|
|
136
|
+
.run(text: "Your profile was updated") # We don't need to pass `user` here as it's a context argument
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Steps
|
|
142
|
+
|
|
143
|
+
Steps are a bit more powerful than you think.
|
|
144
|
+
|
|
145
|
+
```ruby
|
|
146
|
+
class User::Charge
|
|
147
|
+
# Run step only when condition meets
|
|
148
|
+
step :create_payment_account, unless: :payment_account?
|
|
149
|
+
|
|
150
|
+
# Run step only when condition meets
|
|
151
|
+
step :charge_credit_card, if: :pay_with_credit_card?
|
|
152
|
+
|
|
153
|
+
# Run step after other step
|
|
154
|
+
step :update_payment_account, after: :create_payment_account
|
|
155
|
+
|
|
156
|
+
# Or before
|
|
157
|
+
step :save_information, before: :log_action
|
|
158
|
+
end
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Outputs
|
|
162
|
+
|
|
163
|
+
Outputs are pretty straightforward.
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
class User::Charge
|
|
167
|
+
# Simple output
|
|
168
|
+
output :payment
|
|
169
|
+
|
|
170
|
+
# Output with initial value
|
|
171
|
+
output :items, default: []
|
|
172
|
+
end
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Context
|
|
176
|
+
|
|
177
|
+
The context specifies the relationship between parent and child service objects.
|
|
178
|
+
|
|
179
|
+
What context does:
|
|
180
|
+
1. It tells the parent service object to pass context arguments into a child service object
|
|
181
|
+
2. When the child service object fails, it tells the parent service object to fail too (customizable)
|
|
182
|
+
|
|
183
|
+
```ruby
|
|
184
|
+
class User::Charge
|
|
185
|
+
# Arguments
|
|
186
|
+
arg :user, type: User, context: true
|
|
187
|
+
arg :cents, type: Integer
|
|
188
|
+
|
|
189
|
+
# ...
|
|
190
|
+
|
|
191
|
+
private
|
|
192
|
+
|
|
193
|
+
# ...
|
|
194
|
+
|
|
195
|
+
def send_notification
|
|
196
|
+
# Run service object w/o any context
|
|
197
|
+
User::SendNotification
|
|
198
|
+
.run(user: user, text: "...")
|
|
199
|
+
|
|
200
|
+
# Run service object and specify current one as a parent
|
|
201
|
+
User::SendNotification
|
|
202
|
+
.with(self)
|
|
203
|
+
.run(text: "...")
|
|
204
|
+
|
|
205
|
+
# Run service object with context but don't load errors from the child service object
|
|
206
|
+
service = User::SendNotification
|
|
207
|
+
.with(self, load_errors: false)
|
|
208
|
+
.run(text: "...")
|
|
209
|
+
|
|
210
|
+
if service.failed?
|
|
211
|
+
# That's ok. Process it somehow...
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Complex Example
|
|
218
|
+
|
|
219
|
+
### Creation of records
|
|
220
|
+
|
|
221
|
+
Let's investigate a more exciting example where we create a wrapper to create database records.
|
|
222
|
+
|
|
223
|
+
**Here is an example of controller (pretty thin, yeah? but we can make it even thinner):**
|
|
224
|
+
```ruby
|
|
225
|
+
class ContactsController < ApplicationController
|
|
226
|
+
# ...
|
|
227
|
+
|
|
228
|
+
def create
|
|
229
|
+
service = Contact::Create.run(service_args)
|
|
230
|
+
|
|
231
|
+
if service.success?
|
|
232
|
+
render locals: { contact: service.contact }, status: :ok
|
|
233
|
+
else
|
|
234
|
+
render "shared/errors", locals: { service: service }, status: :bad_request
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# ...
|
|
239
|
+
end
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Then, let's create a service object (no way, it couldn't be so simple):**
|
|
243
|
+
```ruby
|
|
244
|
+
class Contact::Create < CreateService
|
|
245
|
+
# We create alias just for a better readability
|
|
246
|
+
# so that we can call `service.contact` instead of `service.record`
|
|
247
|
+
alias contact record
|
|
248
|
+
|
|
249
|
+
private
|
|
250
|
+
|
|
251
|
+
def filtered_params
|
|
252
|
+
params.require(:contact).permit(:name, :phone)
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Let's check what logic we put into `CreateService`:**
|
|
258
|
+
```ruby
|
|
259
|
+
class CreateService < ApplicationService
|
|
260
|
+
# Arguments
|
|
261
|
+
arg :attributes, type: Hash, optional: true
|
|
262
|
+
|
|
263
|
+
# Outputs
|
|
264
|
+
output :record
|
|
265
|
+
|
|
266
|
+
# Steps
|
|
267
|
+
step :initialize_record
|
|
268
|
+
step :assign_attributes
|
|
269
|
+
step :authorize
|
|
270
|
+
step :validate
|
|
271
|
+
step :save_record
|
|
272
|
+
|
|
273
|
+
private
|
|
274
|
+
|
|
275
|
+
def initialize_record
|
|
276
|
+
self.record = self.class.module_parent.new
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def assign_attributes
|
|
280
|
+
record.assign_attributes(filtered_params)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def authorize
|
|
284
|
+
return if force || attributes
|
|
285
|
+
|
|
286
|
+
# Here is some Pundit logic 👇
|
|
287
|
+
authorize!(record, with_action: :create?)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def validate
|
|
291
|
+
return if record.valid?
|
|
292
|
+
|
|
293
|
+
errors.copy_from(record)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def save_record
|
|
297
|
+
record.save_with!(self)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def filtered_params
|
|
301
|
+
raise NotImplementedError
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Now we can easily reuse all this code and create as many services as we want:**
|
|
307
|
+
```ruby
|
|
308
|
+
class Team::Create < CreateService
|
|
309
|
+
alias team record
|
|
310
|
+
|
|
311
|
+
private
|
|
312
|
+
|
|
313
|
+
def filtered_params
|
|
314
|
+
params.require(:team).permit(:name)
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## More examples
|
|
320
|
+
|
|
321
|
+
You can find more examples here:
|
|
322
|
+
[https://github.com/light-ruby/light-services/tree/v2/spec/data/services](https://github.com/light-ruby/light-services/tree/v2/spec/data/services)
|
|
323
|
+
|
|
324
|
+
# Happy coding!
|
|
325
|
+
|
|
326
|
+
## License
|
|
327
|
+
|
|
328
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/lib/light/services/base.rb
CHANGED
|
@@ -102,7 +102,7 @@ module Light
|
|
|
102
102
|
|
|
103
103
|
def with(service_or_config = {}, config = {})
|
|
104
104
|
service = service_or_config.is_a?(Hash) ? nil : service_or_config
|
|
105
|
-
config =
|
|
105
|
+
config = service_or_config unless service
|
|
106
106
|
|
|
107
107
|
BaseWithContext.new(self, service, config)
|
|
108
108
|
end
|
|
@@ -180,9 +180,9 @@ module Light
|
|
|
180
180
|
log "🏎 Run service #{self.class}"
|
|
181
181
|
end
|
|
182
182
|
|
|
183
|
-
def within_transaction
|
|
183
|
+
def within_transaction(&block)
|
|
184
184
|
if @config[:use_transactions] && defined?(ActiveRecord::Base)
|
|
185
|
-
ActiveRecord::Base.transaction(requires_new: true)
|
|
185
|
+
ActiveRecord::Base.transaction(requires_new: true, &block)
|
|
186
186
|
else
|
|
187
187
|
yield
|
|
188
188
|
end
|
|
@@ -40,7 +40,11 @@ module Light
|
|
|
40
40
|
settings_collection.each do |name, settings|
|
|
41
41
|
next if !settings.default_exists || key?(name)
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
if settings.default.is_a?(Proc)
|
|
44
|
+
set(name, settings.default.call)
|
|
45
|
+
else
|
|
46
|
+
set(name, deep_dup(settings.default))
|
|
47
|
+
end
|
|
44
48
|
end
|
|
45
49
|
end
|
|
46
50
|
|
|
@@ -10,12 +10,12 @@ module Light
|
|
|
10
10
|
@messages = {}
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def add(key,
|
|
14
|
-
raise Light::Services::Error, "Error text can't be blank" if !
|
|
13
|
+
def add(key, texts, opts = {})
|
|
14
|
+
raise Light::Services::Error, "Error text can't be blank" if !texts || texts.blank?
|
|
15
15
|
|
|
16
16
|
message = nil
|
|
17
17
|
|
|
18
|
-
[*
|
|
18
|
+
[*texts].each do |text|
|
|
19
19
|
message = text.is_a?(Message) ? text : Message.new(key, text, opts)
|
|
20
20
|
|
|
21
21
|
@messages[key] ||= []
|
|
@@ -48,7 +48,7 @@ module Light
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def copy_to(entity)
|
|
51
|
-
if defined?(ActiveRecord::Base) && entity.is_a?(ActiveRecord::Base) || entity.is_a?(Light::Services::Base)
|
|
51
|
+
if (defined?(ActiveRecord::Base) && entity.is_a?(ActiveRecord::Base)) || entity.is_a?(Light::Services::Base)
|
|
52
52
|
each do |key, messages|
|
|
53
53
|
messages.each do |message|
|
|
54
54
|
entity.errors.add(key, message.to_s)
|
|
@@ -67,7 +67,7 @@ module Light
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def to_h
|
|
70
|
-
@messages.to_h.
|
|
70
|
+
@messages.to_h.transform_values { |value| value.map(&:to_s) }
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
def method_missing(method, *args, &block)
|
|
@@ -25,9 +25,10 @@ module Light
|
|
|
25
25
|
|
|
26
26
|
def valid_type?(value)
|
|
27
27
|
return if !@type || [*@type].any? do |type|
|
|
28
|
-
|
|
28
|
+
case type
|
|
29
|
+
when :boolean
|
|
29
30
|
value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
|
30
|
-
|
|
31
|
+
when Symbol
|
|
31
32
|
arg_type(value) == type
|
|
32
33
|
else
|
|
33
34
|
value.is_a?(type)
|
|
@@ -45,9 +46,9 @@ module Light
|
|
|
45
46
|
|
|
46
47
|
@arg_types_cache[klass] ||= klass
|
|
47
48
|
.name
|
|
48
|
-
.gsub(/::/,
|
|
49
|
-
.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
|
50
|
-
.gsub(/([a-z\d])([A-Z])/,'\1_\2')
|
|
49
|
+
.gsub(/::/, "/")
|
|
50
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
51
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
51
52
|
.tr("-", "_")
|
|
52
53
|
.downcase
|
|
53
54
|
.to_sym
|
data/light-services.gemspec
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: light-services
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.0.
|
|
4
|
+
version: 2.0.0.rc8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Emelianenko
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-
|
|
11
|
+
date: 2022-09-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Powerful implementation of Service Object pattern for Ruby and Rails
|
|
14
14
|
email:
|
|
@@ -56,6 +56,7 @@ metadata:
|
|
|
56
56
|
homepage_uri: https://github.com/light-ruby/light-services
|
|
57
57
|
source_code_uri: https://github.com/light-ruby/light-services
|
|
58
58
|
changelog_uri: https://github.com/light-ruby/light-services/blob/master/CHANGELOG.md
|
|
59
|
+
rubygems_mfa_required: 'true'
|
|
59
60
|
post_install_message:
|
|
60
61
|
rdoc_options: []
|
|
61
62
|
require_paths:
|