rampart-core 0.1.3 → 0.1.4
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/lib/rampart/engine_loader.rb +107 -45
- data/lib/rampart/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 153fe0a9dfec68247636c637d29acc8405bc6d123fd9d43e8beea3e4e4cf6d9f
|
|
4
|
+
data.tar.gz: c93f4f3ac48733d1e6017560c4466c73da6da23ba1d1a658b55e8d6170c45c60
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f44a2e70c2668cd1275976b3675e6c2a54914b83c3b6828dd0ed63ab4115398d506ee1eccd28d53ed17c34b6dba0be85aa38cc7e7976f8d39eaf32da0241316d
|
|
7
|
+
data.tar.gz: d4afcfe18c00e66d495415ab7468d4b371cd52911c1c074021153a26fa5cce5500fbaedb5fca0baf2f1d4f6d36090c1e3e1b33c45a2975378f2db47f21f84d20
|
|
@@ -3,50 +3,115 @@
|
|
|
3
3
|
module Rampart
|
|
4
4
|
# Generic loader for Rails engines using hexagonal architecture
|
|
5
5
|
#
|
|
6
|
-
# This loader
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
6
|
+
# This loader configures Zeitwerk to properly handle the mismatch between
|
|
7
|
+
# hexagonal architecture directory structure (app/{layer}/{context}/{subdirs}/)
|
|
8
|
+
# and flat Ruby namespace conventions (Context::ClassName instead of
|
|
9
|
+
# Context::Subdirs::ClassName).
|
|
10
|
+
#
|
|
11
|
+
# It collapses ALL subdirectories within the context namespace so that
|
|
12
|
+
# Zeitwerk expects files to define constants directly under the context
|
|
13
|
+
# namespace (e.g., CatContent::CatListing, not CatContent::Aggregates::CatListing).
|
|
10
14
|
#
|
|
11
15
|
# Usage in your engine:
|
|
12
16
|
# module YourContext
|
|
13
17
|
# class Engine < ::Rails::Engine
|
|
14
|
-
#
|
|
15
|
-
# Rampart::EngineLoader.load_all(
|
|
16
|
-
# engine_root: YourContext::Engine.root,
|
|
17
|
-
# context_name: "your_context"
|
|
18
|
-
# )
|
|
19
|
-
# end
|
|
18
|
+
# Rampart::EngineLoader.setup(self)
|
|
20
19
|
# end
|
|
21
20
|
# end
|
|
22
21
|
class EngineLoader
|
|
22
|
+
# Hexagonal architecture layers in load order
|
|
23
|
+
LAYERS = %w[domain application infrastructure].freeze
|
|
23
24
|
class << self
|
|
25
|
+
# Set up hexagonal architecture loading for an engine
|
|
26
|
+
#
|
|
27
|
+
# This configures:
|
|
28
|
+
# 1. Autoload paths for domain, application, and infrastructure layers
|
|
29
|
+
# 2. Eager load paths for CI/production
|
|
30
|
+
# 3. Zeitwerk collapse for flat namespaces (before eager loading)
|
|
31
|
+
# 4. File loading in correct dependency order (via config.to_prepare)
|
|
32
|
+
#
|
|
33
|
+
# @param engine_class [Class] The Rails::Engine class (e.g., YourContext::Engine)
|
|
34
|
+
def setup(engine_class)
|
|
35
|
+
# Infer context_name from engine module (e.g., CatContent::Engine -> "cat_content")
|
|
36
|
+
context_name = engine_class.module_parent.name.underscore
|
|
37
|
+
root = engine_class.root
|
|
38
|
+
|
|
39
|
+
# Add hexagonal layer directories to autoload and eager_load paths
|
|
40
|
+
layer_paths = LAYERS.map { |layer| root.join("app/#{layer}") }
|
|
41
|
+
engine_class.config.autoload_paths += layer_paths
|
|
42
|
+
engine_class.config.eager_load_paths += layer_paths
|
|
43
|
+
|
|
44
|
+
# Register initializer to configure Zeitwerk collapse before autoload paths are set
|
|
45
|
+
engine_class.initializer "#{context_name}.zeitwerk", before: :set_autoload_paths do
|
|
46
|
+
Rampart::EngineLoader.configure_autoloading(
|
|
47
|
+
engine_root: root,
|
|
48
|
+
context_name: context_name
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Register config.to_prepare to load files in correct order
|
|
53
|
+
engine_class.config.to_prepare do
|
|
54
|
+
Rampart::EngineLoader.load_all(
|
|
55
|
+
engine_root: root,
|
|
56
|
+
context_name: context_name
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Configure Zeitwerk to collapse all subdirectories within hexagonal layers
|
|
62
|
+
#
|
|
63
|
+
# @param engine_root [Pathname] Root path of the Rails engine
|
|
64
|
+
# @param context_name [String] Snake-case name of the bounded context
|
|
65
|
+
def configure_autoloading(engine_root:, context_name:)
|
|
66
|
+
return unless defined?(Rails) && Rails.respond_to?(:autoloaders)
|
|
67
|
+
|
|
68
|
+
loader = Rails.autoloaders.main
|
|
69
|
+
|
|
70
|
+
# Configure collapse for each hexagonal layer
|
|
71
|
+
LAYERS.each do |layer|
|
|
72
|
+
layer_path = engine_root.join("app/#{layer}/#{context_name}")
|
|
73
|
+
next unless layer_path.exist?
|
|
74
|
+
|
|
75
|
+
# Recursively collapse ALL subdirectories within the context path
|
|
76
|
+
collapse_all_subdirectories(loader, layer_path)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
24
80
|
# Load all hexagonal architecture components for an engine
|
|
25
81
|
#
|
|
26
82
|
# @param engine_root [Pathname] Root path of the Rails engine
|
|
27
|
-
# @param context_name [String] Snake-case name of the bounded context
|
|
83
|
+
# @param context_name [String] Snake-case name of the bounded context
|
|
28
84
|
def load_all(engine_root:, context_name:)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
85
|
+
LAYERS.each do |layer|
|
|
86
|
+
send("load_#{layer}_layer", engine_root, context_name)
|
|
87
|
+
end
|
|
32
88
|
end
|
|
33
89
|
|
|
34
90
|
private
|
|
35
91
|
|
|
92
|
+
# Recursively collapse all subdirectories
|
|
93
|
+
def collapse_all_subdirectories(loader, path)
|
|
94
|
+
return unless path.exist?
|
|
95
|
+
|
|
96
|
+
Dir.glob(path.join("*/")).each do |subdir|
|
|
97
|
+
subdir_path = Pathname.new(subdir)
|
|
98
|
+
loader.collapse(subdir_path.to_s)
|
|
99
|
+
# Recursively collapse nested subdirectories
|
|
100
|
+
collapse_all_subdirectories(loader, subdir_path)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
36
104
|
def load_domain_layer(root, context_name)
|
|
37
105
|
domain = root.join("app/domain/#{context_name}")
|
|
38
106
|
return unless domain.exist?
|
|
39
107
|
|
|
40
|
-
# Load
|
|
108
|
+
# Load errors/exceptions first (they have no dependencies)
|
|
41
109
|
load_directory(domain, pattern: "*_error.rb")
|
|
42
110
|
load_directory(domain, pattern: "*_exception.rb")
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
load_directory(domain.join("services"))
|
|
48
|
-
load_directory(domain.join("ports"))
|
|
49
|
-
|
|
111
|
+
|
|
112
|
+
# Load all subdirectories
|
|
113
|
+
load_all_subdirectories(domain)
|
|
114
|
+
|
|
50
115
|
# Load any remaining files at domain root
|
|
51
116
|
load_directory(domain, pattern: "*.rb")
|
|
52
117
|
end
|
|
@@ -55,10 +120,8 @@ module Rampart
|
|
|
55
120
|
app = root.join("app/application/#{context_name}")
|
|
56
121
|
return unless app.exist?
|
|
57
122
|
|
|
58
|
-
# Load all
|
|
59
|
-
|
|
60
|
-
load_directory(app.join("queries"))
|
|
61
|
-
load_directory(app.join("services"))
|
|
123
|
+
# Load all subdirectories and root files
|
|
124
|
+
load_all_subdirectories(app)
|
|
62
125
|
load_directory(app, pattern: "*.rb")
|
|
63
126
|
end
|
|
64
127
|
|
|
@@ -66,35 +129,34 @@ module Rampart
|
|
|
66
129
|
infra = root.join("app/infrastructure/#{context_name}")
|
|
67
130
|
return unless infra.exist?
|
|
68
131
|
|
|
69
|
-
# Load
|
|
132
|
+
# Load base_record.rb first (models depend on it)
|
|
70
133
|
persistence = infra.join("persistence")
|
|
71
|
-
if persistence.exist?
|
|
72
|
-
load_directory(persistence, pattern: "base_record.rb")
|
|
73
|
-
load_directory(persistence.join("models"))
|
|
74
|
-
load_directory(persistence.join("mappers"))
|
|
75
|
-
load_directory(persistence.join("repositories"))
|
|
76
|
-
end
|
|
134
|
+
load_directory(persistence, pattern: "base_record.rb") if persistence.exist?
|
|
77
135
|
|
|
78
|
-
# Load
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
136
|
+
# Load all subdirectories
|
|
137
|
+
load_all_subdirectories(infra)
|
|
138
|
+
|
|
139
|
+
# Load any remaining files at infrastructure root
|
|
82
140
|
load_directory(infra, pattern: "*.rb")
|
|
83
141
|
end
|
|
84
142
|
|
|
143
|
+
def load_all_subdirectories(dir)
|
|
144
|
+
return unless dir.exist?
|
|
145
|
+
|
|
146
|
+
Dir.glob(dir.join("*/")).sort.each do |subdir|
|
|
147
|
+
load_directory(Pathname.new(subdir))
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
85
151
|
def load_directory(dir, pattern: "**/*.rb")
|
|
86
152
|
return unless dir.exist?
|
|
87
153
|
|
|
88
154
|
Dir.glob(dir.join(pattern)).sort.each do |file|
|
|
89
155
|
next if File.directory?(file)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
# In eager load environments (CI/production), Zeitwerk has already loaded these files.
|
|
95
|
-
# Using require instead of load prevents re-execution and duplicate definitions.
|
|
96
|
-
require file.to_s
|
|
97
|
-
end
|
|
156
|
+
# With Zeitwerk collapse configured, Zeitwerk manages autoloading and hot-reloading.
|
|
157
|
+
# Use require to ensure files are loaded in correct dependency order without
|
|
158
|
+
# re-executing already-loaded files. Zeitwerk handles reloading on code changes.
|
|
159
|
+
require file.to_s
|
|
98
160
|
end
|
|
99
161
|
end
|
|
100
162
|
end
|
data/lib/rampart/version.rb
CHANGED