Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Gaolz/766af8908754b5fd36065f292cc7a11f to your computer and use it in GitHub Desktop.
Save Gaolz/766af8908754b5fd36065f292cc7a11f to your computer and use it in GitHub Desktop.
如何封装公共方法到 Rails Engine 中及其注意事项

1. 什么是 Rails Engine

  • Rails Guides:Engine 可以看作为宿主应用提供附加功能的微型应用(如 feedmob-rescue, feedmob-sidebar)

  • 而 Rails Application 实质上就是一个加强版的 Engine

2. Rails Engine 优缺点

1. 背景:当宿主应用过于复杂且有某一功能耦合度不高时可以单独抽出来做成一个 Engine.

2. 优点:

  • 解耦 (避免宿主应用代码过多,导致难以维护)
  • 可以针对 engine 单独测试

3. 缺点:

  • 虽然提供了 moutable 的方式可以使 engine 中的方法与宿主应用隔离开,不过需要注意的是,engine 中与 宿主应用相互调用方法时,需要加对方的前缀

3. Mountable Engine 与 Full Engine

1. 生成命令

# mounable
rails plugin new blorgh --mountable

# full

rails plugin new blorgh

2. 结构

通过 --moutable 选项生成的 engine. 会比 --full 选项增加如下特性【实质就是与宿主应用隔离开】

  • 静态资源文件的清单文件(application.js 和 application.css)
  • 具有独立命名空间的ApplicationController
  • 具有独立命名空间的 ApplicationHelper
  • 引擎的布局视图模板
  • 在 config/routes.rb 文件中为引擎设置独立的命名空间:
# full
Rails.application.routes.draw do
end

# mountable
Blorgh::Engine.routes.draw do
end
  • 在 lib/blorgh/engine.rb 文件中为引擎设置独立的命名空间:
# full
module Blorgh
  class Engine < ::Rails::Engine
  end
end

# mountable
module Blorgh
  class Engine < ::Rails::Engine
    isolate_namespace Blorgh
  end
end

3. 适用场景

  • 如果与宿主应用交互挺多,比如像 feedmob-rescue 这种直接从宿主应用捕获异常,新增中间件的处理请求异常的,或是功能简单,可以使用 --full

  • 担心会与宿主应用出现命名冲突,功能被覆盖掉,还是使用 --moutable


4. Engine 可以做些啥(以 moutable 举例)

1. 加载的流程

  • 加载 lib/feedmob/sidebar/sidebar.rb --> 引入 engine | railtie

1. 抽象封装功能(feedmob-sidebar)

2. 处理通用异常(feedmob-rescue)


5. 注意事项

1. 隔离

# lib/blorgh/engine.rb
module Blorgh
  class Engine < ::Rails::Engine
    
    # 这一行把 engine 与宿主应用的类隔离开,防止命名冲突
    isolate_namespace Blorgh
  end
end

这里的 isolate_namespace 方法尤其需要注意。通过调用此方法,可以把引擎的控制器、模型、路由和其他组件隔离到各自的命名空间中,以便和应用中的类似组件隔离开来。避免引擎中的组建被应用中的同名组件覆盖。

2. 路由

Feedmob::Rescue::Engine.routes.draw do
  get :not_found, to: 'page#not_found'
end
  • 这里的路由是通过 Blorgh::Engine 对象而非 YourApp::Application 类定义的。
  • 这样做的目的是把引擎路由限制在引擎中,这样就可以根据需要把引擎路由挂载到不同位置,同时也把引擎路由和应用中的其他路由隔离开来。

3. 路由挂载

    1. full
Rails.application.routes.draw do

# ...

match "*path", to: "admin/advertisers#error_404", via: :all
end

full 选项不能通过挂载的方式灵活在宿主应用中插入 engine 路由的位置,默认就会出现在 match "*path" 的后面导致出现地址访问不到的情况。

    1. mountable
Rails.application.routes.draw do

# ...

  mount Feedmob::Rescue::Engine, at: '/'

  match "*path", to: "admin/advertisers#error_404", via: :all
end

moutable 选项可灵活插入到宿主应用的路由中,避免地址访问不到的情况

  • 生成的 engine 路由如下:
not_found GET  /not_found(.:format) feedmob/rescue/page#not_found

4. helper 互相调用

    1. engine 中调用宿主中的 helper
# 错误
<%= link_to "Home", root_path %>

# 正确
<%= link_to "Home", main_app.root_path %>
    1. 宿主 应用中调用 enginehelper
      1. mount Feedmob::Rescue::Engine, at: '/'
      <%= link_to "Home",engine_path %>
      1. mount Ahoy::Engine, at: '/ahoy'
      <%= link_to "Home",ahoy.engine_path %>

5. Active Support on_load 钩子

  • 例子:feedmob-sidebar
module Feedmob
  module Sidebar
    class Engine < ::Rails::Engine
      isolate_namespace Feedmob::Sidebar

      initializer 'local_helper.action_controller' do
        ActiveSupport.on_load(:action_view) do
          include Feedmob::Sidebar::SidebarHelper
          include Feedmob::Sidebar::PageRemarkHelper
        end
      end
    end
  end
end
  • 作用:ActiveSupport.on_load 可以延迟加载代码,在真正需要时才加载。
  • 这里的作用就是在渲染页面(生成 ActionView 实例)的时候才会引入两个 Engine 中的 helper

6. Railtie

  • Rails::Railtie is the core of the Rails framework and provides several hooks to extend Rails and/or modify the initialization process.
  • 提供钩子来扩展 Rails 功能,通过在初始化中做一些定制化功能。
  • 例子:feedmob-rescue -> http_method_not_allowed 中间件
require 'feedmob/rescue/http_method_not_allowed'

module FeedmobRescue
  class Railtie < Rails::Railtie
    initializer "feedmob_rescue.configure_rails_initialization" do
      if defined? ActionDispatch::RemoteIp
        Rails.application.config.middleware.insert_after(ActionDispatch::RemoteIp, FeedmobRescue::HttpMethodNotAllowed)
      else
        Rails.application.config.middleware.use FeedmobRescue::HttpMethodNotAllowed
      end
    end
  end
end

7. 参考文章:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment