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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7916d1e6a73deee6ce9dddf92550452365db224960d08d4171db0581bc67fde
4
- data.tar.gz: 57d614b6361e6643890c88730100fe33b490c109962d66e4dae241a985bf9e42
3
+ metadata.gz: 761226eff3ab278171ca2a9250b6fe089ceb53eeb2b1e221b680d250c3f1d2be
4
+ data.tar.gz: 567e4aa90fff412e1d42eba985a9e7a3f6397a53d622ec7e996503e22aa78a75
5
5
  SHA512:
6
- metadata.gz: 4e4807fa30e0d3b053dd769078c8d3938eb02ad7de3f14812fa3bf9a6442f38652a317bff66ac4765c273b1b29c24697ae71cc857040263de03df4361869f4cd
7
- data.tar.gz: '0488df45e2d0177e24db8f1f88077ccc94b1e8c91e7612a6176ad70f99802561fe0f774e61a73eaccaf0a0c1ce9cfaf4b6223a0cbe1a4743877305e389924e8f'
6
+ metadata.gz: 24c06c7384fca171053cd271ef96107ee36753015bd4198ed1b6f87e4ea5fe68db663d4a7a9e7b50af6cd8a03d71bce575ff3e17d8ffba2045442b55e8dafb19
7
+ data.tar.gz: 2889e28b5f8507849beaef07ce99fd886063a4cf40c91d035ad92190788d8322d956e0315aa94a85a067471671a36d634171e84a26d56c65692fd651c0b998de
data/README.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # ServiceRecord
2
2
 
3
+ [![Gem](https://img.shields.io/gem/v/service_record)](https://rubygems.org/gems/service_record)
3
4
  [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/uxxman/service_record/CI)](https://github.com/uxxman/service_record/actions?query=workflow%3ACI)
4
5
  [![Code Climate coverage](https://img.shields.io/codeclimate/coverage/uxxman/service_record)](https://codeclimate.com/github/uxxman/service_record)
5
6
  [![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/uxxman/service_record)](https://codeclimate.com/github/uxxman/service_record)
6
- [![Gem](https://img.shields.io/gem/v/service_record)](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' if params[:email].blank?
87
- errors << 'Email is invalid' if params[:email].present? && /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i.match?(params[:email])
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]).try(:authenticate, params[:password])
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
- return JsonWebToken.encode(user_id: user.id) if user.present?
123
-
124
- errors.add :authentication, 'invalid credentials'
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 test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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
 
@@ -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.run_callbacks :perform do
17
- service.result = service.perform
18
- service.result = nil if service.failure?
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
- attr_accessor :result
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
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ServiceRecord
4
+ # Defines before/around/after callbacks for the 'perform' method
2
5
  module Callbacks
3
6
  extend ActiveSupport::Concern
4
7
 
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ServiceRecord
4
+ # Exception to be raised when a service fails
2
5
  class Failure < StandardError
3
6
  attr_reader :service
4
7
 
@@ -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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ServiceRecord
2
- VERSION = '1.2.4'.freeze
4
+ VERSION = '1.4.0'
3
5
  end
@@ -1,2 +1,4 @@
1
- require 'active_model'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'service_record/base'
4
+ require 'service_record/version'
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.2.4
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: 2020-10-20 00:00:00.000000000 Z
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: '5.0'
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: '5.0'
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.0
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.1.2
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: []