command_service_object 0.5.10 → 0.5.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +72 -22
- data/lib/command_service_object.rb +1 -1
- data/lib/command_service_object/helpers/controller_helper.rb +2 -0
- data/lib/command_service_object/hooks.rb +6 -16
- data/lib/command_service_object/version.rb +1 -1
- data/lib/generators/service/install/templates/services/application_service.rb +15 -13
- data/lib/generators/service/install/templates/services/case_base.rb +4 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71a2cbd62d96248cddea4dc3cfbdeb6f07d1d43b62582eb573ca3e689322af01
|
4
|
+
data.tar.gz: 333ecebd2230edf5c67967d4fcfd526a81e0f51d50111ca95f264c543ae12d14
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af30bd8432c0a1a045c152992e038381f64dc5cbd8882aff7bcc483ad8e5e86d24768df660c8502d2afeb2fad756f75edaeb9e12f28c67c40b604ff289906b42
|
7
|
+
data.tar.gz: dcace53060dc60e462025ca5dcb3d6ca93e440fc5f1ada54e587103ce18a6a35aa4dc50705a109d605c72212b8ec96600d1629edfd9d3cc39536568e0c147732
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,21 +2,27 @@
|
|
2
2
|
|
3
3
|
Rails Generator for command service object.
|
4
4
|
|
5
|
-
|
5
|
+
## Theory
|
6
|
+
|
6
7
|
[Command Design Pattern](https://en.wikipedia.org/wiki/Command_pattern) consists of `Command Object` and `Service Object` (Executor), Command object is responsible for containing `Client` requests and run input validations on it to ensure that the request is valid and set default values, then `Service Object` applies the business logic on that command.
|
7
8
|
|
8
|
-
### Implementation
|
9
|
-
|
9
|
+
### Implementation
|
10
|
+
|
11
|
+
Service consists of several objects { `Command Object` `Usecase Object` And `Error Object` (business logic error) }.
|
10
12
|
|
11
13
|
- **Command Object:** the object that responsible for containing `Client` requests and run input validations it's implemented using [Virtus](https://github.com/solnic/virtus) gem and can use `activerecord` for validations and it's existed under `commands` dir.
|
12
14
|
- **Usecase Object:** this object responsible for executing the business logic, Every `usecase` should execute one command type only so that command name should be the same as usecase object name, usecase object existed under 'usecases` dir.
|
13
|
-
- **
|
15
|
+
- **Micros:** small reusable logic under the same service.
|
16
|
+
|
17
|
+
#### Result Object
|
14
18
|
|
15
|
-
|
16
|
-
In case of successful or failure `ApplicationService` the responsible object for all services will return `service_result` object this object contain `value!` method containing successful call result, and `errors` method containing failure `errors` objects.
|
19
|
+
In case of successful or failure `ApplicationService` the responsible object for all services will return `service_result` object this object contain `value!` method containing successful call result, and `errors` method containing failure `errors` objects.
|
17
20
|
|
18
|
-
|
21
|
+
#### Business Logic Failures
|
19
22
|
|
23
|
+
To raise bussiness logic failures you can use `fail!` helper method with `message: String, extra_data: Hash` arguments.
|
24
|
+
|
25
|
+
> You can check if the result successful or not by using `ok?` method.
|
20
26
|
|
21
27
|
## Installation
|
22
28
|
|
@@ -25,49 +31,69 @@ Add this line to your application's Gemfile:
|
|
25
31
|
```ruby
|
26
32
|
gem 'command_service_object'
|
27
33
|
```
|
34
|
+
|
28
35
|
And then execute:
|
29
36
|
|
30
|
-
|
37
|
+
`bundle`
|
31
38
|
|
32
39
|
Or install it yourself as:
|
33
40
|
|
34
|
-
|
41
|
+
`gem install command_service_object`
|
35
42
|
|
36
43
|
Next, you need to run the generator:
|
37
44
|
|
38
|
-
|
39
|
-
$ rails generate service:install
|
40
|
-
```
|
45
|
+
`rails generate service:install`
|
41
46
|
|
42
47
|
## Usage
|
43
48
|
|
44
|
-
|
45
|
-
|
49
|
+
$ rails g service [service_name] [usecases usecases]
|
50
|
+
|
51
|
+
### Generate Service ex
|
46
52
|
|
47
|
-
|
53
|
+
$ rails g service auth login
|
48
54
|
output
|
49
55
|
|
50
56
|
```bash
|
51
57
|
app/services/
|
52
58
|
├── application_service.rb
|
59
|
+
├── external/
|
53
60
|
├── auth_service
|
54
61
|
│ ├── commands
|
55
62
|
│ │ └── login.rb
|
56
63
|
│ └── usecases
|
57
64
|
│ ├── login.rb
|
58
|
-
│ ├── login.rb
|
59
65
|
│ └── micros
|
60
|
-
│ └── user_profile_image.rb
|
61
66
|
├── case_base.rb
|
62
67
|
└── service_result.rb
|
63
68
|
```
|
64
69
|
|
65
|
-
### Generate micros ex
|
70
|
+
### Generate micros ex
|
66
71
|
|
67
|
-
|
72
|
+
$ rails g service:micro auth generate_jwt_token_for
|
73
|
+
|
74
|
+
```bash
|
75
|
+
app/services/
|
76
|
+
├── auth_service
|
77
|
+
│ └── usecases
|
78
|
+
│ └── micros
|
79
|
+
│ └── generate_jwt_token_for.rb
|
80
|
+
```
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
# app/services/auth_service/usecases/micros/generate_jwt_token_for.rb
|
84
|
+
|
85
|
+
module PaymentService::Usecases::Micros
|
86
|
+
class GenerateJwtTokenFor < CaseBase
|
87
|
+
def call
|
88
|
+
# <Payload>
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
```
|
68
93
|
|
69
94
|
then you can edit command params
|
70
95
|
> you can read [Virtus gem docs](https://github.com/solnic/virtus) for more info.
|
96
|
+
|
71
97
|
```ruby
|
72
98
|
# app/services/auth_service/commands/login.rb
|
73
99
|
# frozen_string_literal: true
|
@@ -87,7 +113,9 @@ module AuthService::Commands
|
|
87
113
|
end
|
88
114
|
end
|
89
115
|
```
|
116
|
+
|
90
117
|
and then add your business logic
|
118
|
+
|
91
119
|
```ruby
|
92
120
|
# app/services/auth_service/usecases/login.rb
|
93
121
|
# frozen_string_literal: true
|
@@ -95,14 +123,13 @@ and then add your business logic
|
|
95
123
|
module AuthService::Usecases
|
96
124
|
class Login < CaseBase
|
97
125
|
include CommandServiceObject::Hooks
|
98
|
-
micros :
|
126
|
+
micros :generate_jwt_token_for
|
99
127
|
#
|
100
128
|
# Your business logic goes here, keep [call] method clean by using private
|
101
129
|
# methods for Business logic.
|
102
130
|
#
|
103
131
|
def call
|
104
|
-
|
105
|
-
balance = user_balance # get user balance ex.
|
132
|
+
token = generate_jwt_token_for(user)
|
106
133
|
end
|
107
134
|
|
108
135
|
# This method will run if call method raise error
|
@@ -117,10 +144,33 @@ module AuthService::Usecases
|
|
117
144
|
end
|
118
145
|
end
|
119
146
|
end
|
147
|
+
```
|
120
148
|
|
149
|
+
### External APIs or Services
|
150
|
+
|
151
|
+
You can wrap external apis or services under `external/` dir
|
152
|
+
|
153
|
+
#### ex
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
module External
|
157
|
+
class StripeService
|
158
|
+
class << self
|
159
|
+
def charge(customer:, amount:, currency:, description: nil)
|
160
|
+
Stripe::Charge.create(
|
161
|
+
customer: customer.id,
|
162
|
+
amount: (round_up(amount, currency) * 100).to_i,
|
163
|
+
description: description || customer.email,
|
164
|
+
currency: currency
|
165
|
+
)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
121
170
|
```
|
122
171
|
|
123
172
|
usage from controller
|
173
|
+
|
124
174
|
```ruby
|
125
175
|
class AuthenticationController < ApplicationController
|
126
176
|
default_service :auth_service
|
@@ -5,7 +5,7 @@ require 'command_service_object/helpers/model_helper'
|
|
5
5
|
require 'command_service_object/helpers/controller_helper'
|
6
6
|
require 'command_service_object/helpers/failure_helper'
|
7
7
|
require 'command_service_object/hooks'
|
8
|
-
|
8
|
+
require 'virtus'
|
9
9
|
|
10
10
|
if defined?(Rails) && Rails::VERSION::STRING >= '3.0'
|
11
11
|
require 'command_service_object/railtie'
|
@@ -26,32 +26,22 @@ module CommandServiceObject
|
|
26
26
|
_called_micros.reverse_each(&:rollback)
|
27
27
|
end
|
28
28
|
|
29
|
-
def setup_getters(getters)
|
30
|
-
getters.each do |getter|
|
31
|
-
method_name = getter.name.split('::').last.underscore
|
32
|
-
|
33
|
-
define_singleton_method(method_name) do |_payload|
|
34
|
-
getter.new.call(args)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
29
|
def setup_micros(micros)
|
40
30
|
micros.each do |micro|
|
41
31
|
method_name = micro.name.split('::').last.underscore
|
42
32
|
|
43
33
|
# unrollable micros
|
44
|
-
define_singleton_method("#{method_name}!") do |
|
45
|
-
micro.new(
|
34
|
+
define_singleton_method("#{method_name}!") do |cmd|
|
35
|
+
micro.new(cmd).call
|
46
36
|
end
|
47
37
|
|
48
38
|
# rollable micros
|
49
|
-
define_singleton_method(method_name) do |
|
50
|
-
obj = micro.new(
|
51
|
-
obj.call
|
39
|
+
define_singleton_method(method_name) do |cmd|
|
40
|
+
obj = micro.new(cmd)
|
41
|
+
result = obj.call
|
52
42
|
|
53
43
|
_called_micros << obj
|
54
|
-
|
44
|
+
result
|
55
45
|
end
|
56
46
|
end
|
57
47
|
end
|
@@ -6,24 +6,26 @@ class ApplicationService
|
|
6
6
|
raise Errors::InvalidCommand, cmd.class if cmd.invalid?
|
7
7
|
|
8
8
|
usecase = usecase_for(cmd).new(cmd)
|
9
|
-
usecase.call
|
9
|
+
result = ServiceResult.new { usecase.call }
|
10
|
+
|
11
|
+
rollback(usecase, result, cmd) if result.error.present?
|
12
|
+
result
|
10
13
|
rescue StandardError => e
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
log_errors(e, cmd)
|
15
|
+
ServiceResult.new { raise e }
|
16
|
+
end
|
17
|
+
|
18
|
+
def rollback(usecase, result, cmd)
|
19
|
+
usecase.rollback_micros
|
20
|
+
usecase.rollback
|
21
|
+
log_errors(result.error, cmd)
|
17
22
|
end
|
18
23
|
|
19
|
-
def
|
20
|
-
|
21
|
-
return if failure.class.is_a?(CommandServiceObject::Failure)
|
22
|
-
#
|
24
|
+
def log_errors(err, _cmd)
|
25
|
+
return if err.class.is_a?(CommandServiceObject::Failure)
|
23
26
|
# Add your logging logic
|
24
27
|
# ex:
|
25
|
-
# Rollbar.error(
|
26
|
-
#
|
28
|
+
# Rollbar.error(err)
|
27
29
|
end
|
28
30
|
|
29
31
|
private
|
@@ -3,10 +3,11 @@
|
|
3
3
|
class CaseBase
|
4
4
|
include CommandServiceObject::FailureHelper
|
5
5
|
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :cmd
|
7
|
+
alias_attribute :payload, :cmd
|
7
8
|
|
8
|
-
def initialize(
|
9
|
-
@
|
9
|
+
def initialize(cmd)
|
10
|
+
@cmd = cmd
|
10
11
|
end
|
11
12
|
|
12
13
|
def rollback; end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: command_service_object
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adham EL-Deeb
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-
|
12
|
+
date: 2019-08-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|