command_service_object 0.6.5 → 1.0.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/Gemfile.lock +14 -1
- data/README.md +37 -19
- data/command_service_object.gemspec +1 -0
- data/lib/command_service_object/version.rb +1 -1
- data/lib/generators/.DS_Store +0 -0
- data/lib/generators/service/command/command_generator.rb +13 -2
- data/lib/generators/service/{test/templates/rspec/command.rb.erb → command/templates/command_spec.rb.erb} +0 -0
- data/lib/generators/service/entity/entity_generator.rb +30 -0
- data/lib/generators/service/entity/templates/entity.rb.erb +9 -0
- data/lib/generators/service/external/external_generator.rb +37 -0
- data/lib/generators/service/external/templates/external.rb.erb +16 -0
- data/lib/generators/service/external/templates/external_spec.rb.erb +12 -0
- data/lib/generators/service/external/templates/external_test.rb.erb +0 -0
- data/lib/generators/service/install/templates/initializer.rb +5 -0
- data/lib/generators/service/install/templates/services/application_service.rb +42 -11
- data/lib/generators/service/install/templates/services/case_base.rb +2 -0
- data/lib/generators/service/install/templates/services/listener_base.rb +7 -0
- data/lib/generators/service/install/templates/services/query_base.rb +55 -0
- data/lib/generators/service/listener/listener_generator.rb +30 -0
- data/lib/generators/service/listener/templates/listener.rb.erb +11 -0
- data/lib/generators/service/query/USAGE +8 -0
- data/lib/generators/service/query/query_generator.rb +30 -0
- data/lib/generators/service/query/templates/query.rb.erb +22 -0
- data/lib/generators/service/service_generator.rb +1 -7
- data/lib/generators/service/setup/setup_generator.rb +10 -0
- data/lib/generators/service/setup/templates/doc.md.erb +39 -0
- data/lib/generators/service/usecase/templates/usecase.rb.erb +13 -3
- data/lib/generators/service/{test/templates/rspec/usecase.rb.erb → usecase/templates/usecase_spec.rb.erb} +0 -0
- data/lib/generators/service/usecase/usecase_generator.rb +12 -2
- metadata +36 -11
- data/lib/generators/service/policies.rb +0 -5
- data/lib/generators/service/test/USAGE +0 -8
- data/lib/generators/service/test/templates/minitest/usecase.rb.erb +0 -1
- data/lib/generators/service/test/test_generator.rb +0 -72
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f7f0b0d9360f1cd9db2ce8bbab1f35f44f0d4d631f7ff356607509237807dbc
|
|
4
|
+
data.tar.gz: badfbd6e44876729b1dde74a4dabedd77e11a0b8e0decc333fbe4b43ab9a3e2f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e65c2c56b4d32992a78753248012787b888aeaf099d5d8a4bf93e4a1b1efbb0a6c271ed83b79785a66748e0606175231c02ecef3bb1b267da9fee1d8f4cb4c69
|
|
7
|
+
data.tar.gz: 9c93b24c8f6831dab02c1a603474821ae37de77b5f3ede2ca3bdb8f91a48a2c68228285ca0fb1b8cae624658d7256827e31439b49e8d92a15501f221786491e8
|
data/Gemfile.lock
CHANGED
|
@@ -2,6 +2,7 @@ PATH
|
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
4
|
command_service_object (0.6.5)
|
|
5
|
+
hutch (= 1.0)
|
|
5
6
|
virtus (~> 1.0, >= 1.0.5)
|
|
6
7
|
|
|
7
8
|
GEM
|
|
@@ -48,13 +49,18 @@ GEM
|
|
|
48
49
|
i18n (>= 0.7, < 2)
|
|
49
50
|
minitest (~> 5.1)
|
|
50
51
|
tzinfo (~> 1.1)
|
|
52
|
+
amq-protocol (2.3.2)
|
|
51
53
|
arel (9.0.0)
|
|
52
54
|
axiom-types (0.1.1)
|
|
53
55
|
descendants_tracker (~> 0.0.4)
|
|
54
56
|
ice_nine (~> 0.11.0)
|
|
55
57
|
thread_safe (~> 0.3, >= 0.3.1)
|
|
56
58
|
builder (3.2.3)
|
|
59
|
+
bunny (2.15.0)
|
|
60
|
+
amq-protocol (~> 2.3, >= 2.3.1)
|
|
57
61
|
byebug (9.0.6)
|
|
62
|
+
carrot-top (0.0.7)
|
|
63
|
+
json
|
|
58
64
|
coercible (1.0.0)
|
|
59
65
|
descendants_tracker (~> 0.0.1)
|
|
60
66
|
concurrent-ruby (1.1.5)
|
|
@@ -65,9 +71,15 @@ GEM
|
|
|
65
71
|
erubi (1.8.0)
|
|
66
72
|
globalid (0.4.2)
|
|
67
73
|
activesupport (>= 4.2.0)
|
|
74
|
+
hutch (1.0.0)
|
|
75
|
+
activesupport (>= 4.2, < 7)
|
|
76
|
+
bunny (>= 2.15, < 2.16)
|
|
77
|
+
carrot-top (~> 0.0.7)
|
|
78
|
+
multi_json (~> 1.14)
|
|
68
79
|
i18n (1.6.0)
|
|
69
80
|
concurrent-ruby (~> 1.0)
|
|
70
81
|
ice_nine (0.11.2)
|
|
82
|
+
json (2.3.1)
|
|
71
83
|
loofah (2.2.3)
|
|
72
84
|
crass (~> 1.0.2)
|
|
73
85
|
nokogiri (>= 1.5.9)
|
|
@@ -80,6 +92,7 @@ GEM
|
|
|
80
92
|
mini_mime (1.0.1)
|
|
81
93
|
mini_portile2 (2.4.0)
|
|
82
94
|
minitest (5.11.3)
|
|
95
|
+
multi_json (1.15.0)
|
|
83
96
|
nio4r (2.3.1)
|
|
84
97
|
nokogiri (1.10.4)
|
|
85
98
|
mini_portile2 (~> 2.4.0)
|
|
@@ -144,4 +157,4 @@ DEPENDENCIES
|
|
|
144
157
|
thor (~> 0.20.3)
|
|
145
158
|
|
|
146
159
|
BUNDLED WITH
|
|
147
|
-
2.
|
|
160
|
+
2.1.4
|
data/README.md
CHANGED
|
@@ -8,11 +8,15 @@ Rails Generator for command service object.
|
|
|
8
8
|
|
|
9
9
|
### Implementation
|
|
10
10
|
|
|
11
|
-
Service consists of several objects { `Command Object` `Usecase Object`
|
|
11
|
+
Service consists of several objects { `Command Object` `Usecase Object` And `Error Object` (business logic error) }.
|
|
12
12
|
|
|
13
|
-
- **Command
|
|
14
|
-
- **Usecase
|
|
15
|
-
- **Micros:**
|
|
13
|
+
- **[Command](https://en.wikipedia.org/wiki/Command_pattern):** 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.
|
|
14
|
+
- **Usecase:** 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.
|
|
15
|
+
- **Micros:** Small reusable logic under the same service.
|
|
16
|
+
- **Externals:** Simple ruby module works as a service interface whenever you wanna call any external service or even service that lives under the same project you should use it.
|
|
17
|
+
- **Queries:** This dir is the only entry point for you to get any data form a service.
|
|
18
|
+
- **Listeners:** An event listener that waits for an event outside the service to occur.
|
|
19
|
+
- **Entities:** Many objects are not fundamentally defined by their attributes, but rather by a thread of continuity and identity.
|
|
16
20
|
|
|
17
21
|
#### Result Object
|
|
18
22
|
|
|
@@ -56,8 +60,8 @@ output
|
|
|
56
60
|
```bash
|
|
57
61
|
app/services/
|
|
58
62
|
├── application_service.rb
|
|
59
|
-
├── external/
|
|
60
63
|
├── auth_service
|
|
64
|
+
│ ├ external/
|
|
61
65
|
│ ├── commands
|
|
62
66
|
│ │ └── login.rb
|
|
63
67
|
│ └── usecases
|
|
@@ -99,11 +103,9 @@ then you can edit command params
|
|
|
99
103
|
# frozen_string_literal: true
|
|
100
104
|
|
|
101
105
|
module AuthService::Commands
|
|
102
|
-
class Login
|
|
106
|
+
class Login < CommandBase
|
|
103
107
|
# You can read Virtus gem doc for more info.
|
|
104
108
|
# https://github.com/solnic/virtus
|
|
105
|
-
include Virtus.model
|
|
106
|
-
include ActiveModel::Validations
|
|
107
109
|
|
|
108
110
|
# Attributes
|
|
109
111
|
# attribute :REPLACE_ME, String
|
|
@@ -129,13 +131,29 @@ module AuthService::Usecases
|
|
|
129
131
|
# methods for Business logic.
|
|
130
132
|
#
|
|
131
133
|
def call
|
|
132
|
-
token = generate_jwt_token_for(user)
|
|
134
|
+
token = generate_jwt_token_for(cmd.user)
|
|
135
|
+
replace_me
|
|
136
|
+
|
|
137
|
+
output
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def output
|
|
141
|
+
# return entity object
|
|
133
142
|
end
|
|
134
143
|
|
|
135
144
|
# This method will run if call method raise error
|
|
136
145
|
def rollback
|
|
137
146
|
# rollback logic
|
|
138
147
|
end
|
|
148
|
+
|
|
149
|
+
def allowed?
|
|
150
|
+
# policies loginc for issuer
|
|
151
|
+
# ex:
|
|
152
|
+
#
|
|
153
|
+
# return false if issuer.role != :admin
|
|
154
|
+
|
|
155
|
+
true
|
|
156
|
+
end
|
|
139
157
|
|
|
140
158
|
private
|
|
141
159
|
|
|
@@ -154,16 +172,16 @@ You can wrap external apis or services under `external/` dir
|
|
|
154
172
|
|
|
155
173
|
```ruby
|
|
156
174
|
module External
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
175
|
+
module StripeService
|
|
176
|
+
extend self
|
|
177
|
+
|
|
178
|
+
def charge(customer:, amount:, currency:, description: nil)
|
|
179
|
+
Stripe::Charge.create(
|
|
180
|
+
customer: customer.id,
|
|
181
|
+
amount: (round_up(amount, currency) * 100).to_i,
|
|
182
|
+
description: description || customer.email,
|
|
183
|
+
currency: currency
|
|
184
|
+
)
|
|
167
185
|
end
|
|
168
186
|
end
|
|
169
187
|
end
|
|
Binary file
|
|
@@ -12,15 +12,26 @@ module Service
|
|
|
12
12
|
def call
|
|
13
13
|
invoke Service::Generators::SetupGenerator, [name]
|
|
14
14
|
@model_attributes = model_attributes
|
|
15
|
+
|
|
15
16
|
commands.each do |c|
|
|
16
17
|
@command = c.classify
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
create_main(c)
|
|
19
|
+
create_test(c)
|
|
19
20
|
end
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
private
|
|
23
24
|
|
|
25
|
+
def create_main(m)
|
|
26
|
+
path = "app/services/#{service_name}/commands/#{m.underscore}.rb"
|
|
27
|
+
template 'command.rb.erb', path unless options.skip_command?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def create_test(m)
|
|
31
|
+
path = "spec/services/#{service_name}/commands/#{m.underscore}_spec.rb"
|
|
32
|
+
template 'command_spec.rb.erb', path
|
|
33
|
+
end
|
|
34
|
+
|
|
24
35
|
def service_name
|
|
25
36
|
"#{name.underscore}_service"
|
|
26
37
|
end
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require_relative '../setup/setup_generator.rb'
|
|
2
|
+
|
|
3
|
+
module Service
|
|
4
|
+
module Generators
|
|
5
|
+
class EntityGenerator < Rails::Generators::NamedBase
|
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
|
7
|
+
|
|
8
|
+
argument :entities, type: :array, default: [], banner: 'entities entities'
|
|
9
|
+
|
|
10
|
+
def setup
|
|
11
|
+
invoke Service::Generators::SetupGenerator, [name]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create_micros
|
|
15
|
+
entities.each do |m|
|
|
16
|
+
@entity = m.classify
|
|
17
|
+
|
|
18
|
+
path = "app/services/#{service_name}/entities/#{m.underscore}.rb"
|
|
19
|
+
template 'entity.rb.erb', path
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def service_name
|
|
26
|
+
"#{name.underscore}_service"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require_relative '../setup/setup_generator.rb'
|
|
2
|
+
|
|
3
|
+
module Service
|
|
4
|
+
module Generators
|
|
5
|
+
class ExternalGenerator < Rails::Generators::NamedBase
|
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
|
7
|
+
|
|
8
|
+
argument :externals, type: :array, default: [], banner: 'external external'
|
|
9
|
+
|
|
10
|
+
def create_externals
|
|
11
|
+
invoke Service::Generators::SetupGenerator, [name]
|
|
12
|
+
|
|
13
|
+
externals.each do |m|
|
|
14
|
+
@external = m.classify
|
|
15
|
+
create_main(m)
|
|
16
|
+
create_test(m)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def create_main(m)
|
|
23
|
+
path = "app/services/#{service_name}/externals/#{m.underscore}.rb"
|
|
24
|
+
template 'external.rb.erb', path
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def create_test(m)
|
|
28
|
+
path = "spec/services/#{service_name}/externals/#{m.underscore}_spec.rb"
|
|
29
|
+
template 'external_spec.rb.erb', path
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def service_name
|
|
33
|
+
"#{name.underscore}_service"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module <%= service_name.classify %>::Externals
|
|
4
|
+
module <%= @external %>
|
|
5
|
+
extend self
|
|
6
|
+
|
|
7
|
+
# Example
|
|
8
|
+
# def balance(id)
|
|
9
|
+
# res = http.get("user/:id")
|
|
10
|
+
# res_as_json = JSON.parse(res.body)
|
|
11
|
+
#
|
|
12
|
+
# The return value should only be OpenStruct or Hash Object
|
|
13
|
+
# OpenStruct.new(user_id: res_as_json[:id], balance: res_as_json[:balance])
|
|
14
|
+
# end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails_helper'
|
|
4
|
+
|
|
5
|
+
describe <%= service_name.classify %>::Externals::<%= @external %> do
|
|
6
|
+
# Example
|
|
7
|
+
# context "when success" do
|
|
8
|
+
# it 'should be okay?' do
|
|
9
|
+
# expect(subject.[method_name]).to eql([result])
|
|
10
|
+
# end
|
|
11
|
+
# end
|
|
12
|
+
end
|
|
File without changes
|
|
@@ -2,38 +2,69 @@
|
|
|
2
2
|
|
|
3
3
|
class ApplicationService
|
|
4
4
|
class << self
|
|
5
|
+
attr_reader :cmd, :usecase, :bm, :result
|
|
6
|
+
|
|
5
7
|
def call(cmd)
|
|
6
|
-
|
|
8
|
+
@cmd = cmd
|
|
9
|
+
@bm = Benchmark.measure do
|
|
10
|
+
raise Errors::InvalidCommand, cmd.class if cmd.invalid?
|
|
11
|
+
|
|
12
|
+
@usecase = usecase_class.new(cmd)
|
|
13
|
+
raise Errors::NotAuthorizedError, cmd.class unless usecase.allowed?
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
raise Errors::NotAuthorizedError, cmd.class unless usecase.allowed?
|
|
15
|
+
@result = ServiceResult.new { usecase.call }
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
rollback if result.error.present?
|
|
18
|
+
end
|
|
12
19
|
|
|
13
|
-
rollback(usecase, result, cmd) if result.error.present?
|
|
14
20
|
result
|
|
15
21
|
rescue StandardError => e
|
|
16
|
-
log_errors(e
|
|
22
|
+
log_errors(e)
|
|
17
23
|
ServiceResult.new { raise e }
|
|
24
|
+
ensure
|
|
25
|
+
log_command
|
|
18
26
|
end
|
|
19
27
|
|
|
20
|
-
def rollback
|
|
28
|
+
def rollback
|
|
21
29
|
usecase.rollback_micros
|
|
22
30
|
usecase.rollback
|
|
23
|
-
log_errors(result.error
|
|
31
|
+
log_errors(result.error)
|
|
24
32
|
end
|
|
25
33
|
|
|
26
|
-
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def log_errors(err)
|
|
27
37
|
return if err.class.is_a?(CommandServiceObject::Failure)
|
|
28
38
|
# Add your logging logic
|
|
29
39
|
# ex:
|
|
30
40
|
# Rollbar.error(err)
|
|
31
41
|
end
|
|
32
42
|
|
|
33
|
-
|
|
43
|
+
def log_command
|
|
44
|
+
service_logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(Rails.root.join('log', 'services.log').to_s))
|
|
45
|
+
status = result.ok? ? 'success' : 'faild'
|
|
34
46
|
|
|
35
|
-
|
|
47
|
+
service_logger.tagged(service_name, usecase_name, status) do
|
|
48
|
+
service_logger.info(
|
|
49
|
+
{
|
|
50
|
+
cmd: cmd.as_json,
|
|
51
|
+
result: result.value!.as_json,
|
|
52
|
+
benchmark: bm.as_json
|
|
53
|
+
}.as_json
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def usecase_class
|
|
36
59
|
cmd.class.name.gsub('Commands', 'Usecases').constantize
|
|
37
60
|
end
|
|
61
|
+
|
|
62
|
+
def service_name
|
|
63
|
+
cmd.class.name.split('::').first
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def usecase_name
|
|
67
|
+
cmd.class.name.split('::').last
|
|
68
|
+
end
|
|
38
69
|
end
|
|
39
70
|
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require "active_record"
|
|
2
|
+
|
|
3
|
+
class QueryBase
|
|
4
|
+
RelationRequired = Class.new(StandardError)
|
|
5
|
+
|
|
6
|
+
def initialize(*args)
|
|
7
|
+
@options = args.extract_options!
|
|
8
|
+
@relation = args.first || base_relation
|
|
9
|
+
|
|
10
|
+
if relation.nil?
|
|
11
|
+
raise(
|
|
12
|
+
RelationRequired,
|
|
13
|
+
"Queries require a base relation defined. Use .queries method to define relation."
|
|
14
|
+
)
|
|
15
|
+
elsif !relation.is_a?(ActiveRecord::Relation)
|
|
16
|
+
raise(
|
|
17
|
+
RelationRequired,
|
|
18
|
+
"Queries accept only ActiveRecord::Relation as input"
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.call(*args)
|
|
24
|
+
new(*args).query
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.queries(subject)
|
|
28
|
+
self.base_relation = subject
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def base_relation
|
|
32
|
+
return nil if self.class.base_relation.nil?
|
|
33
|
+
|
|
34
|
+
if self.class.base_relation.is_a?(ActiveRecord::Relation)
|
|
35
|
+
self.class.base_relation
|
|
36
|
+
elsif self.class.base_relation < ActiveRecord::Base
|
|
37
|
+
self.class.base_relation.all
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
class << self
|
|
44
|
+
attr_accessor :base_relation
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
attr_reader :relation, :options
|
|
48
|
+
|
|
49
|
+
def query
|
|
50
|
+
raise(
|
|
51
|
+
NotImplementedError,
|
|
52
|
+
"You need to implement #query method which returns ActiveRecord::Relation object"
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require_relative '../setup/setup_generator.rb'
|
|
2
|
+
|
|
3
|
+
module Service
|
|
4
|
+
module Generators
|
|
5
|
+
class ListenerGenerator < Rails::Generators::NamedBase
|
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
|
7
|
+
|
|
8
|
+
argument :listeners, type: :array, default: [], banner: 'Listener Listener'
|
|
9
|
+
|
|
10
|
+
def setup
|
|
11
|
+
invoke Service::Generators::SetupGenerator, [name]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create_micros
|
|
15
|
+
listeners.each do |m|
|
|
16
|
+
@listener = m.classify
|
|
17
|
+
|
|
18
|
+
path = "app/services/#{service_name}/listeners/#{m.underscore}.rb"
|
|
19
|
+
template 'listener.rb.erb', path
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def service_name
|
|
26
|
+
"#{name.underscore}_service"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require_relative '../setup/setup_generator.rb'
|
|
2
|
+
|
|
3
|
+
module Service
|
|
4
|
+
module Generators
|
|
5
|
+
class QueryGenerator < Rails::Generators::NamedBase
|
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
|
7
|
+
|
|
8
|
+
argument :queries, type: :array, default: [], banner: 'query query'
|
|
9
|
+
|
|
10
|
+
def setup
|
|
11
|
+
invoke Service::Generators::SetupGenerator, [name]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create_queries
|
|
15
|
+
queries.each do |m|
|
|
16
|
+
@query = m.classify
|
|
17
|
+
|
|
18
|
+
path = "app/services/#{service_name}/queries/#{m.underscore}.rb"
|
|
19
|
+
template 'query.rb.erb', path
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def service_name
|
|
26
|
+
"#{name.underscore}_service"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module <%= service_name.classify %>::Queries
|
|
4
|
+
class <%= @query %> < QueryBase
|
|
5
|
+
# queries UserService::Models::User
|
|
6
|
+
|
|
7
|
+
# Example
|
|
8
|
+
# def query
|
|
9
|
+
# map_rows(relation.where(phone: options.fetch(:phone)))
|
|
10
|
+
# end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
# def map_rows(rows)
|
|
15
|
+
# rows.map { |r| UserService::Views::User.new(r.as_json).to_hash }
|
|
16
|
+
# end
|
|
17
|
+
|
|
18
|
+
# def map_row(r)
|
|
19
|
+
# UserService::Views::User.new(r.as_json).to_hash
|
|
20
|
+
# end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -4,7 +4,6 @@ require 'rubygems'
|
|
|
4
4
|
require_relative './setup/setup_generator.rb'
|
|
5
5
|
require_relative './usecase/usecase_generator.rb'
|
|
6
6
|
require_relative './command/command_generator.rb'
|
|
7
|
-
require_relative './test/test_generator.rb'
|
|
8
7
|
require 'rails/generators'
|
|
9
8
|
require 'rails/generators/model_helpers'
|
|
10
9
|
|
|
@@ -16,6 +15,7 @@ module Service
|
|
|
16
15
|
# Skiping options
|
|
17
16
|
class_option :skip_usecase, type: :boolean, default: false, aliases: '-U'
|
|
18
17
|
class_option :skip_command, type: :boolean, default: false, aliases: '-C'
|
|
18
|
+
class_option :skip_entity, type: :boolean, default: false, aliases: '-E'
|
|
19
19
|
class_option :skip_test, type: :boolean, default: false, aliases: '-T'
|
|
20
20
|
|
|
21
21
|
def install_if_not
|
|
@@ -39,12 +39,6 @@ module Service
|
|
|
39
39
|
|
|
40
40
|
invoke Service::Generators::CommandGenerator, [name, usecases]
|
|
41
41
|
end
|
|
42
|
-
|
|
43
|
-
def generate_tests
|
|
44
|
-
return if options.skip_test?
|
|
45
|
-
|
|
46
|
-
invoke Service::Generators::TestGenerator, [name, usecases]
|
|
47
|
-
end
|
|
48
42
|
end
|
|
49
43
|
end
|
|
50
44
|
end
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
module Service
|
|
2
2
|
module Generators
|
|
3
3
|
class SetupGenerator < Rails::Generators::Base
|
|
4
|
+
source_root File.expand_path('templates', __dir__)
|
|
5
|
+
|
|
4
6
|
def setup
|
|
5
7
|
return if File.exist?("app/services/#{service_name}")
|
|
6
8
|
|
|
7
9
|
empty_directory("app/services/#{service_name}")
|
|
10
|
+
empty_directory("app/services/#{service_name}/listeners")
|
|
11
|
+
empty_directory("app/services/#{service_name}/jobs")
|
|
12
|
+
empty_directory("app/services/#{service_name}/externals")
|
|
13
|
+
empty_directory("app/services/#{service_name}/queries")
|
|
8
14
|
empty_directory("app/services/#{service_name}/usecases")
|
|
9
15
|
empty_directory("app/services/#{service_name}/commands")
|
|
16
|
+
empty_directory("app/services/#{service_name}/entities")
|
|
10
17
|
empty_directory("app/services/#{service_name}/usecases/micros")
|
|
18
|
+
|
|
19
|
+
path = "app/services/#{service_name}/doc.md"
|
|
20
|
+
template 'doc.md.erb', path unless File.exist?(path)
|
|
11
21
|
end
|
|
12
22
|
|
|
13
23
|
private
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
## <%= service_name.classify %>
|
|
2
|
+
The authorization system is responsible for identifying a particular user.
|
|
3
|
+
|
|
4
|
+
### Database Tables
|
|
5
|
+
- users
|
|
6
|
+
- profiles
|
|
7
|
+
|
|
8
|
+
### Entities:
|
|
9
|
+
###### *Many objects are not fundamentally defined by their attributes, but rather by a thread of continuity and identity.*
|
|
10
|
+
- User
|
|
11
|
+
- name: string
|
|
12
|
+
- email: string
|
|
13
|
+
- phone: string
|
|
14
|
+
- Profile:
|
|
15
|
+
- address: string
|
|
16
|
+
- foo: boolean
|
|
17
|
+
- bar: int
|
|
18
|
+
|
|
19
|
+
### Usecases (Processes):
|
|
20
|
+
###### *Use Cases focus on Users, Actions, and Processes.*
|
|
21
|
+
- **[Login](login-uml-and-full-doc)** - the process for authorization, we write out to the user an authentication key for a month. Only a user with a verified phone and email address can authenticate.
|
|
22
|
+
- **[Logout](logout-uml-and-full-doc)** - After logging out, the user will need to log in again to access the system.
|
|
23
|
+
|
|
24
|
+
### Queries:
|
|
25
|
+
- FindByEmail(email: string)
|
|
26
|
+
- FindByName(name: string)
|
|
27
|
+
|
|
28
|
+
### Listeners:
|
|
29
|
+
###### *An event listener that waits for an event outside the service to occur.*
|
|
30
|
+
- order_status
|
|
31
|
+
|
|
32
|
+
### Externals:
|
|
33
|
+
###### *Wrapper for any external interactions.*
|
|
34
|
+
- **UserService**
|
|
35
|
+
- user_info(id: id)
|
|
36
|
+
- **Stripe** - payment gateway wrapper.
|
|
37
|
+
- charge(pid, amount, currency)
|
|
38
|
+
- refund(charge_id)
|
|
39
|
+
- create_customer(email, phone, name)
|
|
@@ -8,6 +8,12 @@ module <%= service_name.classify %>::Usecases
|
|
|
8
8
|
#
|
|
9
9
|
def call
|
|
10
10
|
replace_me
|
|
11
|
+
|
|
12
|
+
output
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def output
|
|
16
|
+
# return entity object
|
|
11
17
|
end
|
|
12
18
|
|
|
13
19
|
# This method will run if call method raise error
|
|
@@ -24,10 +30,14 @@ module <%= service_name.classify %>::Usecases
|
|
|
24
30
|
true
|
|
25
31
|
end
|
|
26
32
|
|
|
33
|
+
def broadcast
|
|
34
|
+
Hutch.publish('<%= "#{service_name}_#{@usecase.underscore}" %>', subject: {}, action: '<%= "#{@usecase.underscore}" %>')
|
|
35
|
+
end
|
|
36
|
+
|
|
27
37
|
private
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
def replace_me
|
|
40
|
+
# [business logic]
|
|
41
|
+
end
|
|
32
42
|
end
|
|
33
43
|
end
|
|
File without changes
|
|
@@ -12,13 +12,23 @@ module Service
|
|
|
12
12
|
|
|
13
13
|
usecases.each do |u|
|
|
14
14
|
@usecase = u.classify
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
create_main(u)
|
|
16
|
+
create_test(u)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
private
|
|
21
21
|
|
|
22
|
+
def create_main(m)
|
|
23
|
+
path = "app/services/#{service_name}/usecases/#{m.underscore}.rb"
|
|
24
|
+
template 'usecase.rb.erb', path unless options.skip_command?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def create_test(m)
|
|
28
|
+
path = "spec/services/#{service_name}/usecases/#{m.underscore}_spec.rb"
|
|
29
|
+
template 'usecase_spec.rb.erb', path
|
|
30
|
+
end
|
|
31
|
+
|
|
22
32
|
def service_name
|
|
23
33
|
"#{name.underscore}_service"
|
|
24
34
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: command_service_object
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Adham EL-Deeb
|
|
8
8
|
- Mohamed Diaa
|
|
9
|
-
autorequire:
|
|
9
|
+
autorequire:
|
|
10
10
|
bindir: exe
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2020-09-23 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: bundler
|
|
@@ -121,6 +121,20 @@ dependencies:
|
|
|
121
121
|
- - ">="
|
|
122
122
|
- !ruby/object:Gem::Version
|
|
123
123
|
version: 1.0.5
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: hutch
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - '='
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '1.0'
|
|
131
|
+
type: :runtime
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - '='
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '1.0'
|
|
124
138
|
description: command_service_object gem helps you to generate service and command
|
|
125
139
|
objects using rails generator.
|
|
126
140
|
email:
|
|
@@ -154,10 +168,18 @@ files:
|
|
|
154
168
|
- lib/command_service_object/hooks.rb
|
|
155
169
|
- lib/command_service_object/railtie.rb
|
|
156
170
|
- lib/command_service_object/version.rb
|
|
171
|
+
- lib/generators/.DS_Store
|
|
157
172
|
- lib/generators/service/USAGE
|
|
158
173
|
- lib/generators/service/command/USAGE
|
|
159
174
|
- lib/generators/service/command/command_generator.rb
|
|
160
175
|
- lib/generators/service/command/templates/command.rb.erb
|
|
176
|
+
- lib/generators/service/command/templates/command_spec.rb.erb
|
|
177
|
+
- lib/generators/service/entity/entity_generator.rb
|
|
178
|
+
- lib/generators/service/entity/templates/entity.rb.erb
|
|
179
|
+
- lib/generators/service/external/external_generator.rb
|
|
180
|
+
- lib/generators/service/external/templates/external.rb.erb
|
|
181
|
+
- lib/generators/service/external/templates/external_spec.rb.erb
|
|
182
|
+
- lib/generators/service/external/templates/external_test.rb.erb
|
|
161
183
|
- lib/generators/service/getter/getter_generator.rb
|
|
162
184
|
- lib/generators/service/getter/templates/getter.rb.erb
|
|
163
185
|
- lib/generators/service/install/install_generator.rb
|
|
@@ -165,19 +187,22 @@ files:
|
|
|
165
187
|
- lib/generators/service/install/templates/services/application_service.rb
|
|
166
188
|
- lib/generators/service/install/templates/services/case_base.rb
|
|
167
189
|
- lib/generators/service/install/templates/services/command_base.rb
|
|
190
|
+
- lib/generators/service/install/templates/services/listener_base.rb
|
|
191
|
+
- lib/generators/service/install/templates/services/query_base.rb
|
|
168
192
|
- lib/generators/service/install/templates/services/service_result.rb
|
|
193
|
+
- lib/generators/service/listener/listener_generator.rb
|
|
194
|
+
- lib/generators/service/listener/templates/listener.rb.erb
|
|
169
195
|
- lib/generators/service/micro/micro_generator.rb
|
|
170
196
|
- lib/generators/service/micro/templates/micro.rb.erb
|
|
171
|
-
- lib/generators/service/
|
|
197
|
+
- lib/generators/service/query/USAGE
|
|
198
|
+
- lib/generators/service/query/query_generator.rb
|
|
199
|
+
- lib/generators/service/query/templates/query.rb.erb
|
|
172
200
|
- lib/generators/service/service_generator.rb
|
|
173
201
|
- lib/generators/service/setup/setup_generator.rb
|
|
174
|
-
- lib/generators/service/
|
|
175
|
-
- lib/generators/service/test/templates/minitest/usecase.rb.erb
|
|
176
|
-
- lib/generators/service/test/templates/rspec/command.rb.erb
|
|
177
|
-
- lib/generators/service/test/templates/rspec/usecase.rb.erb
|
|
178
|
-
- lib/generators/service/test/test_generator.rb
|
|
202
|
+
- lib/generators/service/setup/templates/doc.md.erb
|
|
179
203
|
- lib/generators/service/usecase/USAGE
|
|
180
204
|
- lib/generators/service/usecase/templates/usecase.rb.erb
|
|
205
|
+
- lib/generators/service/usecase/templates/usecase_spec.rb.erb
|
|
181
206
|
- lib/generators/service/usecase/usecase_generator.rb
|
|
182
207
|
homepage: https://github.com/adham90/command_service_object
|
|
183
208
|
licenses:
|
|
@@ -202,8 +227,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
202
227
|
- !ruby/object:Gem::Version
|
|
203
228
|
version: '0'
|
|
204
229
|
requirements: []
|
|
205
|
-
rubygems_version: 3.
|
|
206
|
-
signing_key:
|
|
230
|
+
rubygems_version: 3.1.2
|
|
231
|
+
signing_key:
|
|
207
232
|
specification_version: 4
|
|
208
233
|
summary: Rails Generator for command service object.
|
|
209
234
|
test_files: []
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
require_relative '../setup/setup_generator.rb'
|
|
2
|
-
|
|
3
|
-
module Service
|
|
4
|
-
module Generators
|
|
5
|
-
class TestGenerator < Rails::Generators::NamedBase
|
|
6
|
-
source_root File.expand_path('templates', __dir__)
|
|
7
|
-
argument :tests, type: :array, default: [], banner: 'usecase usecase'
|
|
8
|
-
|
|
9
|
-
def generate_test
|
|
10
|
-
return if options.skip_test?
|
|
11
|
-
|
|
12
|
-
if defined?(Minitest)
|
|
13
|
-
minitest_test
|
|
14
|
-
elsif defined?(RSpec)
|
|
15
|
-
rspec_test
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
private
|
|
20
|
-
|
|
21
|
-
def minitest_test
|
|
22
|
-
dir = create_test_dir('test')
|
|
23
|
-
|
|
24
|
-
tests.each do |t|
|
|
25
|
-
@test = t.classify
|
|
26
|
-
path = "#{dir}/usecases/#{t.underscore}_test.rb"
|
|
27
|
-
template 'minitest/usecase.rb.erb', path
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def rspec_test
|
|
32
|
-
dir = create_test_dir('spec')
|
|
33
|
-
create_usecase_test(dir)
|
|
34
|
-
create_command_test(dir)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def create_usecase_test(dir)
|
|
38
|
-
tests.each do |t|
|
|
39
|
-
@test = t.classify
|
|
40
|
-
path = "#{dir}/usecases/#{t.underscore}_spec.rb"
|
|
41
|
-
template 'rspec/usecase.rb.erb', path
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def create_command_test(dir)
|
|
46
|
-
tests.each do |t|
|
|
47
|
-
@test = t.classify
|
|
48
|
-
path = "#{dir}/commands/#{t.underscore}_spec.rb"
|
|
49
|
-
template 'rspec/command.rb.erb', path
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def create_test_dir(path)
|
|
54
|
-
test_path = "#{path}/services"
|
|
55
|
-
service_test = "#{test_path}/#{service_name}"
|
|
56
|
-
usecases_path = "#{test_path}/#{service_name}/usecases"
|
|
57
|
-
commands_path = "#{test_path}/#{service_name}/commands"
|
|
58
|
-
|
|
59
|
-
empty_directory(test_path) unless File.exist?(test_path)
|
|
60
|
-
empty_directory(service_test) unless File.exist?(service_test)
|
|
61
|
-
empty_directory(usecases_path) unless File.exist?(usecases_path)
|
|
62
|
-
empty_directory(commands_path) unless File.exist?(commands_path)
|
|
63
|
-
|
|
64
|
-
service_test
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def service_name
|
|
68
|
-
"#{name.underscore}_service"
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|