Rails 从主应用程序路由时未找到可安装引擎辅助方法

Rails mountable engines helper methods not found when routing from main app

我有一个模块作为安装到主应用程序的可安装引擎 通过

mount MyEngine::Engine, :at => '/myengine'

我在 negine 中为所有内容命名空间,引擎在 engine/app/views/myengine/

中有自己的视图

当我 运行 rails 服务器然后尝试访问

时一切正常

localhost:3000/myengine

首先,然后转到主应用程序的根目录,然后通过主应用程序索引视图中的 link 返回引擎

然而,当我启动服务器时,转到 localhost:3000 并从那里单击 link 到引擎模块,它会尝试正确获取视图,但是引擎助手中包含的方法引发调用它们未定义时出错。

我在 rails 4

您需要在 lib/engine.rb 文件中明确包含助手:

initializer 'your-gem-name.setup_helpers' do |app|
    app.config.to_prepare do
      ActionController::Base.send :include, HelperModuleName
    end
  end
end

我使用 eager_load 加载可安装引擎,它已在主应用程序中初始化,现在一切似乎都在工作。

Myengine::Engine.eager_load!

我也 运行 关注这个问题。我在我的引擎中定义了一个 ApplicationController,当我试图在我的引擎的一个控制器中使用辅助方法时,我看到了 NoMethodErrors。

问题

代码看起来像这样:

在my_engine/app/controllers/my_engine/application_controller.rb

module MyEngine
  class ApplicationController < ActionController::Base
    helper ApplicationHelper
  end
end

在my_engine/app/controllers/my_engine/projects_controller.rb

module MyEngine
  class ProjectsController < ApplicationController
    def new
      # some action code here
    end
  end
end

在my_engine/app/helpers/my_engine/application_helper.rb

module MyEngine
  module ApplicationHelper
    def translations_include_tag
      javascript_include_tag "translations-#{I18n.locale}.js"
    end
  end
end

如果我导航到主机 rails 应用程序中的路由,然后单击导航到 my_engine/projects/new 的 link,我会得到一个 NoMethodErrortranslations_include_tag 不存在。

解释

我认为发生这种情况是因为该应用程序有两个同名的 类:MyEngine 中的 ApplicationController 和主机应用程序中的 ApplicationController。当访问引擎外部的第一条路线时,Rails 会从主机应用程序自动加载 ApplicationController。这意味着 Rails 将主应用程序的 ApplicationController 与名称 "ApplicationController" 相关联。当您导航到 my_engine/projects/new 时,Rails 延迟加载 ProjectsController,它也继承自 ApplicationController。 Rails/ruby 认为您指的是它已经加载的 same ApplicationController,因此实际上 ProjectsController 最终继承自主机应用程序的 ApplicationController 而不是引擎中的那个。由于 translations_include_tag 方法未在主机应用程序的 ApplicationController 中定义,ruby 在视图尝试调用它时引发 NoMethodError

解决方案

我能够通过显式继承 MyEngineApplicationController:

来解决问题

在my_engine/app/controllers/my_engine/projects_controller.rb

module MyEngine
  # changed from just plain 'ol ApplicationController
  class ProjectsController < ::MyEngine::ApplicationController
    def new
      # some action code here
    end
  end
end

解决继承问题后,NoMethodError 消失了。

接受的答案是使用预先加载,这也将解决问题,因为它会强制引擎的 ApplicationController 首先加载。由于引擎的 ApplicationController 是在 MyEngine 内命名的,所以它的全名是 MyEngine::ApplicationController,与主应用程序的 ApplicationController 不冲突,因为常量名称不同。