servactory 2.16.0 → 3.0.0.rc1
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 +38 -9
- data/config/locales/de.yml +134 -0
- data/config/locales/en.yml +15 -0
- data/config/locales/es.yml +134 -0
- data/config/locales/fr.yml +134 -0
- data/config/locales/it.yml +134 -0
- data/config/locales/ru.yml +15 -0
- data/lib/generators/README.md +45 -0
- data/lib/generators/servactory/base.rb +82 -0
- data/lib/generators/servactory/extension/USAGE +54 -0
- data/lib/generators/servactory/extension/extension_generator.rb +41 -0
- data/lib/generators/servactory/extension/templates/extension.rb.tt +62 -0
- data/lib/generators/servactory/install/USAGE +27 -0
- data/lib/generators/servactory/install/install_generator.rb +94 -0
- data/lib/generators/servactory/{templates/services/application_service/base.rb → install/templates/application_service/base.rb.tt} +29 -19
- data/lib/generators/servactory/{templates/services/application_service/exceptions.rb → install/templates/application_service/exceptions.rb.tt} +1 -1
- data/lib/generators/servactory/{templates/services/application_service/result.rb → install/templates/application_service/result.rb.tt} +1 -1
- data/lib/generators/servactory/rspec/USAGE +46 -0
- data/lib/generators/servactory/rspec/rspec_generator.rb +95 -0
- data/lib/generators/servactory/rspec/templates/service_spec.rb.tt +58 -0
- data/lib/generators/servactory/service/USAGE +51 -0
- data/lib/generators/servactory/service/service_generator.rb +56 -0
- data/lib/generators/servactory/service/templates/service.rb.tt +22 -0
- data/lib/servactory/actions/collection.rb +56 -1
- data/lib/servactory/actions/dsl.rb +11 -11
- data/lib/servactory/actions/tools/rules.rb +1 -1
- data/lib/servactory/actions/tools/runner.rb +111 -28
- data/lib/servactory/base.rb +1 -7
- data/lib/servactory/configuration/actions/aliases/collection.rb +5 -0
- data/lib/servactory/configuration/actions/rescue_handlers/collection.rb +5 -0
- data/lib/servactory/configuration/actions/shortcuts/collection.rb +5 -0
- data/lib/servactory/configuration/collection_mode/class_names_collection.rb +5 -0
- data/lib/servactory/configuration/config.rb +36 -0
- data/lib/servactory/configuration/configurable.rb +95 -0
- data/lib/servactory/configuration/dsl.rb +3 -27
- data/lib/servactory/configuration/factory.rb +20 -20
- data/lib/servactory/configuration/hash_mode/class_names_collection.rb +5 -0
- data/lib/servactory/configuration/option_helpers/option_helpers_collection.rb +5 -0
- data/lib/servactory/context/warehouse/inputs.rb +2 -2
- data/lib/servactory/context/workspace/inputs.rb +2 -2
- data/lib/servactory/context/workspace/internals.rb +2 -2
- data/lib/servactory/context/workspace/outputs.rb +2 -2
- data/lib/servactory/context/workspace.rb +11 -7
- data/lib/servactory/dsl.rb +10 -8
- data/lib/servactory/maintenance/attributes/tools/validation.rb +1 -1
- data/lib/servactory/result.rb +2 -2
- data/lib/servactory/stroma/dsl.rb +118 -0
- data/lib/servactory/stroma/entry.rb +32 -0
- data/lib/servactory/stroma/exceptions/base.rb +45 -0
- data/lib/servactory/stroma/exceptions/invalid_hook_type.rb +29 -0
- data/lib/servactory/stroma/exceptions/key_already_registered.rb +32 -0
- data/lib/servactory/stroma/exceptions/registry_frozen.rb +33 -0
- data/lib/servactory/stroma/exceptions/registry_not_finalized.rb +33 -0
- data/lib/servactory/stroma/exceptions/unknown_hook_target.rb +39 -0
- data/lib/servactory/stroma/hooks/applier.rb +63 -0
- data/lib/servactory/stroma/hooks/collection.rb +103 -0
- data/lib/servactory/stroma/hooks/factory.rb +80 -0
- data/lib/servactory/stroma/hooks/hook.rb +74 -0
- data/lib/servactory/stroma/registry.rb +94 -0
- data/lib/servactory/stroma/settings/collection.rb +90 -0
- data/lib/servactory/stroma/settings/registry_settings.rb +88 -0
- data/lib/servactory/stroma/settings/setting.rb +113 -0
- data/lib/servactory/stroma/state.rb +59 -0
- data/lib/servactory/test_kit/fake_type.rb +23 -0
- data/lib/servactory/test_kit/result.rb +45 -0
- data/lib/servactory/test_kit/rspec/helpers/argument_matchers.rb +97 -0
- data/lib/servactory/test_kit/rspec/helpers/concerns/error_messages.rb +179 -0
- data/lib/servactory/test_kit/rspec/helpers/concerns/service_class_validation.rb +74 -0
- data/lib/servactory/test_kit/rspec/helpers/fluent.rb +110 -0
- data/lib/servactory/test_kit/rspec/helpers/input_validator.rb +149 -0
- data/lib/servactory/test_kit/rspec/helpers/legacy.rb +228 -0
- data/lib/servactory/test_kit/rspec/helpers/mock_executor.rb +256 -0
- data/lib/servactory/test_kit/rspec/helpers/output_validator.rb +121 -0
- data/lib/servactory/test_kit/rspec/helpers/service_mock_builder.rb +422 -0
- data/lib/servactory/test_kit/rspec/helpers/service_mock_config.rb +129 -0
- data/lib/servactory/test_kit/rspec/helpers.rb +51 -84
- data/lib/servactory/test_kit/rspec/matchers/base/attribute_matcher.rb +324 -0
- data/lib/servactory/test_kit/rspec/matchers/base/submatcher.rb +133 -0
- data/lib/servactory/test_kit/rspec/matchers/base/submatcher_context.rb +101 -0
- data/lib/servactory/test_kit/rspec/matchers/base/submatcher_registry.rb +205 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/attribute_data_access.rb +100 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/error_message_builder.rb +106 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/value_comparison.rb +97 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matcher.rb +89 -219
- data/lib/servactory/test_kit/rspec/matchers/have_service_internal_matcher.rb +74 -166
- data/lib/servactory/test_kit/rspec/matchers/have_service_output_matcher.rb +238 -0
- data/lib/servactory/test_kit/rspec/matchers/result/be_failure_service_matcher.rb +257 -0
- data/lib/servactory/test_kit/rspec/matchers/result/be_success_service_matcher.rb +185 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/default_submatcher.rb +81 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/optional_submatcher.rb +62 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/required_submatcher.rb +93 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/valid_with_submatcher.rb +271 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/consists_of_submatcher.rb +85 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/inclusion_submatcher.rb +120 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/message_submatcher.rb +115 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/must_submatcher.rb +82 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/schema_submatcher.rb +102 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/target_submatcher.rb +125 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/types_submatcher.rb +77 -0
- data/lib/servactory/test_kit/rspec/matchers.rb +126 -285
- data/lib/servactory/test_kit/utils/faker.rb +62 -2
- data/lib/servactory/tool_kit/dynamic_options/consists_of.rb +166 -0
- data/lib/servactory/tool_kit/dynamic_options/format.rb +195 -8
- data/lib/servactory/tool_kit/dynamic_options/inclusion.rb +219 -17
- data/lib/servactory/tool_kit/dynamic_options/max.rb +143 -0
- data/lib/servactory/tool_kit/dynamic_options/min.rb +143 -0
- data/lib/servactory/tool_kit/dynamic_options/multiple_of.rb +157 -8
- data/lib/servactory/tool_kit/dynamic_options/must.rb +194 -0
- data/lib/servactory/tool_kit/dynamic_options/schema.rb +226 -2
- data/lib/servactory/tool_kit/dynamic_options/target.rb +252 -0
- data/lib/servactory/version.rb +3 -3
- data/lib/servactory.rb +4 -0
- metadata +73 -25
- data/lib/generators/servactory/install_generator.rb +0 -21
- data/lib/generators/servactory/rspec_generator.rb +0 -88
- data/lib/generators/servactory/service_generator.rb +0 -49
- data/lib/servactory/configuration/setup.rb +0 -97
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/consists_of_matcher.rb +0 -68
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/inclusion_matcher.rb +0 -73
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/message_matcher.rb +0 -91
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/must_matcher.rb +0 -72
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/schema_matcher.rb +0 -92
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/types_matcher.rb +0 -72
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/default_matcher.rb +0 -69
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/optional_matcher.rb +0 -63
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/required_matcher.rb +0 -81
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/valid_with_matcher.rb +0 -199
data/lib/servactory/dsl.rb
CHANGED
|
@@ -16,17 +16,19 @@ module Servactory
|
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
Stroma::Registry.register(:configuration, Configuration::DSL)
|
|
20
|
+
Stroma::Registry.register(:info, Info::DSL)
|
|
21
|
+
Stroma::Registry.register(:context, Context::DSL)
|
|
22
|
+
Stroma::Registry.register(:inputs, Inputs::DSL)
|
|
23
|
+
Stroma::Registry.register(:internals, Internals::DSL)
|
|
24
|
+
Stroma::Registry.register(:outputs, Outputs::DSL)
|
|
25
|
+
Stroma::Registry.register(:actions, Actions::DSL)
|
|
26
|
+
Stroma::Registry.finalize!
|
|
27
|
+
|
|
19
28
|
def self.included(base)
|
|
20
|
-
base.include(
|
|
21
|
-
base.include(Info::DSL)
|
|
22
|
-
base.include(Context::DSL)
|
|
23
|
-
base.include(Inputs::DSL)
|
|
24
|
-
base.include(Internals::DSL)
|
|
25
|
-
base.include(Outputs::DSL)
|
|
29
|
+
base.include(Stroma::DSL)
|
|
26
30
|
|
|
27
31
|
Extensions.registry.each { |extension| base.include(extension) }
|
|
28
|
-
|
|
29
|
-
base.include(Actions::DSL)
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
def self.with_extensions(*extensions)
|
|
@@ -74,7 +74,7 @@ module Servactory
|
|
|
74
74
|
def raise_errors
|
|
75
75
|
return if (tmp_errors = errors.not_blank).empty?
|
|
76
76
|
|
|
77
|
-
raise @context.
|
|
77
|
+
raise @context.config
|
|
78
78
|
.public_send(:"#{@attribute.system_name}_exception_class")
|
|
79
79
|
.new(context: @context, message: tmp_errors.first)
|
|
80
80
|
end
|
data/lib/servactory/result.rb
CHANGED
|
@@ -117,7 +117,7 @@ module Servactory
|
|
|
117
117
|
@outputs ||= Outputs.new(
|
|
118
118
|
outputs: @context.send(:servactory_service_warehouse).outputs,
|
|
119
119
|
predicate_methods_enabled:
|
|
120
|
-
@context.is_a?(Servactory::TestKit::Result) || @context.
|
|
120
|
+
@context.is_a?(Servactory::TestKit::Result) || @context.config.predicate_methods_enabled
|
|
121
121
|
)
|
|
122
122
|
end
|
|
123
123
|
|
|
@@ -126,7 +126,7 @@ module Servactory
|
|
|
126
126
|
def rescue_no_method_error_with(exception:) # rubocop:disable Metrics/MethodLength
|
|
127
127
|
raise exception if @context.blank? || @context.instance_of?(Servactory::TestKit::Result)
|
|
128
128
|
|
|
129
|
-
raise @context.
|
|
129
|
+
raise @context.config.failure_class.new(
|
|
130
130
|
type: :base,
|
|
131
131
|
message: @context.send(:servactory_service_info).translate(
|
|
132
132
|
"common.undefined_method.missing_name",
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Stroma
|
|
5
|
+
# Main integration point between Stroma and service classes.
|
|
6
|
+
#
|
|
7
|
+
# ## Purpose
|
|
8
|
+
#
|
|
9
|
+
# Module that provides the core Stroma functionality to service classes:
|
|
10
|
+
# - Includes all registered DSL modules
|
|
11
|
+
# - Provides extensions block for hook registration
|
|
12
|
+
# - Handles inheritance with proper state copying
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# ```ruby
|
|
17
|
+
# class MyService
|
|
18
|
+
# include Servactory::Stroma::DSL
|
|
19
|
+
#
|
|
20
|
+
# extensions do
|
|
21
|
+
# before :actions, MyExtension
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
# ```
|
|
25
|
+
#
|
|
26
|
+
# ## Extension Settings Access
|
|
27
|
+
#
|
|
28
|
+
# Extensions access their settings through the stroma.settings hierarchy:
|
|
29
|
+
#
|
|
30
|
+
# ```ruby
|
|
31
|
+
# # In ClassMethods:
|
|
32
|
+
# stroma.settings[:actions][:authorization][:method_name] = :authorize
|
|
33
|
+
#
|
|
34
|
+
# # In InstanceMethods:
|
|
35
|
+
# self.class.stroma.settings[:actions][:authorization][:method_name]
|
|
36
|
+
# ```
|
|
37
|
+
#
|
|
38
|
+
# ## Integration
|
|
39
|
+
#
|
|
40
|
+
# Included by Servactory::DSL which is included by Servactory::Base.
|
|
41
|
+
# Provides ClassMethods with: stroma, inherited, extensions.
|
|
42
|
+
module DSL
|
|
43
|
+
def self.included(base)
|
|
44
|
+
base.extend(ClassMethods)
|
|
45
|
+
|
|
46
|
+
Registry.entries.each do |entry|
|
|
47
|
+
base.include(entry.extension)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Class-level methods for Stroma integration.
|
|
52
|
+
#
|
|
53
|
+
# ## Purpose
|
|
54
|
+
#
|
|
55
|
+
# Provides access to Stroma state and hooks DSL at the class level.
|
|
56
|
+
# Handles proper duplication during inheritance.
|
|
57
|
+
#
|
|
58
|
+
# ## Key Methods
|
|
59
|
+
#
|
|
60
|
+
# - `stroma` - Access the State container
|
|
61
|
+
# - `inherited` - Copy state to child classes
|
|
62
|
+
# - `extensions` - DSL block for hook registration
|
|
63
|
+
module ClassMethods
|
|
64
|
+
def self.extended(base)
|
|
65
|
+
base.instance_variable_set(:@stroma, State.new)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Handles inheritance by duplicating Stroma state.
|
|
69
|
+
#
|
|
70
|
+
# Creates an independent copy of hooks and settings for the child class,
|
|
71
|
+
# then applies all registered hooks to the child.
|
|
72
|
+
#
|
|
73
|
+
# @param child [Class] The child class being created
|
|
74
|
+
# @return [void]
|
|
75
|
+
def inherited(child)
|
|
76
|
+
super
|
|
77
|
+
|
|
78
|
+
child.instance_variable_set(:@stroma, stroma.dup)
|
|
79
|
+
|
|
80
|
+
Hooks::Applier.new(child, child.stroma.hooks).apply!
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Returns the Stroma state for this service class.
|
|
84
|
+
#
|
|
85
|
+
# @return [State] The Stroma state container
|
|
86
|
+
#
|
|
87
|
+
# @example Accessing hooks
|
|
88
|
+
# stroma.hooks.before(:actions)
|
|
89
|
+
#
|
|
90
|
+
# @example Accessing settings
|
|
91
|
+
# stroma.settings[:actions][:authorization][:method_name]
|
|
92
|
+
def stroma
|
|
93
|
+
@stroma ||= State.new
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
# DSL block for registering hooks.
|
|
99
|
+
#
|
|
100
|
+
# Evaluates the block in the context of a Hooks::Factory,
|
|
101
|
+
# allowing before/after hook registration.
|
|
102
|
+
#
|
|
103
|
+
# @yield Block with before/after DSL calls
|
|
104
|
+
# @return [void]
|
|
105
|
+
#
|
|
106
|
+
# @example
|
|
107
|
+
# extensions do
|
|
108
|
+
# before :actions, AuthorizationExtension
|
|
109
|
+
# after :outputs, LoggingExtension
|
|
110
|
+
# end
|
|
111
|
+
def extensions(&block)
|
|
112
|
+
@stroma_hooks_factory ||= Hooks::Factory.new(stroma.hooks)
|
|
113
|
+
@stroma_hooks_factory.instance_eval(&block)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Stroma
|
|
5
|
+
# Represents a registered DSL entry in the Stroma registry.
|
|
6
|
+
#
|
|
7
|
+
# ## Purpose
|
|
8
|
+
#
|
|
9
|
+
# Immutable value object that holds information about a DSL module
|
|
10
|
+
# registered in the Stroma system. Each entry has a unique key
|
|
11
|
+
# and references a Module that will be included in service classes.
|
|
12
|
+
#
|
|
13
|
+
# ## Attributes
|
|
14
|
+
#
|
|
15
|
+
# - `key` (Symbol): Unique identifier for the DSL module (:inputs, :outputs, :actions)
|
|
16
|
+
# - `extension` (Module): The actual DSL module to be included
|
|
17
|
+
#
|
|
18
|
+
# ## Usage
|
|
19
|
+
#
|
|
20
|
+
# Entries are created internally by Registry.register:
|
|
21
|
+
#
|
|
22
|
+
# ```ruby
|
|
23
|
+
# Stroma::Registry.register(:inputs, Servactory::Inputs::DSL)
|
|
24
|
+
# # Creates: Entry.new(key: :inputs, extension: Servactory::Inputs::DSL)
|
|
25
|
+
# ```
|
|
26
|
+
#
|
|
27
|
+
# ## Immutability
|
|
28
|
+
#
|
|
29
|
+
# Entry is immutable (Data object) - once created, it cannot be modified.
|
|
30
|
+
Entry = Data.define(:key, :extension)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Stroma
|
|
5
|
+
module Exceptions
|
|
6
|
+
# Base exception class for all Stroma-specific exceptions
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Serves as the parent class for all custom exceptions in the Stroma subsystem.
|
|
11
|
+
# Allows catching all Stroma-related exceptions with a single rescue clause.
|
|
12
|
+
#
|
|
13
|
+
# ## Usage
|
|
14
|
+
#
|
|
15
|
+
# All Stroma exceptions inherit from this base class:
|
|
16
|
+
#
|
|
17
|
+
# ```ruby
|
|
18
|
+
# begin
|
|
19
|
+
# Servactory::Stroma::Registry.register(:custom, CustomModule)
|
|
20
|
+
# rescue Servactory::Stroma::Exceptions::Base => e
|
|
21
|
+
# # Catches any Stroma-specific exception
|
|
22
|
+
# handle_stroma_error(e)
|
|
23
|
+
# end
|
|
24
|
+
# ```
|
|
25
|
+
#
|
|
26
|
+
# ## Integration
|
|
27
|
+
#
|
|
28
|
+
# Can be used in application error handlers for centralized error handling:
|
|
29
|
+
#
|
|
30
|
+
# ```ruby
|
|
31
|
+
# rescue_from Servactory::Stroma::Exceptions::Base, with: :handle_stroma_error
|
|
32
|
+
# ```
|
|
33
|
+
#
|
|
34
|
+
# ## Subclasses
|
|
35
|
+
#
|
|
36
|
+
# - RegistryFrozen - Raised when modifying a finalized registry
|
|
37
|
+
# - RegistryNotFinalized - Raised when accessing registry before finalization
|
|
38
|
+
# - KeyAlreadyRegistered - Raised when registering a duplicate key
|
|
39
|
+
# - UnknownHookTarget - Raised when using an invalid hook target key
|
|
40
|
+
# - InvalidHookType - Raised when using an invalid hook type (:before/:after)
|
|
41
|
+
class Base < StandardError
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Stroma
|
|
5
|
+
module Exceptions
|
|
6
|
+
# Raised when an invalid hook type is provided.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Ensures that only valid hook types (:before, :after) are used
|
|
11
|
+
# when creating Servactory::Stroma::Hooks::Hook objects. Provides fail-fast
|
|
12
|
+
# behavior during class definition rather than silent failures at runtime.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# ```ruby
|
|
17
|
+
# # This will raise InvalidHookType:
|
|
18
|
+
# Servactory::Stroma::Hooks::Hook.new(
|
|
19
|
+
# type: :invalid,
|
|
20
|
+
# target_key: :actions,
|
|
21
|
+
# extension: MyModule
|
|
22
|
+
# )
|
|
23
|
+
# # => Servactory::Stroma::Exceptions::InvalidHookType:
|
|
24
|
+
# # Invalid hook type: :invalid. Valid types: :before, :after
|
|
25
|
+
# ```
|
|
26
|
+
class InvalidHookType < Base; end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Stroma
|
|
5
|
+
module Exceptions
|
|
6
|
+
# Raised when registering a duplicate key in the registry
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Indicates that a DSL module key has already been registered.
|
|
11
|
+
# Each DSL module must have a unique key in the registry.
|
|
12
|
+
#
|
|
13
|
+
# ## Usage
|
|
14
|
+
#
|
|
15
|
+
# Raised when attempting to register a duplicate key:
|
|
16
|
+
#
|
|
17
|
+
# ```ruby
|
|
18
|
+
# Servactory::Stroma::Registry.register(:inputs, Inputs::DSL)
|
|
19
|
+
# Servactory::Stroma::Registry.register(:inputs, AnotherModule)
|
|
20
|
+
# # Raises: Servactory::Stroma::Exceptions::KeyAlreadyRegistered
|
|
21
|
+
# ```
|
|
22
|
+
#
|
|
23
|
+
# ## Integration
|
|
24
|
+
#
|
|
25
|
+
# This exception typically indicates a configuration error - each
|
|
26
|
+
# DSL module should only be registered once. Check for duplicate
|
|
27
|
+
# registrations in your initialization code.
|
|
28
|
+
class KeyAlreadyRegistered < Base
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Stroma
|
|
5
|
+
module Exceptions
|
|
6
|
+
# Raised when attempting to modify a finalized registry
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Indicates that the Stroma::Registry has been finalized and cannot accept
|
|
11
|
+
# new module registrations. The registry is finalized once during gem
|
|
12
|
+
# initialization and remains immutable thereafter.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# Raised when attempting to register modules after finalize!:
|
|
17
|
+
#
|
|
18
|
+
# ```ruby
|
|
19
|
+
# Servactory::Stroma::Registry.finalize!
|
|
20
|
+
# Servactory::Stroma::Registry.register(:custom, CustomModule)
|
|
21
|
+
# # Raises: Servactory::Stroma::Exceptions::RegistryFrozen
|
|
22
|
+
# ```
|
|
23
|
+
#
|
|
24
|
+
# ## Integration
|
|
25
|
+
#
|
|
26
|
+
# This exception typically indicates a programming error - module
|
|
27
|
+
# registration should only occur during application boot, before
|
|
28
|
+
# any service classes are defined.
|
|
29
|
+
class RegistryFrozen < Base
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Stroma
|
|
5
|
+
module Exceptions
|
|
6
|
+
# Raised when accessing registry before finalization
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Indicates that the Stroma::Registry was accessed before finalize! was called.
|
|
11
|
+
# The registry must be finalized before it can be used to ensure all DSL modules
|
|
12
|
+
# are registered in the correct order.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# Raised when accessing registry methods before finalization:
|
|
17
|
+
#
|
|
18
|
+
# ```ruby
|
|
19
|
+
# # Before finalize! is called
|
|
20
|
+
# Servactory::Stroma::Registry.entries
|
|
21
|
+
# # Raises: Servactory::Stroma::Exceptions::RegistryNotFinalized
|
|
22
|
+
# ```
|
|
23
|
+
#
|
|
24
|
+
# ## Integration
|
|
25
|
+
#
|
|
26
|
+
# This exception typically indicates that Servactory::DSL module was not
|
|
27
|
+
# properly loaded. Ensure servactory gem is properly required before
|
|
28
|
+
# defining service classes.
|
|
29
|
+
class RegistryNotFinalized < Base
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Stroma
|
|
5
|
+
module Exceptions
|
|
6
|
+
# Raised when using an invalid hook target key
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Indicates that an unknown key was used as a hook target in the
|
|
11
|
+
# extensions block. Only registered DSL module keys can be used
|
|
12
|
+
# as hook targets.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# Raised when using an invalid key in extensions block:
|
|
17
|
+
#
|
|
18
|
+
# ```ruby
|
|
19
|
+
# class ApplicationService::Base
|
|
20
|
+
# include Servactory::DSL
|
|
21
|
+
#
|
|
22
|
+
# extensions do
|
|
23
|
+
# before :unknown_key, SomeModule
|
|
24
|
+
# # Raises: Servactory::Stroma::Exceptions::UnknownHookTarget
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
27
|
+
# ```
|
|
28
|
+
#
|
|
29
|
+
# ## Integration
|
|
30
|
+
#
|
|
31
|
+
# Valid hook target keys are determined by registered DSL modules:
|
|
32
|
+
# :configuration, :info, :context, :inputs, :internals, :outputs, :actions
|
|
33
|
+
#
|
|
34
|
+
# Check Servactory::Stroma::Registry.keys for the list of valid targets.
|
|
35
|
+
class UnknownHookTarget < Base
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Stroma
|
|
5
|
+
module Hooks
|
|
6
|
+
# Applies registered hooks to a target class.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Iterates through all registered DSL modules and includes corresponding
|
|
11
|
+
# before/after hooks in the target class. For each registry entry,
|
|
12
|
+
# before hooks are included first, then after hooks.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# ```ruby
|
|
17
|
+
# applier = Servactory::Stroma::Hooks::Applier.new(ChildService, hooks)
|
|
18
|
+
# applier.apply!
|
|
19
|
+
# # ChildService now includes all hook modules
|
|
20
|
+
# ```
|
|
21
|
+
#
|
|
22
|
+
# ## Integration
|
|
23
|
+
#
|
|
24
|
+
# Called by Servactory::Stroma::DSL.inherited after duplicating
|
|
25
|
+
# parent's configuration. Uses Registry.entries to determine
|
|
26
|
+
# hook application order.
|
|
27
|
+
class Applier
|
|
28
|
+
# Creates a new applier for applying hooks to a class.
|
|
29
|
+
#
|
|
30
|
+
# @param target_class [Class] The class to apply hooks to
|
|
31
|
+
# @param hooks [Collection] The hooks collection to apply
|
|
32
|
+
def initialize(target_class, hooks)
|
|
33
|
+
@target_class = target_class
|
|
34
|
+
@hooks = hooks
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Applies all registered hooks to the target class.
|
|
38
|
+
#
|
|
39
|
+
# For each registry entry, includes before hooks first,
|
|
40
|
+
# then after hooks. Does nothing if hooks collection is empty.
|
|
41
|
+
#
|
|
42
|
+
# @return [void]
|
|
43
|
+
#
|
|
44
|
+
# @example
|
|
45
|
+
# applier.apply!
|
|
46
|
+
# # Target class now includes all extension modules
|
|
47
|
+
def apply!
|
|
48
|
+
return if @hooks.empty?
|
|
49
|
+
|
|
50
|
+
Registry.entries.each do |entry|
|
|
51
|
+
@hooks.before(entry.key).each do |hook|
|
|
52
|
+
@target_class.include(hook.extension)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
@hooks.after(entry.key).each do |hook|
|
|
56
|
+
@target_class.include(hook.extension)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Stroma
|
|
5
|
+
module Hooks
|
|
6
|
+
# Mutable collection manager for Hook objects.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Stores Hook objects and provides query methods to retrieve hooks
|
|
11
|
+
# by type and target key. Supports proper duplication during class
|
|
12
|
+
# inheritance to ensure configuration isolation.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# ```ruby
|
|
17
|
+
# hooks = Servactory::Stroma::Hooks::Collection.new
|
|
18
|
+
# hooks.add(:before, :actions, MyModule)
|
|
19
|
+
# hooks.add(:after, :actions, AnotherModule)
|
|
20
|
+
#
|
|
21
|
+
# hooks.before(:actions) # => [Hook(...)]
|
|
22
|
+
# hooks.after(:actions) # => [Hook(...)]
|
|
23
|
+
# hooks.empty? # => false
|
|
24
|
+
# hooks.size # => 2
|
|
25
|
+
# ```
|
|
26
|
+
#
|
|
27
|
+
# ## Integration
|
|
28
|
+
#
|
|
29
|
+
# Stored in Servactory::Stroma::State and used by
|
|
30
|
+
# Servactory::Stroma::Hooks::Applier to apply hooks to classes.
|
|
31
|
+
# Properly duplicated during class inheritance via initialize_dup.
|
|
32
|
+
class Collection
|
|
33
|
+
extend Forwardable
|
|
34
|
+
|
|
35
|
+
# @!method each
|
|
36
|
+
# Iterates over all hooks in the collection.
|
|
37
|
+
# @yield [Hook] Each hook in the collection
|
|
38
|
+
# @!method map
|
|
39
|
+
# Maps over all hooks in the collection.
|
|
40
|
+
# @yield [Hook] Each hook in the collection
|
|
41
|
+
# @return [Array] Mapped results
|
|
42
|
+
# @!method size
|
|
43
|
+
# Returns the number of hooks in the collection.
|
|
44
|
+
# @return [Integer] Number of hooks
|
|
45
|
+
# @!method empty?
|
|
46
|
+
# Checks if the collection is empty.
|
|
47
|
+
# @return [Boolean] true if no hooks registered
|
|
48
|
+
def_delegators :@collection, :each, :map, :size, :empty?
|
|
49
|
+
|
|
50
|
+
# Creates a new hooks collection.
|
|
51
|
+
#
|
|
52
|
+
# @param collection [Set] Initial collection of hooks (default: empty Set)
|
|
53
|
+
def initialize(collection = Set.new)
|
|
54
|
+
@collection = collection
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Creates a deep copy during inheritance.
|
|
58
|
+
#
|
|
59
|
+
# @param original [Collection] The original collection being duplicated
|
|
60
|
+
# @return [void]
|
|
61
|
+
def initialize_dup(original)
|
|
62
|
+
super
|
|
63
|
+
@collection = original.instance_variable_get(:@collection).dup
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Adds a new hook to the collection.
|
|
67
|
+
#
|
|
68
|
+
# @param type [Symbol] Hook type (:before or :after)
|
|
69
|
+
# @param target_key [Symbol] Registry key to hook into
|
|
70
|
+
# @param extension [Module] Extension module to include
|
|
71
|
+
# @return [Set] The updated collection
|
|
72
|
+
#
|
|
73
|
+
# @example
|
|
74
|
+
# hooks.add(:before, :actions, ValidationModule)
|
|
75
|
+
def add(type, target_key, extension)
|
|
76
|
+
@collection << Hook.new(type:, target_key:, extension:)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Returns all before hooks for a given key.
|
|
80
|
+
#
|
|
81
|
+
# @param key [Symbol] The target key to filter by
|
|
82
|
+
# @return [Array<Hook>] Hooks that run before the target
|
|
83
|
+
#
|
|
84
|
+
# @example
|
|
85
|
+
# hooks.before(:actions) # => [Hook(type: :before, ...)]
|
|
86
|
+
def before(key)
|
|
87
|
+
@collection.select { |hook| hook.before? && hook.target_key == key }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Returns all after hooks for a given key.
|
|
91
|
+
#
|
|
92
|
+
# @param key [Symbol] The target key to filter by
|
|
93
|
+
# @return [Array<Hook>] Hooks that run after the target
|
|
94
|
+
#
|
|
95
|
+
# @example
|
|
96
|
+
# hooks.after(:actions) # => [Hook(type: :after, ...)]
|
|
97
|
+
def after(key)
|
|
98
|
+
@collection.select { |hook| hook.after? && hook.target_key == key }
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Stroma
|
|
5
|
+
module Hooks
|
|
6
|
+
# DSL interface for registering hooks in extensions block.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Provides the `before` and `after` methods used within the extensions
|
|
11
|
+
# block to register hooks. Validates that target keys exist in Registry
|
|
12
|
+
# before adding hooks.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# ```ruby
|
|
17
|
+
# class ApplicationService::Base < Servactory::Base
|
|
18
|
+
# extensions do
|
|
19
|
+
# before :actions, ValidationModule
|
|
20
|
+
# after :outputs, LoggingModule
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
# ```
|
|
24
|
+
#
|
|
25
|
+
# ## Integration
|
|
26
|
+
#
|
|
27
|
+
# Created by DSL.extensions method and receives instance_eval of the block.
|
|
28
|
+
# Validates keys against Registry.keys and raises UnknownHookTarget
|
|
29
|
+
# for invalid keys.
|
|
30
|
+
class Factory
|
|
31
|
+
# Creates a new factory for registering hooks.
|
|
32
|
+
#
|
|
33
|
+
# @param hooks [Collection] The hooks collection to add to
|
|
34
|
+
def initialize(hooks)
|
|
35
|
+
@hooks = hooks
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Registers one or more before hooks for a target key.
|
|
39
|
+
#
|
|
40
|
+
# @param key [Symbol] The registry key to hook before
|
|
41
|
+
# @param extensions [Array<Module>] Extension modules to include
|
|
42
|
+
# @raise [Exceptions::UnknownHookTarget] If key is not registered
|
|
43
|
+
#
|
|
44
|
+
# @example
|
|
45
|
+
# before :actions, ValidationModule, AuthorizationModule
|
|
46
|
+
def before(key, *extensions)
|
|
47
|
+
validate_key!(key)
|
|
48
|
+
extensions.each { |extension| @hooks.add(:before, key, extension) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Registers one or more after hooks for a target key.
|
|
52
|
+
#
|
|
53
|
+
# @param key [Symbol] The registry key to hook after
|
|
54
|
+
# @param extensions [Array<Module>] Extension modules to include
|
|
55
|
+
# @raise [Exceptions::UnknownHookTarget] If key is not registered
|
|
56
|
+
#
|
|
57
|
+
# @example
|
|
58
|
+
# after :outputs, LoggingModule, AuditModule
|
|
59
|
+
def after(key, *extensions)
|
|
60
|
+
validate_key!(key)
|
|
61
|
+
extensions.each { |extension| @hooks.add(:after, key, extension) }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
# Validates that the key exists in the Registry.
|
|
67
|
+
#
|
|
68
|
+
# @param key [Symbol] The key to validate
|
|
69
|
+
# @raise [Exceptions::UnknownHookTarget] If key is not registered
|
|
70
|
+
def validate_key!(key)
|
|
71
|
+
return if Registry.key?(key)
|
|
72
|
+
|
|
73
|
+
raise Exceptions::UnknownHookTarget,
|
|
74
|
+
"Unknown hook target: #{key.inspect}. " \
|
|
75
|
+
"Valid keys: #{Registry.keys.map(&:inspect).join(', ')}"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|