在应用程序布局中渲染引擎

时间:2012-05-16 08:52:28

标签: ruby-on-rails-3 ruby-on-rails-plugins rails-engines

背景

我正在创建一个由核心和几个模块组成的应用程序。这些模块是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

问题

如何在没有太多篡改发动机的情况下尽可能优雅地和干燥地解决这个问题?

我用Google搜索很多,但无法真正理解如何解决它。可能完全缺乏对rails如何工作的洞察力,所以对于那些了解rails的人来说,这个问题很可能没有意义。

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

3 个答案:

答案 0 :(得分:41)

我已成功在我的引擎中使用了我的父应用程序的布局。首先,基于Section 4.3.2 of the Rails Guides (Engines),为了访问父应用程序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的链接(即父应用程序中的视图),则在使用此布局的引擎中显示视图时,将尝试查找{{1}在Engine而不是父应用程序中。您需要将链接更改为some_path才能生效。

希望这有帮助。

答案 1 :(得分:17)

使用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

答案 2 :(得分:6)

我在申请中也做了同样的事情。我所做的只是:

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

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

  5. 引用它