servactory 1.1.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 +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
|