我收到以下錯誤zeitwerk/loader/helpers.rb:95:in const_get': uninitialized constant Controllers::BasePublicDecorator (NameError)
這是使用本地生產控制臺中的錯誤,rails c -e production但在完美運行的開發中不是問題。
在一個引擎中CcsCms::PublicTheme,我有一個裝飾器用于擴展另一個CcsCms::Core引擎的控制器,正是這個裝飾器導致了錯誤。
public_theme/app/decorators/decorators/controllers/base_public_decorator.rb
CcsCms::BasePublicController.class_eval do
before_action :set_theme #ensure that @current_theme is available for the
#header in all public views
private
def set_theme
@current_theme = CcsCms::PublicTheme::Theme.current_theme
end
end
此功能在開發中運行良好,但在生產中失敗并出現如下錯誤
我試圖在 CcsCms::Core 引擎中裝飾的控制器是 CcsCms::BasePublicController.rb
module CcsCms
class BasePublicController < ApplicationController
layout "ccs_cms/layouts/public"
protected
def authorize
end
end
end
在我嘗試使用的帶有裝飾器的主題引擎中,我有一個 Gemfile,它定義了核心引擎,如下所示
gem 'ccs_cms_core', path: '../core'
在 ccs_cms_public_theme.gemspec 我需要核心引擎作為依賴項
spec.add_dependency "ccs_cms_core"
in the engine.rb I am requiring the core engine and loading the decorator paths in a config.to_prepare do block
require "deface"
require 'ccs_cms_admin_dashboard'
require 'ccs_cms_custom_page'
require 'ccs_cms_core'
require 'css_menu'
#require 'tinymce-rails'
require 'delayed_job_active_record'
require 'daemons'
require 'sprockets/railtie'
require 'sassc-rails'
module CcsCms
module PublicTheme
class Engine < ::Rails::Engine
isolate_namespace CcsCms::PublicTheme
paths["app/views"] << "app/views/ccs_cms/public_theme"
initializer "ccs_cms.assets.precompile" do |app|
app.config.assets.precompile = %w( public_theme_manifest.js )
end
initializer :assets do |config|
Rails.application.config.assets.paths << root.join("")
end
initializer :append_migrations do |app|
unless app.root.to_s.match?(root.to_s)
config.paths['db/migrate'].expanded.each do |p|
app.config.paths['db/migrate'] << p
end
end
end
initializer :active_job_setup do |app|
app.config.active_job.queue_adapter = :delayed_job
end
config.to_prepare do
Dir.glob(Engine.root.join("app", "decorators", "**", "*_decorator*.rb")) do |c|
Rails.configuration.cache_classes ? require(c) : load(c)
end
end
config.generators do |g|
g.test_framework :rspec,
fixtures: false,
request: false,
view_specs: false,
helper_specs: false,
controller_specs: false,
routing_specs: false
g.fixture_replacement :factory_bot
g.factory_bot dir: 'spec/factories'
end
end
end
end
Given that my decorator is given the same name as the controller it is decorating from the core engine but with the .decorator extension I am pretty certain that is everything hooked up correctly, as mentioned, this works perfectly in development but I am unable to start a rails console in a production environment due to this error. It seems that the class_eval is failing somehow and I can only think that this may be a path issue but I can not figure it out
UPDATE After quite a big learning curve, thank's muchly to @debugger comments and @Xavier Noria answer it is clear that my issue comes down to Zeitworks autoload functionality
Rails guides here has an interesting and appealing solution to me
Another use case are engines decorating framework classes:
initializer "decorate ActionController::Base" do
> ActiveSupport.on_load(:action_controller_base) do
> include MyDecoration end end
There, the module object stored in MyDecoration by the time the initializer runs becomes an ancestor of ActionController::Base, and reloading MyDecoration is pointless, it won't affect that ancestor chain.
But maybe this isn't the right solution, I again failed to make it work with the following
initializer "decorate CcsCms::BasePublicController" do
ActiveSupport.on_load(:ccs_cms_base_public_controller) do
include CcsCms::BasePublicDecorator
end
end
Generating the following error
zeitwerk/loader/callbacks.rb:25:in `on_file_autoloaded': expected file /home/jamie/Development/rails/comtech/r7/ccs_cms/engines/public_theme/app/decorators/controllers/ccs_cms/base_public_decorator.rb to define constant Controllers::CcsCms::BasePublicDecorator, but didn't (Zeitwerk::NameError)
So back to the solution provided here, thank's again for the answer below I tried the following which did work finally
config.to_prepare do
overrides = Engine.root.join("app", "decorators")
Rails.autoloaders.main.ignore(overrides)
p = Engine.root.join("app", "decorators")
loader = Zeitwerk::Loader.for_gem
loader.ignore(p)
Dir.glob(Engine.root.join("app", "decorators", "**", "*_decorator*.rb")) do |c|
Rails.configuration.cache_classes ? require(c) : load(c)
end
end
uj5u.com熱心網友回復:
這里的問題是,當延遲加載時,沒有人參考一個名為...::BasePublicDecorator. 但是,Zeitwerk 期望在該檔案中定義該常量,并且在預加載時會發現不匹配。
解決方案是將自動加載器配置為忽略裝飾器,因為您正在處理它們的加載,并且因為它們沒有在其名稱后定義常量。該檔案有一個示例。它需要適應您的引擎,但您會看到這個想法。
為了完整起見,我還要解釋一下,在 Zeitwerk 中,急切加載是遞回的const_get,而不是遞回的require。這是為了保證如果您訪問常量,加載在兩種模式下始終成功或失敗(而且它也更有效)。遞回const_get仍然require通過 發出呼叫Module#autoload,并且如果您為某些檔案運行一個呼叫,冪等性也適用,但 Zeitwerk 檢測到預期的常量無論如何都沒有定義,這是一種錯誤情況。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/456269.html
標籤:ruby-on-rails ruby decorator rails-engines ruby-on-rails-7
