service_record 1.2.4 → 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/README.md +14 -12
- data/lib/rails/generators/service/service_generator.rb +7 -0
- data/lib/service_record/base.rb +21 -14
- data/lib/service_record/callbacks.rb +3 -0
- data/lib/service_record/failure.rb +3 -0
- data/lib/service_record/response.rb +23 -0
- data/lib/service_record/version.rb +3 -1
- data/lib/service_record.rb +3 -1
- metadata +155 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 761226eff3ab278171ca2a9250b6fe089ceb53eeb2b1e221b680d250c3f1d2be
|
4
|
+
data.tar.gz: 567e4aa90fff412e1d42eba985a9e7a3f6397a53d622ec7e996503e22aa78a75
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24c06c7384fca171053cd271ef96107ee36753015bd4198ed1b6f87e4ea5fe68db663d4a7a9e7b50af6cd8a03d71bce575ff3e17d8ffba2045442b55e8dafb19
|
7
|
+
data.tar.gz: 2889e28b5f8507849beaef07ce99fd886063a4cf40c91d035ad92190788d8322d956e0315aa94a85a067471671a36d634171e84a26d56c65692fd651c0b998de
|
data/README.md
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# ServiceRecord
|
2
2
|
|
3
|
+
[](https://rubygems.org/gems/service_record)
|
3
4
|
[](https://github.com/uxxman/service_record/actions?query=workflow%3ACI)
|
4
5
|
[](https://codeclimate.com/github/uxxman/service_record)
|
5
6
|
[](https://codeclimate.com/github/uxxman/service_record)
|
6
|
-
[](https://rubygems.org/gems/service_record)
|
7
7
|
|
8
8
|
An ActiveRecord lookalike but for business model requirements, a.k.a Service Objects.
|
9
9
|
|
10
10
|
Rails is packed with amazing tools to get you started with building your new awesome project and enforces reliable and battle-tested guidelines. One of those guideline is "**thin controllers and fat models**", but sometimes (actually most of the time) its difficult to follow because most business requirements are not that simple like most CRUD operations.
|
11
11
|
|
12
|
-
Enters, ServiceRecord. Its similar to ActiveRecord models but their sole purpose is to perform a big/complex/muilt-step task without bloating the controllers or models.
|
12
|
+
Enters, ServiceRecord, a tiny wrapper around basic goodies included in Rails. Its similar to ActiveRecord models but their sole purpose is to perform a big/complex/muilt-step task without bloating the controllers or models.
|
13
13
|
|
14
14
|
## Installation
|
15
15
|
|
@@ -66,9 +66,7 @@ The returned response from a service will have the following useful attributes/m
|
|
66
66
|
* `result` contains returned value of service perform function
|
67
67
|
* `errors` contains details about issues that occurr while performing the service
|
68
68
|
|
69
|
-
There is a **perform!** (with a bang !) method which will raise **ServiceRecord::Failure** in case of service failure.
|
70
|
-
|
71
|
-
|
69
|
+
There is also a **perform!** (with a bang !) method which will raise **ServiceRecord::Failure** in case of service failure.
|
72
70
|
|
73
71
|
## Example
|
74
72
|
|
@@ -83,12 +81,12 @@ def sign_in
|
|
83
81
|
errors = []
|
84
82
|
|
85
83
|
# Basic validation
|
86
|
-
errors << 'Email is required'
|
87
|
-
errors << 'Email is invalid'
|
84
|
+
errors << 'Email is required' if params[:email].blank?
|
85
|
+
errors << 'Email is invalid' if params[:email].present? && /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i.match?(params[:email])
|
88
86
|
errors << 'Password is required' if params[:password].blank?
|
89
87
|
|
90
88
|
if errors.size == 0
|
91
|
-
user = User.find_by(email: params[:email])
|
89
|
+
user = User.find_by(email: params[:email])&.try(:authenticate, params[:password])
|
92
90
|
|
93
91
|
if user.present?
|
94
92
|
token = JsonWebToken.encode(user_id: user.id)
|
@@ -119,9 +117,11 @@ class AuthenticateUser < ApplicationService
|
|
119
117
|
def perform
|
120
118
|
user = User.find_by(email: email).try(:authenticate, password)
|
121
119
|
|
122
|
-
|
123
|
-
|
124
|
-
|
120
|
+
if user.present?
|
121
|
+
JsonWebToken.encode(user_id: user.id)
|
122
|
+
else
|
123
|
+
errors.add :authentication, 'invalid credentials'
|
124
|
+
end
|
125
125
|
end
|
126
126
|
end
|
127
127
|
|
@@ -174,7 +174,9 @@ Availble callbacks are `before_perform`, `after_perform` and `around_perform`. I
|
|
174
174
|
|
175
175
|
## Development
|
176
176
|
|
177
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake
|
177
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
178
|
+
|
179
|
+
ServiceRecord uses appraisals to test the code base against multiple versions of Rails ActiveModel. When first developing, you need to run `bundle install` and then `bundle exec appraisal install`, to install the different gem sets. You can then run all appraisal files (like CI does), with `appraisal rake`.
|
178
180
|
|
179
181
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
180
182
|
|
@@ -1,7 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rails/generators/named_base'
|
2
4
|
|
3
5
|
module Rails
|
4
6
|
module Generators
|
7
|
+
# Create AppicationService and SubService classes using Rails generators.
|
8
|
+
#
|
9
|
+
# E.g:
|
10
|
+
# bin/rails service my_service
|
11
|
+
#
|
5
12
|
class ServiceGenerator < Rails::Generators::NamedBase
|
6
13
|
desc 'This generator creates a service file at app/services'
|
7
14
|
|
data/lib/service_record/base.rb
CHANGED
@@ -1,25 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model'
|
1
4
|
require 'service_record/failure'
|
5
|
+
require 'service_record/response'
|
2
6
|
require 'service_record/callbacks'
|
3
7
|
|
4
8
|
module ServiceRecord
|
9
|
+
# Base class to be extended by all service classes
|
10
|
+
#
|
11
|
+
# class MyService < ServiceRecord
|
12
|
+
# end
|
13
|
+
#
|
5
14
|
class Base
|
6
15
|
include Callbacks
|
7
16
|
include ActiveModel::Attributes
|
8
17
|
include ActiveModel::Validations
|
9
18
|
include ActiveModel::AttributeAssignment
|
10
19
|
|
20
|
+
attr_accessor :result
|
21
|
+
|
22
|
+
# Wrapper around the *perform* instance method that runs all the validations
|
23
|
+
# and callbacks before eventually calling *perform*.
|
11
24
|
def self.perform(args = {})
|
12
25
|
new.tap do |service|
|
13
26
|
service.attributes = args
|
14
|
-
break service unless service.valid?
|
15
27
|
|
16
|
-
service.
|
17
|
-
service.
|
18
|
-
|
28
|
+
if service.valid?
|
29
|
+
service.run_callbacks :perform do
|
30
|
+
service.result = service.perform
|
31
|
+
end
|
19
32
|
end
|
33
|
+
|
34
|
+
return Response.new(service.result, service.errors)
|
20
35
|
end
|
21
36
|
end
|
22
37
|
|
38
|
+
# Wapper around the *perform* class method that raises exception if service fails
|
23
39
|
def self.perform!(args = {})
|
24
40
|
service = perform(args)
|
25
41
|
return service if service.success?
|
@@ -27,16 +43,7 @@ module ServiceRecord
|
|
27
43
|
raise Failure, service
|
28
44
|
end
|
29
45
|
|
30
|
-
|
31
|
-
|
32
|
-
def success?
|
33
|
-
errors.empty?
|
34
|
-
end
|
35
|
-
|
36
|
-
def failure?
|
37
|
-
!success?
|
38
|
-
end
|
39
|
-
|
46
|
+
# Each subclass must define the *perform* method
|
40
47
|
def perform
|
41
48
|
raise NotImplementedError
|
42
49
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ServiceRecord
|
4
|
+
# Response to be returned by a service when it finishes
|
5
|
+
class Response
|
6
|
+
attr_reader :result, :errors
|
7
|
+
|
8
|
+
def initialize(result, errors)
|
9
|
+
@result = result
|
10
|
+
@errors = errors
|
11
|
+
end
|
12
|
+
|
13
|
+
# Checks for errors. Returns +true+ if no errors are found, +false+ otherwise.
|
14
|
+
def success?
|
15
|
+
errors.empty?
|
16
|
+
end
|
17
|
+
|
18
|
+
# Checks for errors. Returns +false+ if no errors are found, +true+ otherwise.
|
19
|
+
def failure?
|
20
|
+
!success?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/service_record.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: service_record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Muhammad Usman
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-09-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -16,14 +16,158 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '6.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '6.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: appraisal
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.4'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '13.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '13.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.9'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.9'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop-packaging
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.5.2
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.5.2
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop-performance
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.14'
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 1.14.3
|
93
|
+
type: :development
|
94
|
+
prerelease: false
|
95
|
+
version_requirements: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - "~>"
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '1.14'
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 1.14.3
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: rubocop-rails
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '2.15'
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 2.15.2
|
113
|
+
type: :development
|
114
|
+
prerelease: false
|
115
|
+
version_requirements: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - "~>"
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '2.15'
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: 2.15.2
|
123
|
+
- !ruby/object:Gem::Dependency
|
124
|
+
name: rubocop-rake
|
125
|
+
requirement: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: 0.6.0
|
130
|
+
type: :development
|
131
|
+
prerelease: false
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - "~>"
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: 0.6.0
|
137
|
+
- !ruby/object:Gem::Dependency
|
138
|
+
name: rubocop-rspec
|
139
|
+
requirement: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - "~>"
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '2.12'
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: 2.12.1
|
147
|
+
type: :development
|
148
|
+
prerelease: false
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - "~>"
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '2.12'
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: 2.12.1
|
157
|
+
- !ruby/object:Gem::Dependency
|
158
|
+
name: simplecov
|
159
|
+
requirement: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - "~>"
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: 0.17.1
|
164
|
+
type: :development
|
165
|
+
prerelease: false
|
166
|
+
version_requirements: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - "~>"
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: 0.17.1
|
27
171
|
description: ActiveRecord lookalike but for business model requirements
|
28
172
|
email:
|
29
173
|
- uxman.sherwani@gmail.com
|
@@ -42,6 +186,7 @@ files:
|
|
42
186
|
- lib/service_record/base.rb
|
43
187
|
- lib/service_record/callbacks.rb
|
44
188
|
- lib/service_record/failure.rb
|
189
|
+
- lib/service_record/response.rb
|
45
190
|
- lib/service_record/version.rb
|
46
191
|
homepage: https://github.com/uxxman/service_record
|
47
192
|
licenses:
|
@@ -49,8 +194,9 @@ licenses:
|
|
49
194
|
metadata:
|
50
195
|
homepage_uri: https://github.com/uxxman/service_record
|
51
196
|
source_code_uri: https://github.com/uxxman/service_record
|
197
|
+
rubygems_mfa_required: 'true'
|
52
198
|
changelog_uri: https://github.com/uxxman/service_record/releases
|
53
|
-
post_install_message:
|
199
|
+
post_install_message:
|
54
200
|
rdoc_options: []
|
55
201
|
require_paths:
|
56
202
|
- lib
|
@@ -58,15 +204,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
58
204
|
requirements:
|
59
205
|
- - ">="
|
60
206
|
- !ruby/object:Gem::Version
|
61
|
-
version: 2.5
|
207
|
+
version: 2.7.5
|
62
208
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
209
|
requirements:
|
64
210
|
- - ">="
|
65
211
|
- !ruby/object:Gem::Version
|
66
212
|
version: '0'
|
67
213
|
requirements: []
|
68
|
-
rubygems_version: 3.
|
69
|
-
signing_key:
|
214
|
+
rubygems_version: 3.3.7
|
215
|
+
signing_key:
|
70
216
|
specification_version: 4
|
71
217
|
summary: Service objects for rails
|
72
218
|
test_files: []
|