services_base 0.1.0 → 1.0.1
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 +5 -5
- data/.gitignore +2 -0
- data/Guardfile +0 -28
- data/README.md +46 -7
- data/lib/services/base.rb +21 -0
- data/lib/services/responses/base.rb +23 -0
- data/lib/services/responses/configuration.rb +19 -0
- data/lib/services/responses/error.rb +37 -0
- data/lib/services/responses/success.rb +9 -0
- data/lib/services/version.rb +3 -0
- data/lib/services_base.rb +6 -22
- data/services_base.gemspec +6 -6
- metadata +20 -16
- data/lib/services_base/version.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e08cc56780595fc4e0b65496e1c7390c38a750a9ed0ca0b6909e0bf42446d5bc
|
4
|
+
data.tar.gz: 80d6012ad972450cd9a18ed08dc9d299a5db68a1b9c640d2d851fae311b4233e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e5423dd27046562d40eedb65aa57895c36d5ed4a39fdd008361a07738af818cc93e94c8073ea23c6592322e0f5edf620d0292a2635d5fcd0082a634cd182dac2
|
7
|
+
data.tar.gz: 7f893261a0dcbff1518a0043e6c5b0085d0a9f8e24bcb91f3553ef9b20a2c136932486275aadab14b383ad93dabc3e2bfb76e41f3f81178749a28747cc537488
|
data/.gitignore
CHANGED
data/Guardfile
CHANGED
@@ -11,32 +11,4 @@ guard :rspec, cmd: "bundle exec rspec" do
|
|
11
11
|
# Ruby files
|
12
12
|
ruby = dsl.ruby
|
13
13
|
dsl.watch_spec_files_for(ruby.lib_files)
|
14
|
-
|
15
|
-
# # Rails files
|
16
|
-
# rails = dsl.rails(view_extensions: %w(erb haml slim))
|
17
|
-
# dsl.watch_spec_files_for(rails.app_files)
|
18
|
-
# dsl.watch_spec_files_for(rails.views)
|
19
|
-
|
20
|
-
# watch(rails.controllers) do |m|
|
21
|
-
# [
|
22
|
-
# rspec.spec.call("routing/#{m[1]}_routing"),
|
23
|
-
# rspec.spec.call("controllers/#{m[1]}_controller"),
|
24
|
-
# rspec.spec.call("acceptance/#{m[1]}")
|
25
|
-
# ]
|
26
|
-
# end
|
27
|
-
|
28
|
-
# # Rails config changes
|
29
|
-
# watch(rails.spec_helper) { rspec.spec_dir }
|
30
|
-
# watch(rails.routes) { "#{rspec.spec_dir}/routing" }
|
31
|
-
# watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
|
32
|
-
|
33
|
-
# # Capybara features specs
|
34
|
-
# watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") }
|
35
|
-
# watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") }
|
36
|
-
|
37
|
-
# # Turnip features and steps
|
38
|
-
# watch(%r{^spec/acceptance/(.+)\.feature$})
|
39
|
-
# watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
|
40
|
-
# Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
|
41
|
-
# end
|
42
14
|
end
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# ServicesBase
|
2
2
|
|
3
|
-
ServicesBase is a very simple
|
3
|
+
ServicesBase is a very simple skeleton for implementing Service Objects and Micro-Services in Ruby.
|
4
|
+
|
5
|
+
It implements a Base module for your service objects (`Services::Base`) and two Response classes (`Services::Responses::Success` and `Services::Responses::Error`).
|
4
6
|
|
5
7
|
It was heavily inspired by Chris Holtz's excellent article [Organize your app with service objects](http://chrisholtz.com/blog/organize-your-app-with-service-objects/). You should read it.
|
6
8
|
|
@@ -21,15 +23,52 @@ Or install it yourself as:
|
|
21
23
|
$ gem install services_base
|
22
24
|
|
23
25
|
## Usage
|
26
|
+
```ruby
|
27
|
+
class Services::MyService
|
28
|
+
include Services::Base
|
29
|
+
|
30
|
+
def call(id)
|
31
|
+
# do something
|
32
|
+
result_object = { foo: 'bar' }
|
33
|
+
|
34
|
+
# and return success
|
35
|
+
return Services::Responses::Success.new(result_object)
|
36
|
+
|
37
|
+
# or if there's an error:
|
38
|
+
return Services::Responses::Error.new(result_object, error_message: 'Something went wrong', error_code: 123)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
response = Services::MyService.call(123)
|
43
|
+
response.is_a?(Services::Responses::Success) # true when it's a success
|
44
|
+
response.is_a?(Services::Responses::Error) # true when it's an error
|
45
|
+
response.is_a?(Services::Responses::Base) # always true
|
46
|
+
|
47
|
+
response.success? # true/false
|
48
|
+
response.result # the resulting object
|
49
|
+
response.error_code # available when there's an error
|
50
|
+
response.error_message # available when there's an error
|
51
|
+
```
|
52
|
+
|
53
|
+
## Configuration
|
54
|
+
|
55
|
+
When running a service asynchronously (ie: in Sidekiq or Delayed Job), it may be desirable to raise an exception instead of returning a `Services::Responses::Error` in order to retry the job at a later time. It is easy to do so:
|
24
56
|
|
25
|
-
|
26
|
-
|
57
|
+
```ruby
|
58
|
+
Services::Responses.configure do |config|
|
59
|
+
config.raise_exception_on_async_error = true
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
When `raise_exception_on_async_error` is set to `true`, an exception will be thrown whenever a new
|
64
|
+
`Services::Responses::Error` is initialized during an asynchronous job.
|
27
65
|
|
28
|
-
|
29
|
-
# do something
|
30
|
-
end
|
31
|
-
end
|
66
|
+
Alternatively, you can raise an exception only for a specific `Error` object:
|
32
67
|
|
68
|
+
```ruby
|
69
|
+
# will raise an exception ONLY if it's called from an async job.
|
70
|
+
Services::Responses::Error.new(my_exception, raise_exception_on_async_error: true)
|
71
|
+
```
|
33
72
|
|
34
73
|
## License
|
35
74
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Significantly inspired by http://chrisholtz.com/blog/organize-your-app-with-service-objects/
|
2
|
+
module Services
|
3
|
+
module Base
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
base.include InstanceMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def call(*args, &block)
|
11
|
+
self.new.call *args, &block
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module InstanceMethods
|
16
|
+
def call(*args, &block)
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Services
|
2
|
+
module Responses
|
3
|
+
class Base
|
4
|
+
attr_reader :result
|
5
|
+
|
6
|
+
def initialize(result = nil)
|
7
|
+
@result = result
|
8
|
+
end
|
9
|
+
|
10
|
+
def success?
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
def error?
|
15
|
+
!success?
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.async_environment?
|
19
|
+
(defined?(Sidekiq) && !!Sidekiq.server?) || (ENV["_"] || "").include?("delayed_job") || (ENV["_"] || "").include?("resque")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Services::Responses
|
2
|
+
class << self
|
3
|
+
attr_accessor :configuration
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.configure
|
7
|
+
yield(configuration)
|
8
|
+
end
|
9
|
+
|
10
|
+
class Configuration
|
11
|
+
attr_accessor :raise_exception_on_async_error
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@raise_exception_on_async_error = false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Services::Responses.configuration ||= Services::Responses::Configuration.new
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Services
|
2
|
+
module Responses
|
3
|
+
class Error < Services::Responses::Base
|
4
|
+
attr_accessor :error_message, :error_code, :exception
|
5
|
+
|
6
|
+
def initialize(result, error_message: nil, error_code: nil, exception: nil, raise_exception_on_async_error: nil)
|
7
|
+
@result, @error_message, @error_code, @exception, @raise_exception_on_async_error =
|
8
|
+
result, error_message, error_code, exception, raise_exception_on_async_error
|
9
|
+
|
10
|
+
raise_if_async!
|
11
|
+
end
|
12
|
+
|
13
|
+
def success?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
def raise_if_async!
|
19
|
+
return unless should_raise?
|
20
|
+
|
21
|
+
if @result.is_a?(Exception)
|
22
|
+
raise @result
|
23
|
+
elsif @exception
|
24
|
+
raise @exception
|
25
|
+
else
|
26
|
+
err = ("#{@error_message} " + "[#{@error_code}]".gsub("[]", "")).strip
|
27
|
+
raise StandardError.new(err)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def should_raise?
|
32
|
+
(Services::Responses.configuration.raise_exception_on_async_error || @raise_exception_on_async_error) \
|
33
|
+
&& Services::Responses::Base.async_environment?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/services_base.rb
CHANGED
@@ -1,22 +1,6 @@
|
|
1
|
-
require "
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
base.include InstanceMethods
|
8
|
-
end
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
def call(*args, &block)
|
12
|
-
@instance ||= self.new
|
13
|
-
@instance.call *args, &block
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
module InstanceMethods
|
18
|
-
def call(*args, &block)
|
19
|
-
raise NotImplementedError
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
1
|
+
require "services/version"
|
2
|
+
require "services/base"
|
3
|
+
require "services/responses/base"
|
4
|
+
require "services/responses/error"
|
5
|
+
require "services/responses/success"
|
6
|
+
require "services/responses/configuration"
|
data/services_base.gemspec
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require '
|
4
|
+
require 'services/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "services_base"
|
8
|
-
spec.version =
|
8
|
+
spec.version = Services::VERSION
|
9
9
|
spec.authors = ["Carl Mercier"]
|
10
10
|
spec.email = ["carl@varagesale.com"]
|
11
11
|
|
12
|
-
spec.summary = %q{Simple
|
13
|
-
spec.description = %q{Simple
|
12
|
+
spec.summary = %q{Simple skeleton for implementing consistent Service Objects and Micro-Services in Ruby}
|
13
|
+
spec.description = %q{Simple skeleton for implementing consistent Service Objects and Micro-Services in Ruby}
|
14
14
|
spec.homepage = "https://github.com/varagesale/services_base"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
@@ -19,8 +19,8 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_development_dependency "bundler"
|
23
|
-
spec.add_development_dependency "rake", "~>
|
22
|
+
spec.add_development_dependency "bundler"
|
23
|
+
spec.add_development_dependency "rake", "~> 12.3.3"
|
24
24
|
spec.add_development_dependency "rspec"
|
25
25
|
spec.add_development_dependency "guard"
|
26
26
|
spec.add_development_dependency "guard-rspec"
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: services_base
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carl Mercier
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-12-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :development
|
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: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 12.3.3
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 12.3.3
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,7 +80,7 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
description: Simple
|
83
|
+
description: Simple skeleton for implementing consistent Service Objects and Micro-Services
|
84
84
|
in Ruby
|
85
85
|
email:
|
86
86
|
- carl@varagesale.com
|
@@ -98,14 +98,19 @@ files:
|
|
98
98
|
- Rakefile
|
99
99
|
- bin/console
|
100
100
|
- bin/setup
|
101
|
+
- lib/services/base.rb
|
102
|
+
- lib/services/responses/base.rb
|
103
|
+
- lib/services/responses/configuration.rb
|
104
|
+
- lib/services/responses/error.rb
|
105
|
+
- lib/services/responses/success.rb
|
106
|
+
- lib/services/version.rb
|
101
107
|
- lib/services_base.rb
|
102
|
-
- lib/services_base/version.rb
|
103
108
|
- services_base.gemspec
|
104
109
|
homepage: https://github.com/varagesale/services_base
|
105
110
|
licenses:
|
106
111
|
- MIT
|
107
112
|
metadata: {}
|
108
|
-
post_install_message:
|
113
|
+
post_install_message:
|
109
114
|
rdoc_options: []
|
110
115
|
require_paths:
|
111
116
|
- lib
|
@@ -120,10 +125,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
125
|
- !ruby/object:Gem::Version
|
121
126
|
version: '0'
|
122
127
|
requirements: []
|
123
|
-
|
124
|
-
|
125
|
-
signing_key:
|
128
|
+
rubygems_version: 3.1.4
|
129
|
+
signing_key:
|
126
130
|
specification_version: 4
|
127
|
-
summary: Simple
|
131
|
+
summary: Simple skeleton for implementing consistent Service Objects and Micro-Services
|
128
132
|
in Ruby
|
129
133
|
test_files: []
|