Class: AdminSuite::HostAutoloadPolicy
- Inherits:
-
Object
- Object
- AdminSuite::HostAutoloadPolicy
- Defined in:
- lib/admin_suite/host_autoload_policy.rb
Overview
Zeitwerk integration for host apps, extracted from the engine's "admin_suite.host_dsl_ignore" initializer so it is directly testable against the real Zeitwerk-loader interface instead of a copy of it.
Host apps may store AdminSuite DSL files under app/admin_suite/** and
app/admin/portals/**. These are side-effect DSL files (they do not
define constants), so Zeitwerk must ignore them to avoid eager-load
Zeitwerk::NameErrors in production. Host apps may also use Admin::*
constants inside app/admin/**, which needs a namespace mapping since
Rails' default autoload root (app/admin) otherwise expects top-level
constants.
Class Method Summary collapse
- .apply!(app, loaders: Rails.autoloaders) ⇒ void
-
.contains_admin_suite_portal_dsl?(admin_portals_dir) ⇒ Boolean
True if any file under
admin_portals_diruses theAdminSuite.portalDSL (as opposed to defining real constants). -
.host_admin_namespace_files?(admin_dir) ⇒ Boolean
True if any file under app/admin (excluding app/admin/portals) appears to define
Admin::*constants.
Class Method Details
.apply!(app, loaders: Rails.autoloaders) ⇒ void
This method returns an undefined value.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/admin_suite/host_autoload_policy.rb', line 24 def apply!(app, loaders: Rails.autoloaders) root = app.root admin_suite_app_dir = root.join("app/admin_suite") admin_dir = root.join("app/admin") admin_portals_dir = root.join("app/admin/portals") # If the host uses `Admin::*` constants inside `app/admin/**`, Rails' default # autoload root (`app/admin`) would expect top-level constants like # `Resources::UserResource`. We fix that by mapping `app/admin` to `Admin`. # This avoids requiring host apps to add their own Zeitwerk initializer. admin_dir_mapped = admin_dir.exist? && host_admin_namespace_files?(admin_dir) if admin_dir_mapped admin_dir_s = admin_dir.to_s app.config.autoload_paths.delete(admin_dir_s) app.config.eager_load_paths.delete(admin_dir_s) # Ensure `Admin` exists so Zeitwerk can use it as a namespace. ensure_module!(Object, :Admin) main_loader(loaders).push_dir(admin_dir, namespace: ::Admin) # `app/admin/renderers/foo_renderer.rb` now resolves to # `Admin::Renderers::FooRenderer` under this same push_dir, with no # extra work: the whole `app/admin` tree (including `renderers/`) is # namespaced under `::Admin` by Zeitwerk's directory conventions. end # A host with ONLY DSL files under `app/admin` (no `Admin::*` constants # anywhere) never takes the branch above, so `app/admin` remains a # default Rails autoload root mapping to top-level constants. Without # this, `app/admin/renderers/foo_renderer.rb` would need to define a # top-level `Renderers::FooRenderer` instead of the documented # `Admin::Renderers::FooRenderer`, and `host_renderer_class` in # base_helper.rb would never find it. Map `app/admin/renderers` # explicitly in that case. renderers_dir = root.join("app/admin/renderers") if renderers_dir.exist? && !admin_dir_mapped ensure_module!(Object, :Admin) ensure_module!(::Admin, :Renderers) main_loader(loaders).push_dir(renderers_dir, namespace: ::Admin::Renderers) end each_loader(loaders) do |loader| loader.ignore(admin_suite_app_dir) if admin_suite_app_dir.exist? next unless admin_portals_dir.exist? loader.ignore(admin_portals_dir) if contains_admin_suite_portal_dsl?(admin_portals_dir) end end |
.contains_admin_suite_portal_dsl?(admin_portals_dir) ⇒ Boolean
True if any file under admin_portals_dir uses the AdminSuite.portal
DSL (as opposed to defining real constants).
100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/admin_suite/host_autoload_policy.rb', line 100 def contains_admin_suite_portal_dsl?(admin_portals_dir) portal_files = Dir[admin_portals_dir.join("**/*.rb").to_s] portal_files.any? do |file| content = File.binread(file) content = content.encode("UTF-8", invalid: :replace, undef: :replace, replace: "") portal_dsl_pattern = /(::)?AdminSuite\s*\.\s*portal\b/ portal_dsl_pattern.match?(content) rescue StandardError false end end |
.host_admin_namespace_files?(admin_dir) ⇒ Boolean
True if any file under app/admin (excluding app/admin/portals)
appears to define Admin::* constants.
81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/admin_suite/host_autoload_policy.rb', line 81 def host_admin_namespace_files?(admin_dir) Dir[admin_dir.join("**/*.rb").to_s].any? do |file| next false if file.include?("/portals/") content = File.binread(file) content = content.encode("UTF-8", invalid: :replace, undef: :replace, replace: "") content.match?(/\b(module|class)\s+Admin\b/) || content.match?(/\b(module|class)\s+Admin::/) rescue StandardError false end end |