stroma 0.3.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27a735955a54fe9ea248a066afbdf10edaa55bc903b83574e84d405fb01fa020
4
- data.tar.gz: e63528c3ac53e3a32a757493745dac02ee46958a486b184cda3d3d4fc4463c6f
3
+ metadata.gz: 73bb516679b54a0f43b33efffc0719d76211dda900024976ef6398e0b0911564
4
+ data.tar.gz: 34301bc64ab57e7f5f3b0fbf9bd9d71d3a23887d83dd652706b5184d4d6237a4
5
5
  SHA512:
6
- metadata.gz: 19f5d6847f269dfe65070f267426b68ec7a61b72cdfde478ec3542ed9d75a232465c32412eab4bb8d432e4fb648962b309a5d7dc450ec2b14ce329506a86e55c
7
- data.tar.gz: fc4c1300028fa543f2d097d70dcba472644837579a73d58235e51215ac7792d1ce0a1e423f3daac97dac1bcbcad0ab48642f3b63ae663dade353e505bd59466d
6
+ metadata.gz: 2ee5cfa5a9f5f54aa8cb2b713a29cf1802abb6b98cd70f2e89eb3697652ce631e4f77017c0c8207309877e7e2289275caeabcda123d48cebd1527a0e397185d8
7
+ data.tar.gz: d8f0362ad411eea72508423e3a931e360924c996d88c011924a6ca0d483d45ddbdbd83bd590fc90cc5b466785bcac232c853a9dcc40fc8eefbfb154b90444180
data/README.md CHANGED
@@ -39,32 +39,27 @@ Building modular DSLs shouldn't require reinventing the wheel. Stroma provides a
39
39
  Stroma is a foundation for library authors building DSL-driven frameworks (service objects, form objects, decorators, etc.).
40
40
 
41
41
  **Core lifecycle:**
42
- 1. **Register** - Define DSL modules at boot time via `Stroma::Registry`
43
- 2. **Compose** - Classes include `Stroma::DSL` to gain all registered modules automatically
44
- 3. **Extend** (optional) - Users can add cross-cutting logic via `before`/`after` hooks
42
+ 1. **Define** - Create a Matrix with DSL modules at boot time
43
+ 2. **Include** - Classes include the matrix's DSL to gain all modules
44
+ 3. **Extend** (optional) - Add cross-cutting logic via `before`/`after` hooks
45
45
 
46
46
  ## 🚀 Quick Start
47
47
 
48
48
  ### Installation
49
49
 
50
50
  ```ruby
51
- spec.add_dependency "stroma", ">= 0.3"
51
+ spec.add_dependency "stroma", ">= 0.4"
52
52
  ```
53
53
 
54
54
  ### Define your library's DSL
55
55
 
56
56
  ```ruby
57
57
  module MyLib
58
- module DSL
59
- # Register DSL modules at load time
60
- Stroma::Registry.register(:inputs, MyLib::Inputs::DSL)
61
- Stroma::Registry.register(:actions, MyLib::Actions::DSL)
62
- Stroma::Registry.finalize!
63
-
64
- def self.included(base)
65
- base.include(Stroma::DSL)
66
- end
58
+ STROMA = Stroma::Matrix.define(:my_lib) do
59
+ register :inputs, MyLib::Inputs::DSL
60
+ register :actions, MyLib::Actions::DSL
67
61
  end
62
+ private_constant :STROMA
68
63
  end
69
64
  ```
70
65
 
@@ -73,7 +68,7 @@ end
73
68
  ```ruby
74
69
  module MyLib
75
70
  class Base
76
- include MyLib::DSL
71
+ include STROMA.dsl
77
72
  end
78
73
  end
79
74
  ```
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stroma
4
+ module DSL
5
+ # Generates a DSL module scoped to a specific Matrix.
6
+ #
7
+ # ## Purpose
8
+ #
9
+ # Creates a module that:
10
+ # - Stores matrix reference on the module itself
11
+ # - Defines ClassMethods for service classes
12
+ # - Handles inheritance with state duplication
13
+ #
14
+ # Memory model:
15
+ # - Matrix owns @dsl_module (generated once, cached)
16
+ # - ServiceClass gets @stroma_matrix (same reference)
17
+ # - ServiceClass gets @stroma (unique State per class)
18
+ #
19
+ # ## Usage
20
+ #
21
+ # ```ruby
22
+ # # Called internally by Matrix#dsl
23
+ # dsl_module = Stroma::DSL::Generator.call(matrix)
24
+ #
25
+ # # The generated module is included in base classes
26
+ # class MyLib::Base
27
+ # include dsl_module
28
+ # end
29
+ # ```
30
+ #
31
+ # ## Integration
32
+ #
33
+ # Called by Matrix#dsl to generate the DSL module.
34
+ # Generated module includes all registered extensions.
35
+ class Generator
36
+ class << self
37
+ # Generates a DSL module for the given matrix.
38
+ #
39
+ # @param matrix [Matrix] The matrix to generate DSL for
40
+ # @return [Module] The generated DSL module
41
+ def call(matrix)
42
+ new(matrix).generate
43
+ end
44
+ end
45
+
46
+ # Creates a new generator for the given matrix.
47
+ #
48
+ # @param matrix [Matrix] The matrix to generate DSL for
49
+ def initialize(matrix)
50
+ @matrix = matrix
51
+ end
52
+
53
+ # Generates the DSL module.
54
+ #
55
+ # Creates a module with ClassMethods that provides:
56
+ # - stroma_matrix accessor for matrix reference
57
+ # - stroma accessor for per-class state
58
+ # - inherited hook for state duplication
59
+ # - extensions DSL for registering hooks
60
+ #
61
+ # @return [Module] The generated DSL module
62
+ def generate # rubocop:disable Metrics/MethodLength
63
+ matrix = @matrix
64
+ class_methods = build_class_methods
65
+
66
+ Module.new do
67
+ @stroma_matrix = matrix
68
+
69
+ class << self
70
+ attr_reader :stroma_matrix
71
+
72
+ def included(base)
73
+ mtx = stroma_matrix
74
+ base.extend(self::ClassMethods)
75
+ base.instance_variable_set(:@stroma_matrix, mtx)
76
+ base.instance_variable_set(:@stroma, State.new)
77
+
78
+ mtx.entries.each { |entry| base.include(entry.extension) }
79
+ end
80
+ end
81
+
82
+ const_set(:ClassMethods, class_methods)
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ # Builds the ClassMethods module.
89
+ #
90
+ # @return [Module] The ClassMethods module
91
+ def build_class_methods # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
92
+ Module.new do
93
+ attr_reader :stroma_matrix
94
+
95
+ def stroma
96
+ @stroma ||= State.new
97
+ end
98
+
99
+ def inherited(child)
100
+ super
101
+ child.instance_variable_set(:@stroma_matrix, stroma_matrix)
102
+ child.instance_variable_set(:@stroma, stroma.dup)
103
+ Hooks::Applier.apply!(child, child.stroma.hooks, stroma_matrix)
104
+ end
105
+
106
+ private
107
+
108
+ def extensions(&block)
109
+ @stroma_hooks_factory ||= Hooks::Factory.new(stroma.hooks, stroma_matrix)
110
+ @stroma_hooks_factory.instance_eval(&block)
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -6,31 +6,46 @@ module Stroma
6
6
  #
7
7
  # ## Purpose
8
8
  #
9
- # Iterates through all registered DSL modules and includes corresponding
10
- # before/after hooks in the target class. For each registry entry,
11
- # before hooks are included first, then after hooks.
9
+ # Includes hook extension modules into target class.
10
+ # Maintains order based on matrix registry entries.
11
+ # For each entry: before hooks first, then after hooks.
12
12
  #
13
13
  # ## Usage
14
14
  #
15
15
  # ```ruby
16
- # applier = Stroma::Hooks::Applier.new(ChildService, hooks)
16
+ # # Called internally during class inheritance
17
+ # applier = Stroma::Hooks::Applier.new(ChildService, hooks, matrix)
17
18
  # applier.apply!
18
- # # ChildService now includes all hook modules
19
19
  # ```
20
20
  #
21
21
  # ## Integration
22
22
  #
23
- # Called by Stroma::DSL.inherited after duplicating
24
- # parent's configuration. Uses Registry.entries to determine
25
- # hook application order.
23
+ # Called by DSL::Generator's inherited hook.
24
+ # Creates a temporary instance that is garbage collected after apply!.
26
25
  class Applier
26
+ class << self
27
+ # Applies all registered hooks to the target class.
28
+ #
29
+ # Convenience class method that creates an applier and applies hooks.
30
+ #
31
+ # @param target_class [Class] The class to apply hooks to
32
+ # @param hooks [Collection] The hooks collection to apply
33
+ # @param matrix [Matrix] The matrix providing registry entries
34
+ # @return [void]
35
+ def apply!(target_class, hooks, matrix)
36
+ new(target_class, hooks, matrix).apply!
37
+ end
38
+ end
39
+
27
40
  # Creates a new applier for applying hooks to a class.
28
41
  #
29
42
  # @param target_class [Class] The class to apply hooks to
30
43
  # @param hooks [Collection] The hooks collection to apply
31
- def initialize(target_class, hooks)
44
+ # @param matrix [Matrix] The matrix providing registry entries
45
+ def initialize(target_class, hooks, matrix)
32
46
  @target_class = target_class
33
47
  @hooks = hooks
48
+ @matrix = matrix
34
49
  end
35
50
 
36
51
  # Applies all registered hooks to the target class.
@@ -39,21 +54,12 @@ module Stroma
39
54
  # then after hooks. Does nothing if hooks collection is empty.
40
55
  #
41
56
  # @return [void]
42
- #
43
- # @example
44
- # applier.apply!
45
- # # Target class now includes all extension modules
46
57
  def apply!
47
58
  return if @hooks.empty?
48
59
 
49
- Registry.entries.each do |entry|
50
- @hooks.before(entry.key).each do |hook|
51
- @target_class.include(hook.extension)
52
- end
53
-
54
- @hooks.after(entry.key).each do |hook|
55
- @target_class.include(hook.extension)
56
- end
60
+ @matrix.entries.each do |entry|
61
+ @hooks.before(entry.key).each { |hook| @target_class.include(hook.extension) }
62
+ @hooks.after(entry.key).each { |hook| @target_class.include(hook.extension) }
57
63
  end
58
64
  end
59
65
  end
@@ -6,21 +6,16 @@ module Stroma
6
6
  #
7
7
  # ## Purpose
8
8
  #
9
- # Provides the `before` and `after` methods used within the extensions
10
- # block to register hooks. Validates that target keys exist in Registry
11
- # before adding hooks.
9
+ # Provides before/after DSL methods for hook registration.
10
+ # Validates target keys against the matrix's registry.
11
+ # Delegates to Hooks::Collection for storage.
12
12
  #
13
13
  # ## Usage
14
14
  #
15
- # Used within `extensions` block in classes that include Stroma::DSL:
16
- #
17
15
  # ```ruby
18
- # # Library Base class (includes Stroma::DSL via library's DSL module)
19
- # class MyLib::Base
20
- # include MyLib::DSL # MyLib::DSL includes Stroma::DSL
21
- #
16
+ # class MyService < MyLib::Base
22
17
  # extensions do
23
- # before :actions, ValidationModule
18
+ # before :actions, ValidationModule, AuthModule
24
19
  # after :outputs, LoggingModule
25
20
  # end
26
21
  # end
@@ -28,15 +23,16 @@ module Stroma
28
23
  #
29
24
  # ## Integration
30
25
  #
31
- # Created by DSL.extensions method and receives instance_eval of the block.
32
- # Validates keys against Registry.keys and raises UnknownHookTarget
33
- # for invalid keys.
26
+ # Created by DSL::Generator's extensions method.
27
+ # Cached as @stroma_hooks_factory on each service class.
34
28
  class Factory
35
29
  # Creates a new factory for registering hooks.
36
30
  #
37
31
  # @param hooks [Collection] The hooks collection to add to
38
- def initialize(hooks)
32
+ # @param matrix [Matrix] The matrix providing valid keys
33
+ def initialize(hooks, matrix)
39
34
  @hooks = hooks
35
+ @matrix = matrix
40
36
  end
41
37
 
42
38
  # Registers one or more before hooks for a target key.
@@ -44,6 +40,7 @@ module Stroma
44
40
  # @param key [Symbol] The registry key to hook before
45
41
  # @param extensions [Array<Module>] Extension modules to include
46
42
  # @raise [Exceptions::UnknownHookTarget] If key is not registered
43
+ # @return [void]
47
44
  #
48
45
  # @example
49
46
  # before :actions, ValidationModule, AuthorizationModule
@@ -57,6 +54,7 @@ module Stroma
57
54
  # @param key [Symbol] The registry key to hook after
58
55
  # @param extensions [Array<Module>] Extension modules to include
59
56
  # @raise [Exceptions::UnknownHookTarget] If key is not registered
57
+ # @return [void]
60
58
  #
61
59
  # @example
62
60
  # after :outputs, LoggingModule, AuditModule
@@ -67,16 +65,17 @@ module Stroma
67
65
 
68
66
  private
69
67
 
70
- # Validates that the key exists in the Registry.
68
+ # Validates that the key exists in the matrix's registry.
71
69
  #
72
70
  # @param key [Symbol] The key to validate
73
71
  # @raise [Exceptions::UnknownHookTarget] If key is not registered
72
+ # @return [void]
74
73
  def validate_key!(key)
75
- return if Registry.key?(key)
74
+ return if @matrix.key?(key)
76
75
 
77
76
  raise Exceptions::UnknownHookTarget,
78
- "Unknown hook target: #{key.inspect}. " \
79
- "Valid keys: #{Registry.keys.map(&:inspect).join(', ')}"
77
+ "Unknown hook target #{key.inspect} for #{@matrix.name.inspect}. " \
78
+ "Valid: #{@matrix.keys.map(&:inspect).join(', ')}"
80
79
  end
81
80
  end
82
81
  end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stroma
4
+ # Main entry point for libraries using Stroma.
5
+ #
6
+ # ## Purpose
7
+ #
8
+ # Creates an isolated registry and generates a scoped DSL module.
9
+ # Each matrix has its own registry - no conflicts with other libraries.
10
+ #
11
+ # Lifecycle:
12
+ # - Boot time: Matrix.define creates Registry, registers extensions
13
+ # - Boot time: finalize! freezes registry, dsl generates Module
14
+ # - Boot time: freeze makes Matrix immutable
15
+ # - Runtime: All structures frozen, no allocations
16
+ #
17
+ # ## Usage
18
+ #
19
+ # ```ruby
20
+ # module MyLib
21
+ # STROMA = Stroma::Matrix.define(:my_lib) do
22
+ # register :inputs, Inputs::DSL
23
+ # register :outputs, Outputs::DSL
24
+ # end
25
+ # private_constant :STROMA
26
+ # end
27
+ #
28
+ # class MyLib::Base
29
+ # include MyLib::STROMA.dsl
30
+ # end
31
+ # ```
32
+ #
33
+ # ## Integration
34
+ #
35
+ # Stored as a constant in the library's namespace.
36
+ # Owns the Registry and generates DSL module via DSL::Generator.
37
+ class Matrix
38
+ class << self
39
+ # Defines a new Matrix with given name.
40
+ #
41
+ # Preferred way to create a Matrix. Semantically indicates
42
+ # that we are defining an immutable DSL scope.
43
+ #
44
+ # @param name [Symbol, String] The matrix identifier
45
+ # @yield Block for registering DSL modules
46
+ # @return [Matrix] The frozen matrix instance
47
+ #
48
+ # @example
49
+ # STROMA = Stroma::Matrix.define(:my_lib) do
50
+ # register :inputs, Inputs::DSL
51
+ # register :outputs, Outputs::DSL
52
+ # end
53
+ def define(name, &block)
54
+ new(name, &block)
55
+ end
56
+ end
57
+
58
+ # @!attribute [r] name
59
+ # @return [Symbol] The matrix identifier
60
+ # @!attribute [r] registry
61
+ # @return [Registry] The registry of DSL modules
62
+ # @!attribute [r] dsl
63
+ # @return [Module] The DSL module to include in base classes
64
+ attr_reader :name, :registry, :dsl
65
+
66
+ # Creates a new Matrix with given name.
67
+ #
68
+ # Evaluates the block to register DSL modules, then finalizes
69
+ # the registry and freezes the matrix.
70
+ #
71
+ # @param name [Symbol, String] The matrix identifier
72
+ # @yield Block for registering DSL modules
73
+ def initialize(name, &block)
74
+ @name = name.to_sym
75
+ @registry = Registry.new(@name)
76
+
77
+ instance_eval(&block) if block_given?
78
+ @registry.finalize!
79
+ @dsl = DSL::Generator.call(self)
80
+ freeze
81
+ end
82
+
83
+ # Registers a DSL module with the given key.
84
+ #
85
+ # @param key [Symbol] The registry key
86
+ # @param extension [Module] The DSL module to register
87
+ # @return [void]
88
+ def register(key, extension)
89
+ @registry.register(key, extension)
90
+ end
91
+
92
+ # Returns all registered entries.
93
+ #
94
+ # @return [Array<Entry>] The registry entries
95
+ def entries
96
+ registry.entries
97
+ end
98
+
99
+ # Returns all registered keys.
100
+ #
101
+ # @return [Array<Symbol>] The registry keys
102
+ def keys
103
+ registry.keys
104
+ end
105
+
106
+ # Checks if a key is registered.
107
+ #
108
+ # @param key [Symbol] The key to check
109
+ # @return [Boolean] true if the key is registered
110
+ def key?(key)
111
+ registry.key?(key)
112
+ end
113
+ end
114
+ end
@@ -1,63 +1,71 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stroma
4
- # Manages global registration of DSL modules for Stroma.
4
+ # Manages registration of DSL modules for a specific matrix.
5
5
  #
6
6
  # ## Purpose
7
7
  #
8
- # Singleton registry that stores all DSL modules that will be included
9
- # in service classes. Implements two-phase lifecycle: registration
10
- # followed by finalization.
8
+ # Stores DSL module entries with their keys.
9
+ # Implements two-phase lifecycle: registration → finalization.
10
+ # Each Matrix has its own Registry - no global state.
11
11
  #
12
12
  # ## Usage
13
13
  #
14
14
  # ```ruby
15
- # # During gem initialization:
16
- # Stroma::Registry.register(:inputs, Inputs::DSL)
17
- # Stroma::Registry.register(:outputs, Outputs::DSL)
18
- # Stroma::Registry.finalize!
15
+ # registry = Stroma::Registry.new(:my_lib)
16
+ # registry.register(:inputs, Inputs::DSL)
17
+ # registry.register(:outputs, Outputs::DSL)
18
+ # registry.finalize!
19
19
  #
20
- # # After finalization:
21
- # Stroma::Registry.keys # => [:inputs, :outputs]
22
- # Stroma::Registry.key?(:inputs) # => true
20
+ # registry.keys # => [:inputs, :outputs]
21
+ # registry.key?(:inputs) # => true
23
22
  # ```
24
23
  #
25
24
  # ## Integration
26
25
  #
27
- # Used by Stroma::DSL to include all registered modules in service classes.
28
- # Used by Stroma::Hooks::Factory to validate hook target keys.
29
- #
30
- # ## Thread Safety
31
- #
32
- # Registration must occur during single-threaded boot phase.
33
- # After finalization, all read operations are thread-safe.
26
+ # Created and owned by Matrix.
27
+ # Entries are accessed via Matrix#entries and Matrix#keys.
34
28
  class Registry
35
- include Singleton
29
+ # @!attribute [r] matrix_name
30
+ # @return [Symbol] The name of the owning matrix
31
+ attr_reader :matrix_name
36
32
 
37
- class << self
38
- delegate :register,
39
- :finalize!,
40
- :entries,
41
- :keys,
42
- :key?,
43
- to: :instance
44
- end
45
-
46
- def initialize
33
+ # Creates a new registry for the given matrix.
34
+ #
35
+ # @param matrix_name [Symbol, String] The matrix identifier
36
+ def initialize(matrix_name)
37
+ @matrix_name = matrix_name.to_sym
47
38
  @entries = []
48
39
  @finalized = false
49
40
  end
50
41
 
42
+ # Registers a DSL module with the given key.
43
+ #
44
+ # @param key [Symbol, String] The registry key
45
+ # @param extension [Module] The DSL module to register
46
+ # @raise [Exceptions::RegistryFrozen] If registry is finalized
47
+ # @raise [Exceptions::KeyAlreadyRegistered] If key already exists
48
+ # @return [void]
51
49
  def register(key, extension)
52
- raise Exceptions::RegistryFrozen, "Registry is finalized" if @finalized
50
+ if @finalized
51
+ raise Exceptions::RegistryFrozen,
52
+ "Registry for #{@matrix_name.inspect} is finalized"
53
+ end
53
54
 
55
+ key = key.to_sym
54
56
  if @entries.any? { |e| e.key == key }
55
- raise Exceptions::KeyAlreadyRegistered, "Key #{key.inspect} already registered"
57
+ raise Exceptions::KeyAlreadyRegistered,
58
+ "Key #{key.inspect} already registered in #{@matrix_name.inspect}"
56
59
  end
57
60
 
58
61
  @entries << Entry.new(key:, extension:)
59
62
  end
60
63
 
64
+ # Finalizes the registry, preventing further registrations.
65
+ #
66
+ # Idempotent - can be called multiple times safely.
67
+ #
68
+ # @return [void]
61
69
  def finalize!
62
70
  return if @finalized
63
71
 
@@ -65,28 +73,45 @@ module Stroma
65
73
  @finalized = true
66
74
  end
67
75
 
76
+ # Returns all registered entries.
77
+ #
78
+ # @raise [Exceptions::RegistryNotFinalized] If not finalized
79
+ # @return [Array<Entry>] The registry entries
68
80
  def entries
69
81
  ensure_finalized!
70
82
  @entries
71
83
  end
72
84
 
85
+ # Returns all registered keys.
86
+ #
87
+ # @raise [Exceptions::RegistryNotFinalized] If not finalized
88
+ # @return [Array<Symbol>] The registry keys
73
89
  def keys
74
90
  ensure_finalized!
75
91
  @entries.map(&:key)
76
92
  end
77
93
 
94
+ # Checks if a key is registered.
95
+ #
96
+ # @param key [Symbol, String] The key to check
97
+ # @raise [Exceptions::RegistryNotFinalized] If not finalized
98
+ # @return [Boolean] true if the key is registered
78
99
  def key?(key)
79
100
  ensure_finalized!
80
- @entries.any? { |e| e.key == key }
101
+ @entries.any? { |e| e.key == key.to_sym }
81
102
  end
82
103
 
83
104
  private
84
105
 
106
+ # Ensures the registry is finalized.
107
+ #
108
+ # @raise [Exceptions::RegistryNotFinalized] If not finalized
109
+ # @return [void]
85
110
  def ensure_finalized!
86
111
  return if @finalized
87
112
 
88
113
  raise Exceptions::RegistryNotFinalized,
89
- "Registry not finalized. Call Stroma::Registry.finalize! after registration."
114
+ "Registry for #{@matrix_name.inspect} not finalized"
90
115
  end
91
116
  end
92
117
  end
@@ -3,7 +3,7 @@
3
3
  module Stroma
4
4
  module VERSION
5
5
  MAJOR = 0
6
- MINOR = 3
6
+ MINOR = 4
7
7
  PATCH = 0
8
8
  PRE = nil
9
9
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stroma
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Sokolov
@@ -132,7 +132,7 @@ files:
132
132
  - README.md
133
133
  - Rakefile
134
134
  - lib/stroma.rb
135
- - lib/stroma/dsl.rb
135
+ - lib/stroma/dsl/generator.rb
136
136
  - lib/stroma/engine.rb
137
137
  - lib/stroma/entry.rb
138
138
  - lib/stroma/exceptions/base.rb
@@ -145,6 +145,7 @@ files:
145
145
  - lib/stroma/hooks/collection.rb
146
146
  - lib/stroma/hooks/factory.rb
147
147
  - lib/stroma/hooks/hook.rb
148
+ - lib/stroma/matrix.rb
148
149
  - lib/stroma/registry.rb
149
150
  - lib/stroma/settings/collection.rb
150
151
  - lib/stroma/settings/registry_settings.rb
@@ -173,7 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
173
174
  - !ruby/object:Gem::Version
174
175
  version: '0'
175
176
  requirements: []
176
- rubygems_version: 3.7.2
177
+ rubygems_version: 3.6.9
177
178
  specification_version: 4
178
179
  summary: Foundation for building modular, extensible DSLs
179
180
  test_files: []
data/lib/stroma/dsl.rb DELETED
@@ -1,124 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Stroma
4
- # Main integration point between Stroma and service classes.
5
- #
6
- # ## Purpose
7
- #
8
- # Module that provides the core Stroma functionality to service classes:
9
- # - Includes all registered DSL modules
10
- # - Provides extensions block for hook registration
11
- # - Handles inheritance with proper state copying
12
- #
13
- # ## Usage
14
- #
15
- # Library authors create a DSL module that includes Stroma::DSL:
16
- #
17
- # ```ruby
18
- # module MyLib::DSL
19
- # def self.included(base)
20
- # base.include(Stroma::DSL)
21
- # end
22
- # end
23
- #
24
- # class MyLib::Base
25
- # include MyLib::DSL
26
- #
27
- # extensions do
28
- # before :actions, MyExtension
29
- # end
30
- # end
31
- # ```
32
- #
33
- # ## Extension Settings Access
34
- #
35
- # Extensions access their settings through the stroma.settings hierarchy:
36
- #
37
- # ```ruby
38
- # # In ClassMethods:
39
- # stroma.settings[:actions][:authorization][:method_name] = :authorize
40
- #
41
- # # In InstanceMethods:
42
- # self.class.stroma.settings[:actions][:authorization][:method_name]
43
- # ```
44
- #
45
- # ## Integration
46
- #
47
- # Included by service classes that want Stroma hook functionality.
48
- # Provides ClassMethods with: stroma, inherited, extensions.
49
- module DSL
50
- def self.included(base)
51
- base.extend(ClassMethods)
52
-
53
- Registry.entries.each do |entry|
54
- base.include(entry.extension)
55
- end
56
- end
57
-
58
- # Class-level methods for Stroma integration.
59
- #
60
- # ## Purpose
61
- #
62
- # Provides access to Stroma state and hooks DSL at the class level.
63
- # Handles proper duplication during inheritance.
64
- #
65
- # ## Key Methods
66
- #
67
- # - `stroma` - Access the State container
68
- # - `inherited` - Copy state to child classes
69
- # - `extensions` - DSL block for hook registration
70
- module ClassMethods
71
- def self.extended(base)
72
- base.instance_variable_set(:@stroma, State.new)
73
- end
74
-
75
- # Handles inheritance by duplicating Stroma state.
76
- #
77
- # Creates an independent copy of hooks and settings for the child class,
78
- # then applies all registered hooks to the child.
79
- #
80
- # @param child [Class] The child class being created
81
- # @return [void]
82
- def inherited(child)
83
- super
84
-
85
- child.instance_variable_set(:@stroma, stroma.dup)
86
-
87
- Hooks::Applier.new(child, child.stroma.hooks).apply!
88
- end
89
-
90
- # Returns the Stroma state for this service class.
91
- #
92
- # @return [State] The Stroma state container
93
- #
94
- # @example Accessing hooks
95
- # stroma.hooks.before(:actions)
96
- #
97
- # @example Accessing settings
98
- # stroma.settings[:actions][:authorization][:method_name]
99
- def stroma
100
- @stroma ||= State.new
101
- end
102
-
103
- private
104
-
105
- # DSL block for registering hooks.
106
- #
107
- # Evaluates the block in the context of a Hooks::Factory,
108
- # allowing before/after hook registration.
109
- #
110
- # @yield Block with before/after DSL calls
111
- # @return [void]
112
- #
113
- # @example
114
- # extensions do
115
- # before :actions, AuthorizationExtension
116
- # after :outputs, LoggingExtension
117
- # end
118
- def extensions(&block)
119
- @stroma_hooks_factory ||= Hooks::Factory.new(stroma.hooks)
120
- @stroma_hooks_factory.instance_eval(&block)
121
- end
122
- end
123
- end
124
- end