single_action_service 0.3.0 → 0.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: 568daaaceb993e7f869cd2785cafc48d27c71faa16e11e971237fd3f483bc512
4
- data.tar.gz: 75f0112d564ba9da90a88e8bfacbbb3987c83c577d1e7472c4539b859d79b1aa
3
+ metadata.gz: 6457094da633a11d6323fc370acdb124e0af8a74fd873a3b21b97e77a36ba7ac
4
+ data.tar.gz: 466715ec68f4508c7fd67c353b862965379a14ae4dd7ce718b6de8addd5b855f
5
5
  SHA512:
6
- metadata.gz: aedab1f0637bc635e5824cbd5b906fdc26d5a4c66388c450e5c9030945fdb48e05e6d9803acfb1d1fefa6f6791b6d462e96d39202d1c0a922e9a4e018e939a4e
7
- data.tar.gz: b7bea6bd03dc96471c71472d57d3cf2c71ba72c1f3e7053899a982b1c43b09bb89b8d8abaae4c412dd1b3064b88a0c972488720050326c69eae80a21af6c7db8
6
+ metadata.gz: 8cf3e5df350aa221819badc9064c14467bc9d35dc6fa124306ebdf33ab461b2c769a59a4b0859eaf9f4bcf0a5105c21db931dd41f25556691f148c55f5378e8f
7
+ data.tar.gz: 7faebeb6bce78ff1e2d6aa601da89df2cfcfaf30d6b1251ec3ae878440bb7c694f624f34e43cfc53ab5f2e6583fb08bdebf1c7879e3e4e09fe7ed21d732a02bf
data/Gemfile.lock CHANGED
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- single_action_service (0.3.0)
4
+ single_action_service (0.4.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
9
  diff-lcs (1.3)
10
- rake (10.5.0)
10
+ rake (13.0.3)
11
11
  rspec (3.9.0)
12
12
  rspec-core (~> 3.9.0)
13
13
  rspec-expectations (~> 3.9.0)
@@ -26,10 +26,10 @@ PLATFORMS
26
26
  ruby
27
27
 
28
28
  DEPENDENCIES
29
- bundler (~> 1.17)
30
- rake (~> 10.0)
29
+ bundler (~> 2.1)
30
+ rake (~> 13.0)
31
31
  rspec (~> 3.0)
32
32
  single_action_service!
33
33
 
34
34
  BUNDLED WITH
35
- 1.17.3
35
+ 2.2.9
data/README.md CHANGED
@@ -22,70 +22,139 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- 1. Inherit:
25
+ Create an inheritor from a `SingleActionService::Base` with a single method named `call`
26
+
26
27
  ```ruby
27
- class MySingleAction < SingleActionService::Base
28
+ class Summator < SingleActionService::Base
29
+ def call
30
+ end
28
31
  end
29
32
  ```
30
- 2. Create a constructor with parameters or pass them to call:
33
+
34
+ Create a constructor with parameters
35
+
31
36
  ```ruby
32
- # Usually a data source is passed
33
- def initialize(x, y)
34
- @x = x
35
- @y = y
37
+ class Summator < SingleActionService::Base
38
+ def initialize(x, y)
39
+ @x = x
40
+ @y = y
41
+ end
36
42
  end
37
43
  ```
38
- or
44
+
45
+ or pass them to the call method
46
+
39
47
  ```ruby
40
- def call(x, y)
48
+ class Summator < SingleActionService::Base
49
+ def call(x, y)
50
+ end
41
51
  end
42
52
  ```
43
- 3. Perform the action:
44
- ```ruby
45
- sum = x + y
46
- ```
47
- 4. Return the result:
48
- ```ruby
49
- success(sum)
50
- ```
51
- or without data
53
+
54
+ Perform the action and return the result by calling `success(data = nil)`
55
+
52
56
  ```ruby
53
- success
57
+ class Summator < SingleActionService::Base
58
+ def call(x, y)
59
+ sum = x + y
60
+ success(sum)
61
+ end
62
+ end
54
63
  ```
55
- 5. Or return an error:
64
+
65
+ or return the error based on the validations by calling `error(data: nil, code: nil)`
66
+
56
67
  ```ruby
57
- return error(data: sum, code: 1) unless sum.positive?
68
+ class Summator < SingleActionService::Base
69
+ NIL_NUMBERS_ERROR = :nil_numbers_error
70
+
71
+ def call(x, y)
72
+ if x.nil? || y.nil?
73
+ return error(code: NIL_NUMBERS_ERROR)
74
+ end
75
+ end
76
+ end
58
77
  ```
59
- The 'data' parameter is any data (optional).
60
78
 
61
- The 'code' parameter is used to identify the error (optional).
79
+ Call the service and process a result
62
80
 
63
- 6. You can process the result received from the service as follows:
64
81
  ```ruby
65
- action = MySingleAction.new
66
- result = action.call(1,2)
67
- # or uses result.error?
82
+ summator = Summator.new
83
+ result = summator.call(1, 2)
84
+
68
85
  if result.success?
69
86
  result.data
70
87
  else
71
- {
72
- error_code: result.error_code,
73
- data: result.data
74
- }
88
+ result.error_code
75
89
  end
76
90
  ```
77
- or uses data! if you want to throw an exception
91
+
92
+ Or you can use a `data!` method if you want to throw an exception when an error has occurred:
93
+
78
94
  ```ruby
79
95
  begin
80
- result = MySingleAction.new.call(1,2).data!
96
+ result = Summator.new.call(1, 2).data!
81
97
  rescue SingleActionService::InvalidResult => e
82
- {
83
- error_code: e.result.error_code,
84
- data: e.result.data
85
- }
98
+ e.result.error_code
86
99
  end
87
100
  ```
88
101
 
102
+ You can define a list of errors of a service by calling a `self.errors(errors_data)` method.<br/>For each error, a method `#{error_name}_error(data = nil)` will be generated to instantiate the result with the appropriate code and optional data:
103
+
104
+ ```ruby
105
+ class Summator < SingleActionService::Base
106
+ errors [
107
+ { name: :nil_numbers, code: 1 }
108
+ ]
109
+
110
+ def call(x, y)
111
+ if x.nil? || y.nil?
112
+ return nil_numbers_error
113
+ end
114
+ end
115
+ end
116
+ ```
117
+
118
+ You can check for the specific error calling an autogenerated checking method of the result:
119
+
120
+ ```ruby
121
+ result = Summator.new.call(nil, nil)
122
+ result.nil_numbers_error? # true
123
+ ```
124
+
125
+ ## API Reference
126
+
127
+ ### SingleActionService::Base
128
+
129
+ A base class for services. Create an inheritor from him to use it. Methods:
130
+
131
+ Name |Description
132
+ ---------------------------------|-----------
133
+ `success(data = nil)` |Returns a successful `SingleActionService::Result` with<br/>data passed in arguments.
134
+ `error(data: nil, code: nil)` |Returns an error `SingleActionService::Result` with<br/> data and the error code passed in arguments.
135
+ `#{error_name}_error(data = nil)`|Autogenerated method to create an error result <br/>with the specific error code<br/>without having to pass it in arguments.<br/>Generated for each error passed to the `self.errors` method.<br/>Returns an error `SingleActionService::Result` with<br/> data passed to arguments.
136
+ `self.errors(errors_data)` |Call this method to identify possible service errors.<br/>Accepts an array of objects:<br/>`{ name: :error_name, code: :error_code }`
137
+
138
+ ### SingleActionService::Result
139
+
140
+ A base class for the result that the service returns. Instantiated by service methods such as `success`, `error` and autogenerated error methods. Methods:
141
+
142
+ Name |Description
143
+ ----------------------|-----------
144
+ `success?` |Call this method to check the result for success.<br/>Returns `true` for successful results created by the<br/>`success` method of a service.
145
+ `error?` |Call this method to check the result for error.<br/>Returns `false` for error results created by the<br/>`error` method of a service or by autogenerated<br/> error methods.
146
+ `#{error_name}_error?`|Call this method to check the result for the specific error.<br/>Autogenerated for each error passed to the `self.errors`<br/> of a service.
147
+ `data` |Returns data passed to the result instantiation method
148
+ `data!` |Returns data passed to the result instantiation method.<br/>Throws a `SingleActionService::InvalidResult`<br/>exception if the result contains an error.
149
+
150
+ ### SingleActionService::InvalidResult
151
+
152
+ An exception thrown by the `data!` method of a result. Methods:
153
+
154
+ Name |Description
155
+ ------------|-----------
156
+ `result` |Returns a `SingleActionService::Result`
157
+
89
158
  ## Contributing
90
159
 
91
160
  Bug reports and pull requests are welcome on GitHub at https://github.com/sequenia/single_action_service. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
@@ -1,7 +1,9 @@
1
- require "single_action_service/exceptions"
2
- require "single_action_service/base"
3
- require "single_action_service/result"
4
- require "single_action_service/version"
1
+ require 'single_action_service/exceptions'
2
+ require 'single_action_service/module_helper'
3
+ require 'single_action_service/base'
4
+ require 'single_action_service/result'
5
+ require 'single_action_service/service_error'
6
+ require 'single_action_service/version'
5
7
 
6
8
  module SingleActionService
7
9
  end
@@ -1,13 +1,100 @@
1
- module SingleActionService
2
- class Base
3
- protected
1
+ # Parent class for services.
2
+ # A service is an object that implements part of the business logic.
3
+ # Create an inheritor to use it
4
+ # and call 'success' or 'error' methods to return a result object.
5
+ class SingleActionService::Base
6
+ protected
4
7
 
5
- def success(data = nil)
6
- Result.new(true, data: data)
8
+ # Helper methods to setup the service
9
+ class << self
10
+ # Some usefull methods that exists in Rails
11
+ # but does not exists in the pure ruby
12
+ unless respond_to?(:module_parent)
13
+ include SingleActionService::ModuleHelper
7
14
  end
8
15
 
9
- def error(data: nil, code: nil)
10
- Result.new(false, data: data, error_code: code)
16
+ # Call this method to generate methods in the service to return
17
+ # specific errors.
18
+ #
19
+ # @param errors_data is an array of hashes with information about errors.
20
+ # Each hash can contain keys:
21
+ # :name => A symbol representing a name of the error.
22
+ # :code => A symbol representing an error code of the error.
23
+ #
24
+ # For each name, a method "#{name}_error" will be generated
25
+ # to return a result with the corresponding error code.
26
+ # The returned result will have "#{name}_error?" methods
27
+ # for checking for a specific error.
28
+ #
29
+ # For example, if you pass an array:
30
+ # [{ name: :already_exists, code: :'errors.already_exists' }],
31
+ # the 'already_exists_error' method will be generated to return the
32
+ # result with a :'errors.already_exists' code.
33
+ # You can check for the error by calling 'already_exists_error?'
34
+ # method on the result object.
35
+ def errors(errors_data = nil)
36
+ return @errors if errors_data.nil?
37
+
38
+ parse_errors(errors_data)
39
+ create_result_class
40
+ define_methods_to_create_error_results
41
+ end
42
+
43
+ def parse_errors(errors_data)
44
+ @errors = errors_data.map do |error_data|
45
+ SingleActionService::ServiceError.new(**error_data)
46
+ end
47
+ end
48
+
49
+ def create_result_class
50
+ demodulized_name = name.split('::').last
51
+ result_class_name = "#{demodulized_name}Result"
52
+ return if module_parent.const_defined?(result_class_name)
53
+
54
+ # Programmatically create the inheritor for the service result object
55
+ # with autogenerated methods for checking for errors.
56
+ errors = @errors
57
+ @result_class = Class.new(SingleActionService::Result) do
58
+ def self.define_error_checking_method(error)
59
+ method_name = "#{error.name}_error?"
60
+
61
+ define_method(method_name) do
62
+ error_code == error.code
63
+ end
64
+ end
65
+
66
+ errors.each do |error|
67
+ define_error_checking_method(error)
68
+ end
69
+ end
70
+
71
+ module_parent.const_set(result_class_name, @result_class)
11
72
  end
73
+
74
+ def define_methods_to_create_error_results
75
+ @errors.each do |error_object|
76
+ result_method_name = "#{error_object.name}_error"
77
+ define_method(result_method_name) do |data = nil|
78
+ error(code: error_object.code, data: data)
79
+ end
80
+ end
81
+ end
82
+
83
+ def result_class
84
+ @result_class ||= SingleActionService::Result
85
+ end
86
+ end
87
+
88
+ # @return a result with a success indicator and passed data.
89
+ # @param data is any data to return from service.
90
+ def success(data = nil)
91
+ self.class.result_class.new(true, data: data)
92
+ end
93
+
94
+ # @return a result with an error indicator, passed data and error code.
95
+ # @param data is any data to return from the service.
96
+ # @param code is an error code of the occurred error.
97
+ def error(data: nil, code: nil)
98
+ self.class.result_class.new(false, data: data, error_code: code)
12
99
  end
13
100
  end
@@ -7,6 +7,7 @@ module SingleActionService
7
7
  attr_reader :result
8
8
 
9
9
  def initialize(result)
10
+ super
10
11
  @result = result
11
12
  end
12
13
  end
@@ -0,0 +1,21 @@
1
+ module SingleActionService::ModuleHelper
2
+ def module_parent
3
+ module_parent_name ? constantize(module_parent_name) : Object
4
+ end
5
+
6
+ def constantize(string)
7
+ string.split('::').inject(Object) do |module_object, class_name|
8
+ module_object.const_get(class_name)
9
+ end
10
+ end
11
+
12
+ def module_parent_name
13
+ if defined?(@parent_name)
14
+ @parent_name
15
+ else
16
+ parent_name = name =~ /::[^:]+\z/ ? -$` : nil
17
+ @parent_name = parent_name unless frozen?
18
+ parent_name
19
+ end
20
+ end
21
+ end
@@ -1,25 +1,23 @@
1
- module SingleActionService
2
- class Result
3
- attr_accessor :status, :data, :error_code
1
+ class SingleActionService::Result
2
+ attr_accessor :status, :data, :error_code
4
3
 
5
- def initialize(status, data: nil, error_code: nil)
6
- @status = status
7
- @data = data
8
- @error_code = error_code
9
- end
4
+ def initialize(status, data: nil, error_code: nil)
5
+ @status = status
6
+ @data = data
7
+ @error_code = error_code
8
+ end
10
9
 
11
- def success?
12
- status
13
- end
10
+ def success?
11
+ status
12
+ end
14
13
 
15
- def error?
16
- !success?
17
- end
14
+ def error?
15
+ !success?
16
+ end
18
17
 
19
- def data!
20
- return data if success?
18
+ def data!
19
+ return data if success?
21
20
 
22
- raise InvalidResult.new(self)
23
- end
21
+ raise SingleActionService::InvalidResult, self
24
22
  end
25
23
  end
@@ -0,0 +1,8 @@
1
+ class SingleActionService::ServiceError
2
+ attr_accessor :code, :name
3
+
4
+ def initialize(code: nil, name: nil)
5
+ @code = code
6
+ @name = name
7
+ end
8
+ end
@@ -1,3 +1,3 @@
1
1
  module SingleActionService
2
- VERSION = "0.3.0"
2
+ VERSION = '0.4.0'.freeze
3
3
  end
@@ -1,29 +1,31 @@
1
-
2
- lib = File.expand_path("../lib", __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "single_action_service/version"
3
+ require 'single_action_service/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "single_action_service"
6
+ spec.name = 'single_action_service'
8
7
  spec.version = SingleActionService::VERSION
9
- spec.authors = ["Bazov Peter"]
10
- spec.email = ["petr@sequenia.com"]
8
+ spec.authors = ['Bazov Peter']
9
+ spec.email = ['petr@sequenia.com']
11
10
 
12
- spec.summary = %q{Single Action Service}
13
- spec.description = %q{A Ruby library to organize the code}
14
- spec.homepage = "http://sequenia.com/"
15
- spec.license = "MIT"
11
+ spec.summary = 'Single Action Service'
12
+ spec.description = 'A Ruby library to organize the code'
13
+ spec.homepage = 'https://github.com/sequenia/SingleActionService'
14
+ spec.license = 'MIT'
16
15
 
17
16
  # Specify which files should be added to the gem when it is released.
18
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ # The `git ls-files -z` loads the files in the RubyGem
18
+ # that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
20
+ `git ls-files -z`
21
+ .split("\x0")
22
+ .reject { |f| f.match(%r{^(test|spec|features)/}) }
21
23
  end
22
- spec.bindir = "exe"
24
+ spec.bindir = 'exe'
23
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
- spec.require_paths = ["lib"]
26
+ spec.require_paths = ['lib']
25
27
 
26
- spec.add_development_dependency "bundler", "~> 1.17"
27
- spec.add_development_dependency "rake", "~> 10.0"
28
- spec.add_development_dependency "rspec", "~> 3.0"
28
+ spec.add_development_dependency 'bundler', '~> 2.1'
29
+ spec.add_development_dependency 'rake', '~> 13.0'
30
+ spec.add_development_dependency 'rspec', '~> 3.0'
29
31
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: single_action_service
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bazov Peter
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-16 00:00:00.000000000 Z
11
+ date: 2021-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.17'
19
+ version: '2.1'
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: '1.17'
26
+ version: '2.1'
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: '10.0'
33
+ version: '13.0'
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: '10.0'
40
+ version: '13.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -73,10 +73,12 @@ files:
73
73
  - lib/single_action_service.rb
74
74
  - lib/single_action_service/base.rb
75
75
  - lib/single_action_service/exceptions.rb
76
+ - lib/single_action_service/module_helper.rb
76
77
  - lib/single_action_service/result.rb
78
+ - lib/single_action_service/service_error.rb
77
79
  - lib/single_action_service/version.rb
78
80
  - single_action_service.gemspec
79
- homepage: http://sequenia.com/
81
+ homepage: https://github.com/sequenia/SingleActionService
80
82
  licenses:
81
83
  - MIT
82
84
  metadata: {}
@@ -95,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
97
  - !ruby/object:Gem::Version
96
98
  version: '0'
97
99
  requirements: []
98
- rubygems_version: 3.0.4
100
+ rubygems_version: 3.2.3
99
101
  signing_key:
100
102
  specification_version: 4
101
103
  summary: Single Action Service