servactory 1.6.4 → 1.6.6
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/README.md +48 -1
- data/lib/servactory/inputs/dsl.rb +2 -1
- data/lib/servactory/inputs/input.rb +28 -3
- data/lib/servactory/internals/validations/type.rb +8 -0
- data/lib/servactory/methods/dsl.rb +35 -7
- data/lib/servactory/methods/method_collection.rb +19 -0
- data/lib/servactory/methods/stage.rb +25 -0
- data/lib/servactory/methods/{collection.rb → stage_collection.rb} +2 -2
- data/lib/servactory/methods/workbench.rb +32 -8
- data/lib/servactory/outputs/validations/type.rb +10 -0
- data/lib/servactory/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c8df03a33262af045766ff179c047c3260b85f182aa0541137597e13d7d50bbe
|
4
|
+
data.tar.gz: 813454694dafd13757259a9c45ad455e30e4fd73013a8d51b5022624d3bd194a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b61bf7ef77779b5f8c697c514a2af1baac91095c553a31ff60c9e6080233f6153eae13b24a818e527a3f887755e35c3643ffd81c47fbbabdb5c64f8dc1a98054
|
7
|
+
data.tar.gz: 9722ed5fff0d2390a11636e2dc28e313cb338c726e8a9c630c4eee75ac1046a8684cb7dc7385691aaab7393bf1b9568a61b500c5a2334ca2d157d2eb239988d5
|
data/README.md
CHANGED
@@ -9,9 +9,56 @@ A set of tools for building reliable services of any complexity.
|
|
9
9
|
|
10
10
|
See [servactory.com](https://servactory.com) for documentation.
|
11
11
|
|
12
|
+
## Examples
|
13
|
+
|
14
|
+
### Service
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
class UserService::Authenticate < ApplicationService::Base
|
18
|
+
input :email, type: String
|
19
|
+
input :password, type: String
|
20
|
+
|
21
|
+
output :user, type: User
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def call
|
26
|
+
if (user = User.find_by(email: inputs.email)&.authenticate(inputs.password))
|
27
|
+
self.user = user
|
28
|
+
else
|
29
|
+
fail!(message: "Authentication failed")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
### Using in controller
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
class SessionsController < ApplicationController
|
39
|
+
def create
|
40
|
+
service_result = UserService::Authenticate.call(**session_params)
|
41
|
+
|
42
|
+
if service_result.success?
|
43
|
+
session[:current_user_id] = service_result.user.id
|
44
|
+
redirect_to service_result.user
|
45
|
+
else
|
46
|
+
flash.now[:message] = service_result.errors.first.message
|
47
|
+
render :new
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def session_params
|
54
|
+
params.require(:session).permit(:email, :password)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
12
59
|
## Contributing
|
13
60
|
|
14
|
-
This project is intended to be a safe, welcoming space for collaboration. Contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. We recommend reading the [contributing guide](./docs/
|
61
|
+
This project is intended to be a safe, welcoming space for collaboration. Contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. We recommend reading the [contributing guide](./website/docs/CONTRIBUTING.md) as well.
|
15
62
|
|
16
63
|
## License
|
17
64
|
|
@@ -5,12 +5,21 @@ module Servactory
|
|
5
5
|
class Input # rubocop:disable Metrics/ClassLength
|
6
6
|
ARRAY_DEFAULT_VALUE = ->(is: false, message: nil) { { is: is, message: message } }
|
7
7
|
|
8
|
+
HELPER_LIBRARY = {
|
9
|
+
optional: { required: false },
|
10
|
+
internal: { internal: true },
|
11
|
+
as_array: { array: true }
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
private_constant :ARRAY_DEFAULT_VALUE, :HELPER_LIBRARY
|
15
|
+
|
8
16
|
attr_reader :name,
|
9
17
|
:internal_name
|
10
18
|
|
11
19
|
# rubocop:disable Style/KeywordParametersOrder
|
12
20
|
def initialize(
|
13
21
|
name,
|
22
|
+
*helpers,
|
14
23
|
as: nil,
|
15
24
|
type:,
|
16
25
|
**options
|
@@ -18,6 +27,8 @@ module Servactory
|
|
18
27
|
@name = name
|
19
28
|
@internal_name = as.present? ? as : name
|
20
29
|
|
30
|
+
options = apply_helpers_for_options(helpers: helpers, options: options) if helpers.present?
|
31
|
+
|
21
32
|
add_basic_options_with(type: type, options: options)
|
22
33
|
|
23
34
|
collection_of_options.each do |option|
|
@@ -28,6 +39,20 @@ module Servactory
|
|
28
39
|
end
|
29
40
|
# rubocop:enable Style/KeywordParametersOrder
|
30
41
|
|
42
|
+
def apply_helpers_for_options(helpers:, options:)
|
43
|
+
prepared_options = {}
|
44
|
+
|
45
|
+
helpers.each do |helper|
|
46
|
+
next unless HELPER_LIBRARY.key?(helper)
|
47
|
+
|
48
|
+
found_helper = HELPER_LIBRARY[helper]
|
49
|
+
|
50
|
+
prepared_options.merge!(found_helper)
|
51
|
+
end
|
52
|
+
|
53
|
+
options.merge(prepared_options)
|
54
|
+
end
|
55
|
+
|
31
56
|
def add_basic_options_with(type:, options:)
|
32
57
|
# Check Class: Servactory::Inputs::Validations::Required
|
33
58
|
add_required_option_with(options)
|
@@ -63,7 +88,7 @@ module Servactory
|
|
63
88
|
)
|
64
89
|
],
|
65
90
|
define_input_conflicts: [
|
66
|
-
DefineInputConflict.new(content: -> {
|
91
|
+
DefineInputConflict.new(content: -> { :required_vs_default if required? && default_value_present? })
|
67
92
|
],
|
68
93
|
need_for_checks: true,
|
69
94
|
value_key: :is,
|
@@ -114,8 +139,8 @@ module Servactory
|
|
114
139
|
)
|
115
140
|
],
|
116
141
|
define_input_conflicts: [
|
117
|
-
DefineInputConflict.new(content: -> {
|
118
|
-
DefineInputConflict.new(content: -> {
|
142
|
+
DefineInputConflict.new(content: -> { :array_vs_array if array? && types.include?(Array) }),
|
143
|
+
DefineInputConflict.new(content: -> { :array_vs_inclusion if array? && inclusion_present? })
|
119
144
|
],
|
120
145
|
need_for_checks: false,
|
121
146
|
value_key: :is,
|
@@ -31,6 +31,8 @@ module Servactory
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def validate!
|
34
|
+
return unless should_be_checked?
|
35
|
+
|
34
36
|
return if prepared_types.any? { |type| @value.is_a?(type) }
|
35
37
|
|
36
38
|
raise_error_with(
|
@@ -44,6 +46,12 @@ module Servactory
|
|
44
46
|
|
45
47
|
private
|
46
48
|
|
49
|
+
def should_be_checked?
|
50
|
+
@internal.required? || (
|
51
|
+
@internal.optional? && !@value.nil?
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
47
55
|
def prepared_types
|
48
56
|
@prepared_types ||=
|
49
57
|
Array(@internal.types).map do |type|
|
@@ -11,17 +11,45 @@ module Servactory
|
|
11
11
|
def inherited(child)
|
12
12
|
super
|
13
13
|
|
14
|
-
child.send(:
|
14
|
+
child.send(:collection_of_stages).merge(collection_of_stages)
|
15
15
|
end
|
16
16
|
|
17
17
|
private
|
18
18
|
|
19
|
+
def stage(&block)
|
20
|
+
@current_stage = Stage.new(position: next_position)
|
21
|
+
|
22
|
+
instance_eval(&block)
|
23
|
+
|
24
|
+
@current_stage = nil
|
25
|
+
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def wrap_in(wrapper)
|
30
|
+
return if @current_stage.blank?
|
31
|
+
|
32
|
+
@current_stage.wrapper = wrapper
|
33
|
+
end
|
34
|
+
|
35
|
+
def rollback(rollback)
|
36
|
+
return if @current_stage.blank?
|
37
|
+
|
38
|
+
@current_stage.rollback = rollback
|
39
|
+
end
|
40
|
+
|
19
41
|
def make(name, position: nil, **options)
|
20
|
-
|
42
|
+
position = position.presence || next_position
|
43
|
+
|
44
|
+
@current_stage = @current_stage.presence || Stage.new(position: position)
|
45
|
+
|
46
|
+
@current_stage.methods << Method.new(
|
21
47
|
name,
|
22
|
-
position: position
|
48
|
+
position: position,
|
23
49
|
**options
|
24
50
|
)
|
51
|
+
|
52
|
+
collection_of_stages << @current_stage
|
25
53
|
end
|
26
54
|
|
27
55
|
def method_missing(shortcut_name, *args, &block)
|
@@ -39,15 +67,15 @@ module Servactory
|
|
39
67
|
end
|
40
68
|
|
41
69
|
def next_position
|
42
|
-
|
70
|
+
collection_of_stages.size + 1
|
43
71
|
end
|
44
72
|
|
45
|
-
def
|
46
|
-
@
|
73
|
+
def collection_of_stages
|
74
|
+
@collection_of_stages ||= StageCollection.new
|
47
75
|
end
|
48
76
|
|
49
77
|
def methods_workbench
|
50
|
-
@methods_workbench ||= Workbench.work_with(
|
78
|
+
@methods_workbench ||= Workbench.work_with(collection_of_stages)
|
51
79
|
end
|
52
80
|
end
|
53
81
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module Methods
|
5
|
+
class MethodCollection
|
6
|
+
# NOTE: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes
|
7
|
+
extend Forwardable
|
8
|
+
def_delegators :@collection, :<<, :each, :sort_by
|
9
|
+
|
10
|
+
def initialize(collection = Set.new)
|
11
|
+
@collection = collection
|
12
|
+
end
|
13
|
+
|
14
|
+
def sorted_by_position
|
15
|
+
MethodCollection.new(sort_by(&:position))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module Methods
|
5
|
+
class Stage
|
6
|
+
attr_accessor :position,
|
7
|
+
:wrapper,
|
8
|
+
:rollback
|
9
|
+
|
10
|
+
def initialize(position:, wrapper: nil, rollback: nil)
|
11
|
+
@position = position
|
12
|
+
@wrapper = wrapper
|
13
|
+
@rollback = rollback
|
14
|
+
end
|
15
|
+
|
16
|
+
def next_method_position
|
17
|
+
methods.size + 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def methods
|
21
|
+
@methods ||= MethodCollection.new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Servactory
|
4
4
|
module Methods
|
5
|
-
class
|
5
|
+
class StageCollection
|
6
6
|
# NOTE: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes
|
7
7
|
extend Forwardable
|
8
8
|
def_delegators :@collection, :<<, :each, :merge, :sort_by, :size, :empty?
|
@@ -12,7 +12,7 @@ module Servactory
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def sorted_by_position
|
15
|
-
|
15
|
+
StageCollection.new(sort_by(&:position))
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -7,8 +7,8 @@ module Servactory
|
|
7
7
|
new(...)
|
8
8
|
end
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
@
|
10
|
+
def initialize(collection_of_stages)
|
11
|
+
@collection_of_stages = collection_of_stages
|
12
12
|
end
|
13
13
|
|
14
14
|
def assign(context:)
|
@@ -16,24 +16,48 @@ module Servactory
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def run!
|
19
|
-
return try_to_use_call if
|
19
|
+
return try_to_use_call if collection_of_stages.empty?
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
context.send(make_method.name)
|
21
|
+
collection_of_stages.sorted_by_position.each do |stage|
|
22
|
+
call_stage(stage)
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
28
26
|
private
|
29
27
|
|
30
28
|
attr_reader :context,
|
31
|
-
:
|
29
|
+
:collection_of_stages
|
32
30
|
|
33
31
|
def try_to_use_call
|
34
32
|
context.try(:send, :call)
|
35
33
|
end
|
36
34
|
|
35
|
+
def call_stage(stage)
|
36
|
+
wrapper = stage.wrapper
|
37
|
+
rollback = stage.rollback
|
38
|
+
methods = stage.methods.sorted_by_position
|
39
|
+
|
40
|
+
if wrapper.is_a?(Proc)
|
41
|
+
call_wrapper_with_methods(wrapper, rollback, methods)
|
42
|
+
else
|
43
|
+
call_methods(methods)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def call_wrapper_with_methods(wrapper, rollback, methods)
|
48
|
+
wrapper.call(methods: -> { call_methods(methods) })
|
49
|
+
rescue StandardError => e
|
50
|
+
context.send(rollback, e) if rollback.present?
|
51
|
+
end
|
52
|
+
|
53
|
+
def call_methods(methods)
|
54
|
+
methods.each do |make_method|
|
55
|
+
next if unnecessary_for?(make_method)
|
56
|
+
|
57
|
+
context.send(make_method.name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
37
61
|
def unnecessary_for?(make_method)
|
38
62
|
condition = make_method.condition
|
39
63
|
is_condition_opposite = make_method.is_condition_opposite
|
@@ -31,6 +31,8 @@ module Servactory
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def validate!
|
34
|
+
return unless should_be_checked?
|
35
|
+
|
34
36
|
return if prepared_types.any? { |type| @value.is_a?(type) }
|
35
37
|
|
36
38
|
raise_error_with(
|
@@ -44,6 +46,14 @@ module Servactory
|
|
44
46
|
|
45
47
|
private
|
46
48
|
|
49
|
+
def should_be_checked?
|
50
|
+
@output.required? || (
|
51
|
+
@output.optional? && !@output.default.nil?
|
52
|
+
) || (
|
53
|
+
@output.optional? && !@value.nil?
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
47
57
|
def prepared_types
|
48
58
|
@prepared_types ||=
|
49
59
|
Array(@output.types).map do |type|
|
data/lib/servactory/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: servactory
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.6.
|
4
|
+
version: 1.6.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anton Sokolov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-06-
|
11
|
+
date: 2023-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -231,10 +231,12 @@ files:
|
|
231
231
|
- lib/servactory/internals/validations/base.rb
|
232
232
|
- lib/servactory/internals/validations/type.rb
|
233
233
|
- lib/servactory/internals/workbench.rb
|
234
|
-
- lib/servactory/methods/collection.rb
|
235
234
|
- lib/servactory/methods/dsl.rb
|
236
235
|
- lib/servactory/methods/method.rb
|
236
|
+
- lib/servactory/methods/method_collection.rb
|
237
237
|
- lib/servactory/methods/shortcuts/collection.rb
|
238
|
+
- lib/servactory/methods/stage.rb
|
239
|
+
- lib/servactory/methods/stage_collection.rb
|
238
240
|
- lib/servactory/methods/workbench.rb
|
239
241
|
- lib/servactory/outputs/collection.rb
|
240
242
|
- lib/servactory/outputs/dsl.rb
|
@@ -252,7 +254,9 @@ licenses:
|
|
252
254
|
- MIT
|
253
255
|
metadata:
|
254
256
|
homepage_uri: https://github.com/afuno/servactory
|
257
|
+
documentation_uri: https://servactory.com
|
255
258
|
source_code_uri: https://github.com/afuno/servactory
|
259
|
+
bug_tracker_uri: https://github.com/afuno/servactory/issues
|
256
260
|
changelog_uri: https://github.com/afuno/servactory/blob/master/CHANGELOG.md
|
257
261
|
rubygems_mfa_required: 'true'
|
258
262
|
post_install_message:
|