servactory 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +116 -0
- data/Rakefile +12 -0
- data/lib/servactory/base.rb +11 -0
- data/lib/servactory/configuration.rb +18 -0
- data/lib/servactory/context/configuration.rb +23 -0
- data/lib/servactory/context/dsl.rb +58 -0
- data/lib/servactory/context/store.rb +51 -0
- data/lib/servactory/errors/base.rb +7 -0
- data/lib/servactory/errors/failure.rb +7 -0
- data/lib/servactory/errors/input_argument_error.rb +7 -0
- data/lib/servactory/errors/internal_argument_error.rb +7 -0
- data/lib/servactory/errors/output_argument_error.rb +7 -0
- data/lib/servactory/input_arguments/checks/base.rb +23 -0
- data/lib/servactory/input_arguments/checks/inclusion.rb +45 -0
- data/lib/servactory/input_arguments/checks/must.rb +75 -0
- data/lib/servactory/input_arguments/checks/required.rb +54 -0
- data/lib/servactory/input_arguments/checks/type.rb +84 -0
- data/lib/servactory/input_arguments/collection.rb +19 -0
- data/lib/servactory/input_arguments/dsl.rb +27 -0
- data/lib/servactory/input_arguments/input_argument.rb +79 -0
- data/lib/servactory/input_arguments/tools/check.rb +74 -0
- data/lib/servactory/input_arguments/tools/find_unnecessary.rb +32 -0
- data/lib/servactory/input_arguments/tools/prepare.rb +89 -0
- data/lib/servactory/input_arguments/tools/rules.rb +38 -0
- data/lib/servactory/input_arguments/workbench.rb +40 -0
- data/lib/servactory/inputs.rb +7 -0
- data/lib/servactory/internal_arguments/checks/base.rb +17 -0
- data/lib/servactory/internal_arguments/checks/type.rb +55 -0
- data/lib/servactory/internal_arguments/collection.rb +19 -0
- data/lib/servactory/internal_arguments/dsl.rb +27 -0
- data/lib/servactory/internal_arguments/internal_argument.rb +33 -0
- data/lib/servactory/internal_arguments/tools/prepare.rb +58 -0
- data/lib/servactory/internal_arguments/workbench.rb +27 -0
- data/lib/servactory/output_arguments/checks/base.rb +17 -0
- data/lib/servactory/output_arguments/checks/type.rb +55 -0
- data/lib/servactory/output_arguments/collection.rb +19 -0
- data/lib/servactory/output_arguments/dsl.rb +27 -0
- data/lib/servactory/output_arguments/output_argument.rb +40 -0
- data/lib/servactory/output_arguments/tools/conflicts.rb +34 -0
- data/lib/servactory/output_arguments/tools/prepare.rb +58 -0
- data/lib/servactory/output_arguments/workbench.rb +31 -0
- data/lib/servactory/result.rb +21 -0
- data/lib/servactory/stage/dsl.rb +29 -0
- data/lib/servactory/stage/factory.rb +15 -0
- data/lib/servactory/stage/handyman.rb +35 -0
- data/lib/servactory/stage/method.rb +21 -0
- data/lib/servactory/stage/methods.rb +15 -0
- data/lib/servactory/utils.rb +11 -0
- data/lib/servactory/version.rb +11 -0
- data/lib/servactory.rb +34 -0
- metadata +237 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6ba22a3302a9dde10fcadfc2cc5777e83589e86862b8e96484ddd3ad62e78782
|
4
|
+
data.tar.gz: 4fa891477fb78494b3df34bb50f3b5bea018d331471662e235d91c49d82b9e6c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c4c5e7cbf5c7708cc468f25152086d974c1ecfee76222bade55215c4f14ee9b0b20194755338152ac21e72845e8c1358f4e185b08b6db08319c48990b5cf43ef
|
7
|
+
data.tar.gz: 70c1325c6b7de415565d41f4471798d1349c0627eeca7a329b876d703500b338acb725a5bd5adc41578656488541a13690358d377799c493f8e8ba719bb1c710
|
data/README.md
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
# Servactory
|
2
|
+
|
3
|
+
A set of tools for building reliable services of any complexity.
|
4
|
+
|
5
|
+
## Requirements
|
6
|
+
|
7
|
+
- Ruby >= 2.7
|
8
|
+
|
9
|
+
## Getting started
|
10
|
+
|
11
|
+
### Conventions
|
12
|
+
|
13
|
+
- Services are subclasses of `Servactory::Base` and are located in the `app/services` directory. It is common practice to create and inherit from `ApplicationService::Base`, which is a subclass of `Servactory::Base`.
|
14
|
+
- Name services by what they do, not by what they accept. Try to use verbs in names. For example, `UsersService::Create` instead of `UsersService::Creation`.
|
15
|
+
|
16
|
+
### Installation
|
17
|
+
|
18
|
+
Add this to `Gemfile`:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem "servactory"
|
22
|
+
```
|
23
|
+
|
24
|
+
And execute:
|
25
|
+
|
26
|
+
```shell
|
27
|
+
bundle install
|
28
|
+
```
|
29
|
+
|
30
|
+
### Preparation
|
31
|
+
|
32
|
+
We recommend that you first prepare the following files in your project.
|
33
|
+
|
34
|
+
#### ApplicationService::Errors
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# app/services/application_service/errors.rb
|
38
|
+
|
39
|
+
module ApplicationService
|
40
|
+
module Errors
|
41
|
+
class InputArgumentError < Servactory::Errors::InputArgumentError; end
|
42
|
+
class OutputArgumentError < Servactory::Errors::OutputArgumentError; end
|
43
|
+
class InternalArgumentError < Servactory::Errors::InternalArgumentError; end
|
44
|
+
|
45
|
+
class Failure < Servactory::Errors::Failure; end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
#### ApplicationService::Base
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
# app/services/application_service/base.rb
|
54
|
+
|
55
|
+
module ApplicationService
|
56
|
+
class Base < Servactory::Base
|
57
|
+
configuration do
|
58
|
+
input_argument_error_class ApplicationService::Errors::InputArgumentError
|
59
|
+
output_argument_error_class ApplicationService::Errors::OutputArgumentError
|
60
|
+
internal_argument_error_class ApplicationService::Errors::InternalArgumentError
|
61
|
+
|
62
|
+
failure_class ApplicationService::Errors::Failure
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
## Usage
|
69
|
+
|
70
|
+
### Minimal example
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
class SendService < ApplicationService::Base
|
74
|
+
stage { make :something }
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def something
|
79
|
+
# ...
|
80
|
+
end
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
### Inputs
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
class SendService < ApplicationService::Base
|
88
|
+
input :user, type: User
|
89
|
+
|
90
|
+
stage { make :something }
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def something
|
95
|
+
# ...
|
96
|
+
end
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
### Outputs
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
class SendService < ApplicationService::Base
|
104
|
+
input :user, type: User
|
105
|
+
|
106
|
+
output :notification, type: Notification
|
107
|
+
|
108
|
+
stage { make :something }
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def something
|
113
|
+
self.notification = Notification.create!
|
114
|
+
end
|
115
|
+
end
|
116
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
class Configuration
|
5
|
+
attr_accessor :input_argument_error_class,
|
6
|
+
:internal_argument_error_class,
|
7
|
+
:output_argument_error_class,
|
8
|
+
:failure_class
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@input_argument_error_class = Servactory::Errors::InputArgumentError
|
12
|
+
@internal_argument_error_class = Servactory::Errors::InternalArgumentError
|
13
|
+
@output_argument_error_class = Servactory::Errors::OutputArgumentError
|
14
|
+
|
15
|
+
@failure_class = Servactory::Errors::Failure
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module Context
|
5
|
+
class Configuration
|
6
|
+
def input_argument_error_class(input_argument_error_class)
|
7
|
+
Servactory.configuration.input_argument_error_class = input_argument_error_class
|
8
|
+
end
|
9
|
+
|
10
|
+
def output_argument_error_class(output_argument_error_class)
|
11
|
+
Servactory.configuration.output_argument_error_class = output_argument_error_class
|
12
|
+
end
|
13
|
+
|
14
|
+
def internal_argument_error_class(internal_argument_error_class)
|
15
|
+
Servactory.configuration.internal_argument_error_class = internal_argument_error_class
|
16
|
+
end
|
17
|
+
|
18
|
+
def failure_class(failure_class)
|
19
|
+
Servactory.configuration.failure_class = failure_class
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module Context
|
5
|
+
module DSL
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def call!(arguments) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
12
|
+
@context_store ||= Store.new(self)
|
13
|
+
|
14
|
+
assign_data_with(arguments)
|
15
|
+
|
16
|
+
input_arguments_workbench.find_unnecessary!
|
17
|
+
input_arguments_workbench.check_rules!
|
18
|
+
output_arguments_workbench.find_conflicts_in!(collection_of_internal_arguments:)
|
19
|
+
|
20
|
+
prepare_data
|
21
|
+
|
22
|
+
input_arguments_workbench.check!
|
23
|
+
|
24
|
+
stage_handyman.run_methods!
|
25
|
+
|
26
|
+
Servactory::Result.prepare_for(
|
27
|
+
context: context_store.context,
|
28
|
+
collection_of_output_arguments:
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :context_store
|
35
|
+
|
36
|
+
def assign_data_with(arguments)
|
37
|
+
input_arguments_workbench.assign(context: context_store.context, arguments:) # 1
|
38
|
+
internal_arguments_workbench.assign(context: context_store.context) # 2
|
39
|
+
output_arguments_workbench.assign(context: context_store.context) # 3
|
40
|
+
stage_handyman&.assign(context: context_store.context) # 4
|
41
|
+
end
|
42
|
+
|
43
|
+
def prepare_data
|
44
|
+
input_arguments_workbench.prepare # 1
|
45
|
+
|
46
|
+
output_arguments_workbench.prepare # 2
|
47
|
+
internal_arguments_workbench.prepare # 3
|
48
|
+
end
|
49
|
+
|
50
|
+
def configuration(&)
|
51
|
+
context_configuration = Servactory::Context::Configuration.new
|
52
|
+
|
53
|
+
context_configuration.instance_eval(&)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module Context
|
5
|
+
class Store
|
6
|
+
attr_reader :context
|
7
|
+
|
8
|
+
def initialize(service_class)
|
9
|
+
@context = service_class.new
|
10
|
+
|
11
|
+
service_class.class_eval(service_class_template_with(context))
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# EXAMPLE:
|
17
|
+
#
|
18
|
+
# attr_reader(:inputs)
|
19
|
+
#
|
20
|
+
# def assign_inputs(inputs)
|
21
|
+
# @inputs = inputs
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# def fail_input!(input_attribute_name, message, prefix: true)
|
25
|
+
# message_text = prefix ? "[#{context.class.name}] Custom `\#{input_attribute_name}` input error: " : ""
|
26
|
+
#
|
27
|
+
# message_text += message
|
28
|
+
#
|
29
|
+
# raise Servactory.configuration.input_argument_error_class, message_text
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
def service_class_template_with(context)
|
33
|
+
<<-RUBY
|
34
|
+
attr_reader(:inputs)
|
35
|
+
|
36
|
+
def assign_inputs(inputs)
|
37
|
+
@inputs = inputs
|
38
|
+
end
|
39
|
+
|
40
|
+
def fail_input!(input_attribute_name, message, prefix: true)
|
41
|
+
message_text = prefix ? "[#{context.class.name}] Custom `\#{input_attribute_name}` input error: " : ""
|
42
|
+
|
43
|
+
message_text += message
|
44
|
+
|
45
|
+
raise Servactory.configuration.input_argument_error_class, message_text
|
46
|
+
end
|
47
|
+
RUBY
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module InputArguments
|
5
|
+
module Checks
|
6
|
+
class Base
|
7
|
+
def initialize
|
8
|
+
@errors = []
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :errors
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def add_error(message, **arguments)
|
16
|
+
message = message.call(**arguments) if message.is_a?(Proc)
|
17
|
+
|
18
|
+
errors.push(message)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module InputArguments
|
5
|
+
module Checks
|
6
|
+
class Inclusion < Base
|
7
|
+
DEFAULT_MESSAGE = lambda do |service_class_name:, input:|
|
8
|
+
"[#{service_class_name}] Wrong value in `#{input.name}`, must be one of `#{input.inclusion}`"
|
9
|
+
end
|
10
|
+
|
11
|
+
private_constant :DEFAULT_MESSAGE
|
12
|
+
|
13
|
+
def self.check(context:, input:, value:, check_key:, **)
|
14
|
+
return unless should_be_checked_for?(input, check_key)
|
15
|
+
|
16
|
+
new(context:, input:, value:).check
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.should_be_checked_for?(input, check_key)
|
20
|
+
check_key == :inclusion && input.inclusion_present?
|
21
|
+
end
|
22
|
+
|
23
|
+
##########################################################################
|
24
|
+
|
25
|
+
def initialize(context:, input:, value:)
|
26
|
+
super()
|
27
|
+
|
28
|
+
@context = context
|
29
|
+
@input = input
|
30
|
+
@value = value
|
31
|
+
end
|
32
|
+
|
33
|
+
def check
|
34
|
+
return if @input.inclusion.include?(@value)
|
35
|
+
|
36
|
+
add_error(
|
37
|
+
DEFAULT_MESSAGE,
|
38
|
+
service_class_name: @context.class.name,
|
39
|
+
input: @input
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module InputArguments
|
5
|
+
module Checks
|
6
|
+
class Must < Base
|
7
|
+
DEFAULT_MESSAGE = lambda do |service_class_name:, input:, code:|
|
8
|
+
"[#{service_class_name}] Input `#{input.name}` " \
|
9
|
+
"must \"#{code.to_s.humanize(capitalize: false, keep_id_suffix: true)}\""
|
10
|
+
end
|
11
|
+
|
12
|
+
private_constant :DEFAULT_MESSAGE
|
13
|
+
|
14
|
+
def self.check(context:, input:, value:, check_key:, check_options:)
|
15
|
+
return unless should_be_checked_for?(input, check_key)
|
16
|
+
|
17
|
+
new(context:, input:, value:, check_options:).check
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.should_be_checked_for?(input, check_key)
|
21
|
+
check_key == :must && input.must_present?
|
22
|
+
end
|
23
|
+
|
24
|
+
##########################################################################
|
25
|
+
|
26
|
+
def initialize(context:, input:, value:, check_options:)
|
27
|
+
super()
|
28
|
+
|
29
|
+
@context = context
|
30
|
+
@input = input
|
31
|
+
@value = value
|
32
|
+
@check_options = check_options
|
33
|
+
end
|
34
|
+
|
35
|
+
def check
|
36
|
+
@check_options.each do |code, options|
|
37
|
+
message = call_or_fetch_message_from(code, options)
|
38
|
+
|
39
|
+
next if message.blank?
|
40
|
+
|
41
|
+
add_error(
|
42
|
+
DEFAULT_MESSAGE,
|
43
|
+
service_class_name: @context.class.name,
|
44
|
+
input: @input,
|
45
|
+
code:
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
errors
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def call_or_fetch_message_from(code, options) # rubocop:disable Metrics/MethodLength
|
55
|
+
check, message = options.values_at(:is, :message)
|
56
|
+
|
57
|
+
return if check.call(value: @value)
|
58
|
+
|
59
|
+
message.presence || DEFAULT_MESSAGE
|
60
|
+
rescue StandardError => e
|
61
|
+
message_text =
|
62
|
+
"[#{@context.class.name}] Syntax error inside `#{code}` of " \
|
63
|
+
"`#{@input.name}` input: [#{e.class}] #{e.message}"
|
64
|
+
|
65
|
+
add_error(
|
66
|
+
message_text,
|
67
|
+
service_class_name: @context.class.name,
|
68
|
+
input: @input,
|
69
|
+
code:
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module InputArguments
|
5
|
+
module Checks
|
6
|
+
class Required < Base
|
7
|
+
DEFAULT_MESSAGE = lambda do |service_class_name:, input:, value:|
|
8
|
+
if input.array? && value.present?
|
9
|
+
"[#{service_class_name}] Required element in input array `#{input.name}` is missing"
|
10
|
+
else
|
11
|
+
"[#{service_class_name}] Required input `#{input.name}` is missing"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private_constant :DEFAULT_MESSAGE
|
16
|
+
|
17
|
+
def self.check(context:, input:, value:, **)
|
18
|
+
return unless should_be_checked_for?(input)
|
19
|
+
|
20
|
+
new(context:, input:, value:).check
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.should_be_checked_for?(input)
|
24
|
+
input.required?
|
25
|
+
end
|
26
|
+
|
27
|
+
##########################################################################
|
28
|
+
|
29
|
+
def initialize(context:, input:, value:)
|
30
|
+
super()
|
31
|
+
|
32
|
+
@context = context
|
33
|
+
@input = input
|
34
|
+
@value = value
|
35
|
+
end
|
36
|
+
|
37
|
+
def check # rubocop:disable Metrics/MethodLength
|
38
|
+
if @input.array? && @value.present?
|
39
|
+
return if @value.respond_to?(:all?) && @value.all?(&:present?)
|
40
|
+
elsif @value.present?
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
add_error(
|
45
|
+
DEFAULT_MESSAGE,
|
46
|
+
service_class_name: @context.class.name,
|
47
|
+
input: @input,
|
48
|
+
value: @value
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module InputArguments
|
5
|
+
module Checks
|
6
|
+
class Type < Base
|
7
|
+
DEFAULT_MESSAGE = lambda do |service_class_name:, input:, expected_type:, given_type:|
|
8
|
+
if input.array?
|
9
|
+
"[#{service_class_name}] Wrong type in input array `#{input.name}`, expected `#{expected_type}`"
|
10
|
+
else
|
11
|
+
"[#{service_class_name}] Wrong type of input `#{input.name}`, " \
|
12
|
+
"expected `#{expected_type}`, " \
|
13
|
+
"got `#{given_type}`"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private_constant :DEFAULT_MESSAGE
|
18
|
+
|
19
|
+
def self.check(context:, input:, value:, check_key:, check_options:)
|
20
|
+
return unless should_be_checked_for?(input, value, check_key)
|
21
|
+
|
22
|
+
new(context:, input:, value:, types: check_options).check
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.should_be_checked_for?(input, value, check_key)
|
26
|
+
check_key == :types && (
|
27
|
+
input.required? || (
|
28
|
+
input.optional? && !input.default.nil?
|
29
|
+
) || (
|
30
|
+
input.optional? && !value.nil?
|
31
|
+
)
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
##########################################################################
|
36
|
+
|
37
|
+
def initialize(context:, input:, value:, types:)
|
38
|
+
super()
|
39
|
+
|
40
|
+
@context = context
|
41
|
+
@input = input
|
42
|
+
@value = value
|
43
|
+
@types = types
|
44
|
+
end
|
45
|
+
|
46
|
+
def check # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
47
|
+
return if prepared_types.any? do |type|
|
48
|
+
if @input.array?
|
49
|
+
prepared_value.is_a?(::Array) &&
|
50
|
+
prepared_value.respond_to?(:all?) && prepared_value.all?(type)
|
51
|
+
else
|
52
|
+
prepared_value.is_a?(type)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
add_error(
|
57
|
+
DEFAULT_MESSAGE,
|
58
|
+
service_class_name: @context.class.name,
|
59
|
+
input: @input,
|
60
|
+
expected_type: prepared_types.join(", "),
|
61
|
+
given_type: prepared_value.class.name
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
########################################################################
|
66
|
+
|
67
|
+
def prepared_types
|
68
|
+
@prepared_types ||=
|
69
|
+
@types.map do |type|
|
70
|
+
if type.is_a?(String)
|
71
|
+
Object.const_get(type)
|
72
|
+
else
|
73
|
+
type
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def prepared_value
|
79
|
+
@prepared_value ||= @input.optional? && !@input.default.nil? ? @input.default : @value
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module InputArguments
|
5
|
+
class Collection
|
6
|
+
# NOTE: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes
|
7
|
+
extend Forwardable
|
8
|
+
def_delegators :@collection, :<<, :each, :map
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
@collection = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def names
|
15
|
+
map(&:name)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module InputArguments
|
5
|
+
module DSL
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
private
|
12
|
+
|
13
|
+
def input(name, **options)
|
14
|
+
collection_of_input_arguments << InputArgument.new(name, **options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def collection_of_input_arguments
|
18
|
+
@collection_of_input_arguments ||= Collection.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def input_arguments_workbench
|
22
|
+
@input_arguments_workbench ||= Workbench.work_with(collection_of_input_arguments)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|