32

背景

我正在创建一个由一个核心和几个模块组成的应用程序。这些模块是 rails 引擎,并提供实际功能,因为核心本身仅充当主机。引擎从它们各自的路径托管/lib并安装在它们各自的路径上。

coreApp
└──lib
   ├── module1
   ├── module2
   └── etc

然后像这样安装模块

mount Module1::Engine => "/module1", :as => "module1"
mount Module2::Engine => "/module2", :as => "module2"

核心还负责处理会话,尽管登录本身是由模块完成的。

问题

我还没有找到与引擎共享核心应用程序布局的好方法。到目前为止,这就是我使引擎可以使用布局的方式:

coreApp
└── app
    └── views
        └── layouts
            ├── application.html.erb
            └── core.html.erb

该文件core.html.erb仅包含

<%= render :template => 'layouts/application' %>

然后像这样包含在每个模块中

module Module1
  class ApplicationController < ActionController::Base
    layout "core"
  end
end

尽管它不是特别优雅,但它工作正常,并且模块的内容yield在应用程序布局中的语句处呈现。

问题如下:

1. 模块特定样式表不包含在标题中

我需要一种方法来包含活动模块的样式表。

2. 标头需要访问有关登录用户的信息

标头包含有关登录用户的信息,例如

Logged in as <%= @user[:realname] %>

这来自核心home_controller

def index
  @user = User.find_by_id(session[:user])
end

但是当我尝试访问该模块时,出现以下错误

NoMethodError in Module1/home#index

You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]

显然是指@user

问题

如何在不对发动机侧进行过多篡改的情况下尽可能优雅和干燥地解决这个问题?

我已经用谷歌搜索了很多,但无法真正了解如何解决它。可能完全缺乏对 Rails 工作原理的了解,所以这个问题很可能对于熟悉 Rails 的人来说甚至没有意义。

如果有任何不清楚或模棱两可的地方,请发表评论,我会尽力详细说明。

4

3 回答 3

44

我已经成功地在我的引擎中使用了我的父应用程序的布局。首先,根据Rails Guides (Engines) 的第 4.3.2 节,为了访问父应用程序 ApplicationController 的变量(如session您在上面使用的),您需要替换application_controller.rb您当前拥有的引擎:

module Module1
  class ApplicationController < ActionController::Base
    layout "core"
  end
end

对此:

class Module1::ApplicationController < ::ApplicationController
end

这将继承父应用程序的 ApplicationController 以及它的所有变量。

其次,您需要app/views/layouts/application.html.erb从引擎视图中删除该文件,因为您使用的是父应用程序的文件,因此不需要它。

现在,当您从父应用程序渲染 Module1 的视图时,将使用父应用程序的布局,并且session[]将正确评估所有变量。

不要忘记添加单词“main_app”。在布局中的每个链接之前,否则它将尝试在引擎中而不是父应用程序中查找路径。例如,如果父应用程序中的布局包含指向some_path(即父应用程序中的视图)的链接,则当在引擎中显示使用此布局的视图时,将尝试some_path在引擎中而不是父应用程序中查找。您需要将链接更改main_app.some_path为才能正常工作。

希望这可以帮助。

于 2012-06-10T12:02:07.373 回答
18

采用layout 'layouts/application'

如果你不想使用 main_app.your_path 你也可以添加:

module YourEngine
  module ApplicationHelper
    def method_missing(method, *args, &block)
      if (method.to_s.end_with?('_path') || method.to_s.end_with?('_url')) && main_app.respond_to?(method)
        main_app.send(method, *args)
      else
        super
      end
    end
  end
end
于 2013-04-03T15:33:55.257 回答
9

我在我的应用程序中也做了同样的事情。我所做的只是:

  1. 删除 /app/view/layouts/ 中的引擎布局
  2. 将您的 application_controller 更改为

    module EngineModule
      class ApplicationController < ::ApplicationController
        layout 'layouts/application' 
      end
    end
    
  3. 在您看来,如果您想引用任何路径,例如login_path,可以通过main_app.login_path

于 2012-11-08T17:12:17.040 回答