Learning Rails Engine

Rails 3.0 开始支持 Engine。设计 Engine 的初衷是将特定功能的 Rails 项目,打包为 Engine,分享给其他 Rail 项目使用。Engine 可以被看作是一个微型的应用程序,他提供一些额外的功能给他的宿主程序(host applications)。

Engine 分为两种:Full EngineMountable Engine
通过以下命令,可以方便的创建 Engine。

rails plugin new engine_name --full        # Full Engine
rails plugin new engine_name --mountable   # Mountable Engine
PS:可以方便的将 Engine 打包为 Gem,从而在其他 Rails 项目中使用。

下面通过代码来学习这两种 Engine,并且对比两种实现所带来的利与弊,进行初步的分析。

以下实例,基于 Rails 3.2.3。基于 Rails 3.0 创建 Engine, 推荐安装 enginex Gem 来生成 Engine。 首先创建一个 Rails 项目:engine_demo

1. Full Engine

在 engine_demo 项目根目录下执行:

rails plugin new food --full

重新组织一下文件结构:将food 文件夹拷贝至 vendor 目录下 # 目录结构(截图)

Engine food 即是一个迷你版的 Rails application。 下面实现一个展示食物的页面。

# 新建一个 foods_controller.rb 包含 index 方法。
# food/app/controllers/foods_controller.rb
class FoodsController < ActionController::Base
  def index
  end
end

# 配置路由
# food/config/routes.rb
Rails.application.routes.draw do
  get "foods" => "foods#index"
end

# food/lib/food.rb
require "food/engine"
module Food
end

# food/lib/food/engine.rb
module Food
  class Engine < Rails::Engine
  end
end

# engine_demoe
Gemfile
gem 'food', :path => "vendor/food"

$ rake routes
》foods GET /foods(.:format) foods#index
特点:
没有命名空间,与 engine_demo 项目依赖度极高。
Engine food 通过修改 Rails 的 route,将其功能添加进engine_demo 项目。

优势:
engine_demo 项目中不需要做额外的设置,即可拥有该功能。
Engine food 中的routes,controllers,models,helpers 直接开放给 engine_demo 使用。

劣势:容易引起命名冲突。

2. Mountable Engine

在 engine_demo 项目根目录下执行:

rails plugin new blog --mountable

Engine blog 的目录结构与 food 有些不一样。见下面的截图:

下面实现一个blog 展示页面。 看以下代码实例:

# blog/app/controllers/blog/application_controller.rb
module Blog
  class ApplicationController < ActionController::Base
  end
end

# blog/lib/blog.rb
require "blog/engine"
module Blog
end

# blog/lib/blog/engine.rb
module Blog
  class Engine < Rails::Engine
    isolate_namespace Blog
  end
end

# blog/app/controllers/blog/articles_controller.rb
module Blog
  class ArticlesController < Blog:: ApplicationController
    def index
    end
  end
end

# blog/config/routes.rb
Blog::Engine.routes.draw do
  get "articles" => "articles#index"
end

# engine_demo/Gemfile
gem 'blog', :path => "vendor/blog"

# engine_demo/config/routes.rb
EngineDemo::Application.routes.draw do
  mount Blog::Engine => "/blog"
end

# engine_demo
$ rake routes
blog  /blog Blog::Engine

Routes for Blog::Engine:
articles GET /articles(.:format) blog/articles#index
特点:
包含命名空间,很好的与 engine_demo 项目进行了隔离。
通过修改 engine_demo 的 route,引进 Engine blog 的功能。

优势:很好的控制了命名冲突的风险。

劣势:遵守命名规范,增加了一些配置。

总结:Engine 适合与对复杂应用进行划分的场景。

2012-08-29

rocket-wing