<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 引擎入門 本章節中您將學習有關引擎的知識,以及引擎如何通過簡潔易用的方式為Rails應用插上飛翔的翅膀。 通過學習本章節,您將獲得如下知識: * 引擎是什么 * 如何生成一個引擎 * 為引擎添加特性 * 為Rails應用添加引擎 * 給Rails中的引擎提供重載功能 ### Chapters 1. [引擎是什么?](#%E5%BC%95%E6%93%8E%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F-) 2. [生成一個引擎](#%E7%94%9F%E6%88%90%E4%B8%80%E4%B8%AA%E5%BC%95%E6%93%8E) * [引擎探秘](#%E5%BC%95%E6%93%8E%E6%8E%A2%E7%A7%98) 3. [引擎功能簡介](#%E5%BC%95%E6%93%8E%E5%8A%9F%E8%83%BD%E7%AE%80%E4%BB%8B) * [生成一個Article 資源](#%E7%94%9F%E6%88%90%E4%B8%80%E4%B8%AAarticle-%E8%B5%84%E6%BA%90) * [生成評論資源](#%E7%94%9F%E6%88%90%E8%AF%84%E8%AE%BA%E8%B5%84%E6%BA%90) 4. [和Rails應用整合](#%E5%92%8Crails%E5%BA%94%E7%94%A8%E6%95%B4%E5%90%88) * [整合前的準備工作](#%E6%95%B4%E5%90%88%E5%89%8D%E7%9A%84%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C) * [建立引擎](#%E5%BB%BA%E7%AB%8B%E5%BC%95%E6%93%8E) * [訪問Rails應用中的類](#%E8%AE%BF%E9%97%AErails%E5%BA%94%E7%94%A8%E4%B8%AD%E7%9A%84%E7%B1%BB) * [配置引擎](#%E5%92%8Crails%E5%BA%94%E7%94%A8%E6%95%B4%E5%90%88-%E9%85%8D%E7%BD%AE%E5%BC%95%E6%93%8E) 5. [引擎測試](#%E5%BC%95%E6%93%8E%E6%B5%8B%E8%AF%95) * [功能測試](#%E5%8A%9F%E8%83%BD%E6%B5%8B%E8%AF%95) 6. [引擎優化](#%E5%BC%95%E6%93%8E%E4%BC%98%E5%8C%96-) * [重載模型和控制器](#%E9%87%8D%E8%BD%BD%E6%A8%A1%E5%9E%8B%E5%92%8C%E6%8E%A7%E5%88%B6%E5%99%A8) * [視圖重載](#%E8%A7%86%E5%9B%BE%E9%87%8D%E8%BD%BD) * [路徑](#%E8%B7%AF%E5%BE%84) * [渲染頁面相關的Assets文件](#%E6%B8%B2%E6%9F%93%E9%A1%B5%E9%9D%A2%E7%9B%B8%E5%85%B3%E7%9A%84assets%E6%96%87%E4%BB%B6) * [頁面資源文件分組和預編譯](#%E9%A1%B5%E9%9D%A2%E8%B5%84%E6%BA%90%E6%96%87%E4%BB%B6%E5%88%86%E7%BB%84%E5%92%8C%E9%A2%84%E7%BC%96%E8%AF%91) * [其他Gem依賴項](#%E5%85%B6%E4%BB%96gem%E4%BE%9D%E8%B5%96%E9%A1%B9) ### 1 引擎是什么? 引擎可以被認為是一個可以為其宿主提供函數功能的中間件。一個Rails應用可以被看作一個"超級給力"的引擎,因為`Rails::Application` 類是繼承自 `Rails::Engine`的。 從某種意義上說,引擎和Rails應用幾乎可以說是雙胞胎,差別很小。通過本章節的學習,你會發現引擎和Rails應用的結構幾乎是一樣的。 引擎和插件也是近親,擁有相同的`lib`目錄結構,并且都是使用`rails plugin new`命令生成。不同之處在于,一個引擎對于Rails來說是一個"發育完全的插件"(使用命令行生成引擎時會加`--full`選項)。在這里我們將使用幾乎包含`--full`選項所有特性的`--mountable` 來代替。本章節中"發育完全的插件"和引擎是等價的。一個引擎可以是一個插件,但一個插件不能被看作是引擎。 我們將創建一個叫"blorgh"的引擎。這個引擎將為其宿主提供添加主題和主題評論等功能。剛出生的"blorgh"引擎也許會顯得孤單,不過用不了多久,我們將看到她和自己的小伙伴一起愉快的聊天。 引擎也可以離開他的應用宿主獨立存在。這意味著一個應用可以通過一個路徑助手獲得一個`articles_path`方法,使用引擎也可以生成一個名為`articles_path`的方法,而且兩者不會沖突。同理,控制器,模型,數據庫表名都是屬于不同命名空間的。接下來我們來討論該如何實現。 你心里須清楚Rails應用是老大,引擎是老大的小弟。一個Rails應用在他的地盤里面是老大,引擎的作用只是錦上添花。 可以看看下面的一些優秀引擎項目,比如[Devise](https://github.com/plataformatec/devise) ,一個為其宿主應用提供權限認證功能的引擎;[Forem](https://github.com/radar/forem), 一個提供論壇功能的引擎;[Spree](https://github.com/spree/spree),一個提供電子商務平臺功能的引擎。[RefineryCMS](https://github.com/refinery/refinerycms), 一個 CMS 引擎 。 最后,大部分引擎開發工作離不開James Adam,Piotr Sarnacki 等Rails核心開發成員,以及很多默默無聞付出的人們。如果你見到他們,別忘了向他們致謝! ### 2 生成一個引擎 為了生成一個引擎,你必須將生成插件命令和適當的選項配合使用。比如你要生成"blorgh"應用 ,你需要一個"mountable"引擎。那么在命令行終端你就要敲下如下代碼: ``` $ bin/rails plugin new blorgh --mountable ``` 生成插件命令相關的幫助信息可以敲下面代碼得到: ``` $ bin/rails plugin --help ``` `--mountable` 選項告訴生成器你想創建一個"mountable",并且命名空間獨立的引擎。如果你用選項`--full`的話,生成器幾乎會做一樣的操作。`--full` 選項告訴生成器你想創建一個引擎,包含如下結構: * 一個 `app` 目錄樹 * 一個 `config/routes.rb` 文件: ``` Rails.application.routes.draw do end ``` * 一個`lib/blorgh/engine.rb`文件,以及在一個標準的Rails應用文件目錄的`config/application.rb`中的如下聲明: ``` module Blorgh class Engine &lt; ::Rails::Engine end end ``` `--mountable`選項會比`--full`選項多做的事情有: * 生成若干資源文件(`application.js` and `application.css`) * 添加一個命名空間為`ApplicationController` 的子集 * 添加一個命名空間為`ApplicationHelper` 的子集 * 添加 一個引擎的布局視圖模版 * 在`config/routes.rb`中聲明獨立的命名空間 ; ``` Blorgh::Engine.routes.draw do end ``` 在`lib/blorgh/engine.rb`中聲明獨立的命名空間: ``` ```ruby module Blorgh class Engine < ::Rails::Engine isolate_namespace Blorgh end end ``` ``` 除此之外,`--mountable`選項告訴生成器在引擎內部的 `test/dummy` 文件夾中創建一個簡單應用,在`test/dummy/config/routes.rb`中添加簡單應用的路徑。 ``` mount Blorgh::Engine, at: "blorgh" ``` #### 2.1 引擎探秘 ##### 2.1.1 文件沖突 在我們剛才創建的引擎根目錄下有一個`blorgh.gemspec`文件。如果你想把引擎和Rails應用整合,那么接下來要做的是在目標Rails應用的`Gemfile`文件中添加如下代碼: ``` gem 'blorgh', path: "vendor/engines/blorgh" ``` 接下來別忘了運行`bundle install`命令,Bundler通過解析剛才在`Gemfile`文件中關于引擎的聲明,會去解析引擎的`blorgh.gemspec`文件,以及`lib`文件夾中名為`lib/blorgh.rb`的文件,然后定義一個`Blorgh`模塊: ``` require "blorgh/engine" module Blorgh end ``` 提示: 某些引擎會使用一個全局配置文件來配置引擎,這的確是個好主意,所以如果你提供了一個全局配置文件來配置引擎的模塊,那么這會更好的將你的模塊的功能封裝起來。 `lib/blorgh/engine.rb`文件中定義了引擎的基類。 ``` module Blorgh class Engine < Rails::Engine isolate_namespace Blorgh end end ``` 因為引擎繼承自`Rails::Engine`類,gem會通知Rails有一個引擎的特別路徑,之后會正確的整合引擎到Rails應用中。會為Rails應用中的模型,控制器,視圖和郵件等配置加載引擎的`app`目錄路徑。 `isolate_namespace`方法必須拿出來單獨談談。這個方法會把引擎模塊中與控制器,模型,路徑等模塊內的同名組件隔離。如果沒它的話,可能會把引擎的內部方法暴露給其它模塊,這樣會破壞引擎的封裝性,可能會引發不可預期的風險,比如引擎的內部方法被其他模塊重載。舉個例子,如果沒有用命名空間對模塊進行隔離,各模塊的helpers方法會發生沖突,那么引擎內部的helper方法會被Rails應用的控制器所調用。 提示:強烈建議您使用`isolate_namespace`方法定義引擎的模塊,如果沒使用它,這可能會在一個Rails應用中和其它模塊沖突。 命名空間對于執行像`bin/rails g model`的命令意味者什么呢? 比如`bin/rails g model article`,這個操作不會產生一個`Article`,而是`Blorgh::Article`。此外,模型的數據庫表名也是命名空間化的,會用`blorgh_articles` 代替`articles`。與模型的命名空間類似,控制器中的 `ArticlesController`會被`Blorgh::ArticlesController`取代。而且和控制器相關的視圖也會從`app/views/articles`變成`app/views/blorgh/articles`,郵件模塊也是如此。 總而言之,路徑同引擎一樣也是有命名空間的,命名空間的重要性將會在本指南中的[Routes](#routes)繼續討論。 ##### 2.1.2 `app` 目錄 `app`內部的結構和一般的Rails應用差不多,都包含 `assets`, `controllers`, `helpers`, `mailers`, `models` and `views` 等文件。`helpers`, `mailers` and `models` 文件夾是空的,我們就不詳談了。我們將會在將來的章節中討論引擎的模型的時候,深入介紹。 `app/assets`文件夾包含`images`, `javascripts`和`stylesheets`,這些你在一個Rails應用中應該很熟悉了。不同在于,它們每個文件夾下包含一個和引擎同名的子目錄,因為引擎是命名空間化的,那么assets也會遵循這一規定 。 `app/controllers`文件夾下有一個`blorgh`文件夾,他包含一個名為`application_controller.rb`的文件。這個文件為引擎提供控制器的一般功能。`blorgh`文件夾是專屬于`blorgh`引擎的,通過命名空間化的目錄結構,可以很好的將引擎的控制器與外部隔離起來,免受其它引擎或Rails應用的影響。 提示:在引擎內部的`ApplicationController`類命名方式和Rails 應用類似是為了方便你將Rails應用和引擎整合。 最后,`app/views` 文件夾包含一個`layouts`文件。他包含一個`blorgh/application.html.erb`文件。這個文件可以為你的引擎定制視圖。如果這個引擎被當作獨立的組件使用,那么你可以通過這個視圖文件來定制引擎的視圖,就和Rails應用中的`app/views/layouts/application.html.erb`一樣、 如果你不希望強制引擎的使用者使用你的布局樣式,那么可以刪除這個文件,使用其他控制器的視圖文件。 ##### 2.1.3 `bin` 目錄 這個目錄包含了一個`bin/rails`文件,它為你像在Rails應用中使用`rails` 等命令提供了支持,比如為該引擎生成模型和視圖等操作: ``` $ bin/rails g model ``` 必須要注意的是,在引擎內部使用命令行工具生成的組件都會自動調用 `isolate_namespace`方法,以達到組件命名空間化的目的。 ##### 2.1.4 `test`目錄 `test`目錄是引擎執行測試的地方,為了方便測試,`test/dummy`內置了一個精簡版本的Rails 應用,這個應用可以和引擎整合,方便測試,他在`test/dummy/config/routes.rb` 中的聲明如下: ``` Rails.application.routes.draw do mount Blorgh::Engine => "/blorgh" end ``` mounts這行的意思是Rails應用只能通過`/blorgh`路徑來訪問引擎。 在測試目錄下面有一個`test/integration`子目錄,該子目錄是為了實現引擎的的交互測試而存在的。其它的目錄也可以如此創建。舉個例子,你想為你的模型創建一個測試目錄,那么他的文件結構和`test/models`是一樣的。 ### 3 引擎功能簡介 本章中創建的引擎需要提供發布主題, 主題評論,關注[Getting Started Guide](getting_started.html)某人是否有新主題發布等功能。 #### 3.1 生成一個Article 資源 一個博客引擎首先要做的是生成一個`Article` 模型和相關的控制器。為了快速生成這些,你可以使用Rails的generator和 scaffold命令來實現: ``` $ bin/rails generate scaffold article title:string text:text ``` 這個命令執行后會得到如下輸出: ``` invoke active_record create db/migrate/[timestamp]_create_blorgh_articles.rb create app/models/blorgh/article.rb invoke test_unit create test/models/blorgh/article_test.rb create test/fixtures/blorgh/articles.yml invoke resource_route route resources :articles invoke scaffold_controller create app/controllers/blorgh/articles_controller.rb invoke erb create app/views/blorgh/articles create app/views/blorgh/articles/index.html.erb create app/views/blorgh/articles/edit.html.erb create app/views/blorgh/articles/show.html.erb create app/views/blorgh/articles/new.html.erb create app/views/blorgh/articles/_form.html.erb invoke test_unit create test/controllers/blorgh/articles_controller_test.rb invoke helper create app/helpers/blorgh/articles_helper.rb invoke test_unit create test/helpers/blorgh/articles_helper_test.rb invoke assets invoke js create app/assets/javascripts/blorgh/articles.js invoke css create app/assets/stylesheets/blorgh/articles.css invoke css create app/assets/stylesheets/scaffold.css ``` scaffold生成器做的第一件事情是執行生成`active_record`操作,這將會為資源生成一個模型和遷移集,這里要注意的是,生成的遷移集的名字是 `create_blorgh_articles`而非Raisl應用中`create_articles`。這歸功于`Blorgh::Engine`類中`isolate_namespace`方法。這里的模型也是命名空間化的,本來應該是`app/models/article.rb`,現在被 `app/models/blorgh/article.rb`取代。 接下來,模型的單元測試`test_unit`生成器會生成一個測試文件`test/models/blorgh/article_test.rb`(有別于`test/models/article_test.rb`),和一個fixture`test/fixtures/blorgh/articles.yml`文件 接下來,該資源作為引擎的一部分會被插入`config/routes.rb`中。該引擎的資源`resources :articles`在`config/routes.rb`的聲明如下: ``` Blorgh::Engine.routes.draw do resources :articles end ``` 這里需要注意的是該資源的路徑已經和引擎`Blorgh::Engine` 關聯上了,就像普通的`YourApp::Application`一樣。這樣訪問引擎的資源路徑就被限制在特定的范圍。可以提供給[test directory](#test-directory)訪問。這樣也可以讓引擎的資源與Rails應用隔離開來。具體的詳情虧參考[Routes](#routes)。 接下來,`scaffold_controller`生成器被觸發了,生成一個名為`Blorgh::ArticlesController`的控制器(`app/controllers/blorgh/articles_controller.rb`),以及和控制器相關的視圖`app/views/blorgh/articles`。這個生成器同時也會自動為控制器生成一個測試用例(`test/controllers/blorgh/articles_controller_test.rb`)和幫助方法(`app/helpers/blorgh/articles_controller.rb`)。 生成器創建的所有對象幾乎都是命名空間化的,控制器的類被定義在`Blorgh`模塊中: ``` module Blorgh class ArticlesController < ApplicationController ... end end ``` 提示:`Blorgh::ApplicationController`類繼承了`ApplicationController`類,而非Rails應用的`ApplicationController`類。 `app/helpers/blorgh/articles_helper.rb`中的helper模塊也是命名空間化的: `ruby module Blorgh module ArticlesHelper ... end end` 這樣有助于避免和其它引擎或應用的同名資源發生沖突。 最后,生成該資源相關的樣式表和js腳本文件,文件路徑分別是`app/assets/javascripts/blorgh/articles.js` 和 `app/assets/stylesheets/blorgh/articles.css`。稍后你將了解如何使用它們。 一般情況下,基本的樣式表并不會應用到引擎中,因為引擎的布局文件`app/views/layouts/blorgh/application.html.erb`并沒載入。如果要讓基本的樣式表文件對引擎生效。必須在`&lt;head&gt;`標簽內插入如下代碼: ``` <%= stylesheet_link_tag "scaffold" %> ``` 現在,你已經了解了在引擎根目錄下使用 scaffold 生成器進行數據庫創建和遷移的整個過程,接下來,在`test/dummy`目錄下運行`rails server` 后,用瀏覽器打開`http://localhost:3000/blorgh/articles` 后,隨便瀏覽一下,剛才你生成的第一個引擎的功能。 如果你喜歡在控制臺工作,那么`rails console`就像一個Rails應用。記住:`Article`是命名空間化的,所以你必須使用`Blorgh::Article`來訪問它。 ``` >> Blorgh::Article.find(1) => #<Blorgh::Article id: 1 ...> ``` 最后要做的一件事是讓`articles`資源通過引擎的根目錄就能訪問。比如我打開`http://localhost:3000/blorgh`后,就能看到一個博客的主題列表。要實現這個目的,我們可以在引擎的`config/routes.rb`中做如下配置: ``` root to: "articles#index" ``` 現在人們不需要到引擎的`/articles`目錄下瀏覽主題了,這意味著`http://localhost:3000/blorgh`獲得的內容和`http://localhost:3000/blorgh/articles`是相同的。 #### 3.2 生成評論資源 現在,這個引擎可以創建一個新主題,那么自然需要能夠評論的功能。為了實現這個功能,你需要生成一個評論模型,以及和評論相關的控制器,并修改主題的結構用以顯示評論和添加評論。 在Rails應用的根目錄下,運行模型生成器,生成一個`Comment`模型,相關的表包含下面兩個字段:整型 `article_id`和文本`text`。 ``` $ bin/rails generate model Comment article_id:integer text:text ``` 上述操作將會輸出下面的信息: ``` invoke active_record create db/migrate/[timestamp]_create_blorgh_comments.rb create app/models/blorgh/comment.rb invoke test_unit create test/models/blorgh/comment_test.rb create test/fixtures/blorgh/comments.yml ``` 生成器會生成必要的模型文件,由于是命名空間化的,所以會在`blorgh`目錄下生成`Blorgh::Comment`類。然后使用數據遷移命令對blorgh_comments表進行操作: ``` $ rake db:migrate ``` 為了在主題中顯示評論,需要在`app/views/blorgh/articles/show.html.erb`的 "Edit" 按鈕之前添加如下代碼: ``` <h3>Comments</h3> <%= render @article.comments %> ``` 上述代碼需要為評論在`Blorgh::Article`模型中添加一個"一對多"(`has_many`)的關聯聲明。為了添加上述聲明,請打開`app/models/blorgh/article.rb`,并添加如下代碼: ``` has_many :comments ``` 修改過的模型關系是這樣的: ``` module Blorgh class Article < ActiveRecord::Base has_many :comments end end ``` 提示: 因為 `一對多`(`has_many`) 的關聯是在`Blorgh` 內部定義的,Rails明白你想為這些對象使用`Blorgh::Comment`模型。所以不需要特別使用類名來聲明。 接下來,我們需要為主題提供一個表單提交評論,為了實現這個功能,請在 `app/views/blorgh/articles/show.html.erb` 中調用 `render @article.comments` 方法來顯示表單: ``` <%= render "blorgh/comments/form" %> ``` 接下來,上述代碼中的表單必須存在才能被渲染,我們需要做的就是在`app/views/blorgh/comments`目錄下創建一個`_form.html.erb`文件: ``` <h3>New comment</h3> <%= form_for [@article, @article.comments.build] do |f| %> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <%= f.submit %> <% end %> ``` 當表單被提交后,它將通過路徑`/articles/:article_id/comments`給引擎發送一個`POST`請求。現在這個路徑還不存在,所以我們可以修改`config/routes.rb`中的`resources :articles`的相關路徑來實現它: ``` resources :articles do resources :comments end ``` 給表單請求創建一個和評論相關的嵌套路徑。 現在路徑創建好了,相關的控制器卻不存在,為了創建它們,我們使用命令行工具來創建它們: ``` $ bin/rails g controller comments ``` 執行上述操作后,會輸出下面的信息: ``` create app/controllers/blorgh/comments_controller.rb invoke erb exist app/views/blorgh/comments invoke test_unit create test/controllers/blorgh/comments_controller_test.rb invoke helper create app/helpers/blorgh/comments_helper.rb invoke test_unit create test/helpers/blorgh/comments_helper_test.rb invoke assets invoke js create app/assets/javascripts/blorgh/comments.js invoke css create app/assets/stylesheets/blorgh/comments.css ``` 表單通過路徑`/articles/:article_id/comments`提交`POST`請求后,`Blorgh::CommentsController`會響應一個`create`動作。 這個的動作在`app/controllers/blorgh/comments_controller.rb`的定義如下: ``` def create @article = Article.find(params[:article_id]) @comment = @article.comments.create(comment_params) flash[:notice] = "Comment has been created!" redirect_to articles_path end private def comment_params params.require(:comment).permit(:text) end ``` 最后,我們希望在瀏覽主題時顯示和主題相關的評論,但是如果你現在想提交一條評論,會發現遇到如下錯誤:  ``` Missing partial blorgh/comments/comment with {:handlers=>[:erb, :builder], :formats=>[:html], :locale=>[:en, :en]}. Searched in: * "/Users/ryan/Sites/side_projects/blorgh/test/dummy/app/views" * "/Users/ryan/Sites/side_projects/blorgh/app/views" ``` 顯示上述錯誤是因為引擎無法知道和評論相關的內容。Rails 應用會首先去該應用的(`test/dummy`) `app/views`目錄搜索,之后才會到引擎的`app/views` 目錄下搜索匹配的內容。當找不到匹配的內容時,會拋出異常。引擎知道去`blorgh/comments/comment`目錄下搜索,是因為模型對象是從`Blorgh::Comment`接收到請求的。 現在,為了顯示評論,我們需要創建一個新文件 `app/views/blorgh/comments/_comment.html.erb`,并在該文件中添加如下代碼: ``` <%= comment_counter + 1 %>. <%= comment.text %> ``` 本地變量 `comment_counter`是通過`&lt;%= render @article.comments %&gt;`獲取的。這個變量是評論計數器,用來顯示評論總數。 現在,我們完成一個帶評論功能的博客引擎后,接下來我們將介紹如何將引擎與Rails應用整合。 ### 4 和Rails應用整合 在Rails應用中可以很方便的使用引擎,本節將介紹如何將引擎和Rails應用整合。當然通常會把引擎和Rails應中的`User`類關聯起來。 #### 4.1 整合前的準備工作 首先,引擎需要在一個Rails應用中的`Gemfile`進行聲明。如果我們無法知道Rails應用中是否有這些聲明,那么我們可以在引擎目錄之外創建一個新的Raisl應用: ``` $ rails new unicorn ``` 一般而言,在Gemfile聲明引擎和在Rails應用的一般Gem聲明沒有區別: ``` gem 'devise' ``` 但是,假如你在自己的本地機器上開發`blorgh`引擎,那么你需要在`Gemfile`中特別聲明`:path`項: ``` gem 'blorgh', path: "/path/to/blorgh" ``` 運行`bundle`命令,安裝gem 。 如前所述,在`Gemfile`中聲明的gem將會與Rails框架一起加載。應用會從引擎中加載 `lib/blorgh.rb`和`lib/blorgh/engine.rb`等與引擎相關的主要文件。 為了在Rails應用內部調用引擎,我們必須在Rails應用的`config/routes.rb`中做如下聲明: ``` mount Blorgh::Engine, at: "/blog" ``` 上述代碼的意思是引擎將被整合到Rails應用中的"/blog"下。當Rails應用通過 `rails server`啟動時,可通過`http://localhost:3000/blog`訪問。 提示: 對于其他引擎,比如 `Devise` ,它在處理路徑的方式上稍有不同,可以通過自定義的助手方法比如`devise_for`來處理路徑。這些路徑助理方法工作千篇一律,為引擎大部分功能提供預定義路徑的個性化支持。 #### 4.2 建立引擎 和引擎相關的兩個`blorgh_articles` 和 `blorgh_comments`表需要遷移到Rails應用數據庫中,以保證引擎的模型能正確查詢。遷移引擎的數據可以使用下面的命令: ``` $ rake blorgh:install:migrations ``` 如果你有多個引擎需要數據遷移,可以使用`railties:install:migrations`命令來實現: ``` $ rake railties:install:migrations ``` 第一次運行上述命令的時候,將會從引擎中復制所有的遷移集。當下次運行的時候,他只會遷移沒被遷移過的數據。第一次運行該命令會顯示如下信息: ``` Copied migration [timestamp_1]_create_blorgh_articles.rb from blorgh Copied migration [timestamp_2]_create_blorgh_comments.rb from blorgh ``` 第一個時間戳(`[timestamp_1]`)將會是當前時間,接著第二個時間戳(`[timestamp_2]`) 將會是當前時間+1妙。這樣做的原因是之前已經為引擎做過數據遷移操作。 在Rails應用中為引擎做數據遷移可以簡單的使用`rake db:migrate` 執行操作。當通過`http://localhost:3000/blog`訪問引擎的時候,你會發現主題列表是空的。這是因為在應用中創建的表與在引擎中創建的表是不同的。接下來你將發現應用中的引擎和獨立環境中的引擎有很多不同之處。 如果你只想對某一個引擎執行數據遷移操作,那么可以通過`SCOPE`聲明來實現: ``` rake db:migrate SCOPE=blorgh ``` 這將有利于你的引擎執行數據遷移的回滾操作。 如果想讓引擎的數據回到原始狀態,那么可以執行下面的操作: ``` rake db:migrate SCOPE=blorgh VERSION=0 ``` #### 4.3 訪問Rails應用中的類 ##### 4.3.1 訪問Rails應用中的模型 當一個引擎創建之后,那么就需要Rails應用提供一個專屬的類,將引擎和Rails應用關聯起來。在本例中,`blorgh`引擎需要Rails應用提供作者來發表主題和評論。 一個典型的Rails應用會有一個`User`類來實現發布主題和評論的功能。也許某些應用里面會用`Person`類來做這些事情。因此,引擎不應該硬編碼到一個`User`類中。 為了簡單起見,我們的應用將會使用`User`類來實現和引擎的關聯。那么我們可以在應用中使用命令: ``` rails g model user name:string ``` 在這里執行`rake db:migrate`命令是為了我們的應用中有`users`表,以備將來使用。 為了簡單起見,主題表單也會添加一個新的字段`author_name`,這樣方便用戶填寫他們的名字。 當用戶提交了他們的名字后,引擎將會判斷是否存在該用戶,如果不存在,就將該用戶添加到數據庫里面,并通過`User`對象把該用戶和主題關聯起來。 首先需要在引擎內部的`app/views/blorgh/articles/_form.html.erb`文件中添加 `author_name`項。這些內容可以添加到`title`之前,代碼如下: ``` <div class="field"> <%= f.label :author_name %><br> <%= f.text_field :author_name %> </div> ``` 接下來我們需要更新`Blorgh::ArticleController#article_params`方法接受參數的格式: ``` def article_params params.require(:article).permit(:title, :text, :author_name) end ``` 模型`Blorgh::Article`需要添加一些代碼把`author_name`和`User`對象關聯起來。以確保保存主題時,主題相關的 `author`也被同時保存了。同時我們需要為這個字段定義一個`attr_accessor`。以方便我們讀取或設置它的屬性。 上述工作完成后,你需要為`author_name`添加一個屬性讀寫器(`attr_accessor`),調用在`app/models/blorgh/article.rb`的`before_save`方法以便關聯。`author` 將會通過硬編碼的方式和`User`關聯: ``` attr_accessor :author_name belongs_to :author, class_name: "User" before_save :set_author private def set_author self.author = User.find_or_create_by(name: author_name) end ``` 和`author`關聯的`User`類,成了引擎和Rails應用之間聯系的紐帶。與此同時,還需要把`blorgh_articles`和 `users` 表進行關聯。因為通過`author`關聯,那么需要給`blorgh_articles`表添加一個`author_id`字段來實現關聯。 為了生成這個新字段,我們需要在引擎中執行如下操作: ``` $ bin/rails g migration add_author_id_to_blorgh_articles author_id:integer ``` 提示:假如數據遷移命令后面跟了一個字段聲明。那么Rails會認為你想添加一個新字段到聲明的表中,而無需做其他操作。 這個數據遷移操作必須在Rails應用中執行,為此,你必須保證是第一次在命令行中執行下面的操作: ``` $ rake blorgh:install:migrations ``` 需要注意的是,這里只會發生一次數據遷移,這是因為前兩個數據遷移拷貝已經執行過遷移操作了。 ``` NOTE Migration [timestamp]_create_blorgh_articles.rb from blorgh has been skipped. Migration with the same name already exists. NOTE Migration [timestamp]_create_blorgh_comments.rb from blorgh has been skipped. Migration with the same name already exists. Copied migration [timestamp]_add_author_id_to_blorgh_articles.rb from blorgh ``` 運行數據遷移命令: ``` $ rake db:migrate ``` 現在所有準備工作都就緒了。上述操作實現了Rails應用中的`User`表和作者關聯,引擎中的`blorgh_articles`表和主題關聯。 最后,主題的作者將會顯示在主題頁面。在`app/views/blorgh/articles/show.html.erb`文件中的`Title`之前添加如下代碼: ``` <p> <b>Author:</b> <%= @article.author %> </p> ``` 使用`&lt;%=` 標簽和`to_s`方法將會輸出`@article.author`。默認情況下,這看上去很丑: ``` #<User:0x00000100ccb3b0> ``` 這不是我們希望看到的,所以最好顯示用戶的名字。為此,我去需要給Rails應用中的`User`類添加`to_s`方法: ``` def to_s name end ``` 現在,我們將看到主題的作者名字 。 ##### 4.3.2 與控制器交互 Rails應用的控制器一般都會和權限控制,會話變量訪問模塊共享代碼,因為它們都是默認繼承自 `ApplicationController`類。Rails的引擎因為是命名空間化的,和主應用獨立的模塊。所以每個引擎都會有自己的`ApplicationController`類。這樣做有利于避免代碼沖突,但很多時候,引擎控制器需要調用主應用的`ApplicationController`。這里有一個簡單的方法是讓引擎的控制器繼承主應用的`ApplicationController`。我們的Blorgh引擎會在`app/controllers/blorgh/application_controller.rb`中實現上述操作: ``` class Blorgh::ApplicationController < ApplicationController end ``` 一般情況下,引擎的控制器是繼承自`Blorgh::ApplicationController`,所以,做了上述改變后,引擎可以訪問主應用的`ApplicationController`了,也就是說,它變成了主應用的一部分。 上述操作的一個必要條件是:和引擎相關的Rails應用必須包含一個`ApplicationController`類。 #### 4.4 配置引擎 本章節將介紹如何讓`User`類可配置化。下面我們將介紹配置引擎的細節。 ##### 4.4.1 配置應用的配置文件 接下來的內容我們將講述如何讓應用中諸如`User`的類對象為引擎提供定制化的服務。如前所述,引擎要訪問應用中的類不一定每次都叫`User`,所以我來實現可定制化的訪問,必須在引擎里面設置一個名為`author_class`和應用中的`User`類進行交互。 為了定義這個設置,你將在引擎的`Blorgh` 模塊中聲明一個`mattr_accessor`方法和`author_class`關聯。在引擎中的`lib/blorgh.rb`代碼如下: ``` mattr_accessor :author_class ``` 這個方法的功能和它的兄弟`attr_accessor`和`cattr_accessor`功能類似,但是特別提供了一個方法,可以根據指定名字來對類或模塊訪問。我們使用它的時候,必須加上`Blorgh.author_class`前綴。 接下來要做的是通過新的設置器來選擇`Blorgh::Article`的模型,將模型關聯`belongs_to`(`app/models/blorgh/article.rb`)修改如下: ``` belongs_to :author, class_name: Blorgh.author_class ``` 模型`Blorgh::Article`中的`set_author`方法也可以使用這個類: ``` self.author = Blorgh.author_class.constantize.find_or_create_by(name: author_name) ``` 為了確保`author_class`調用`constantize`的結果一致,你需要重載`lib/blorgh.rb`中`Blorgh` 模塊的`author_class`的get方法,確保在獲取返回值之前調用`constantize`方法: `ruby def self.author_class @@author_class.constantize end` 上述代碼將會讓`set_author` 方法變成這樣: ``` self.author = Blorgh.author_class.find_or_create_by(name: author_name) ``` 總之,這樣會更明確它的行為,`author_class`方法會保證返回一個`Class`對象。 我們讓`author_class`方法返回一個`Class`替代`String`后,我們也必須修改`Blorgh::Article`模塊中的`belongs_to`定義: ``` belongs_to :author, class_name: Blorgh.author_class.to_s ``` 為了讓這些配置在應用中生效,必須使用一個初始化器。使用初始化器可以保證這種配置在Rails應用調用引擎模塊之前就生效,因為應用和引擎交互時也許需要用到某些配置。 在應用中的`config/initializers/blorgh.rb`添加一個新的初始化器,并添加如下代碼: ``` Blorgh.author_class = "User" ``` 警告:使用`String`版本的類對象要比使用類對象本身更好。如果你使用類對象,Rails會嘗試加載和類相關的數據庫表。如果這個表不存在,就會拋出異常。所以,稍后在引擎中最好使用`String`類型,并且把類用`constantize`方法轉換一下。 接下來我們創建一個新主題,除了讓引擎讀取`config/initializers/blorgh.rb`中的類信息之外,你將發現它和之前沒什么區別, 這里對類沒有嚴格的定義,只是提供了一個類必須做什么的指導。引擎也只是調用`find_or_create_by`方法來獲取符合條件的類對象。當然這個對象也可以被其他對象引用。 ##### 4.4.2 配置引擎 在引擎內部,有很多配置引擎的方法,比如initializers, internationalization和其他配置項。一個Rails引擎和一個Rails應用具有很多相同的功能。實際上一個Rails應用就是一個超級引擎。 如果你想使用一個初始化器,必須在引擎載入之前使用,配置文件在`config/initializers` 目錄下。這個目錄的詳細使用說明在[Initializers section](configuring.html#initializers)中,它和一個應用中的`config/initializers`文件相對目錄是一致的。可以把它當作一個Rails應用中的初始化器來配置。 關于本地文件,和一個應用中的目錄類似,都在`config/locales`目錄下。 ### 5 引擎測試 生成一個引擎后,引擎內部的`test/dummy`目錄下會生成一個簡單的Rails應用。這個應用被用來給引擎提供集成測試環境。你可以擴展這個應用的功能來測試你的引擎。 `test`目錄將會被當作一個典型的Rails測試環境,允許單元測試,功能測試和交互測試。 #### 5.1 功能測試 在編寫引擎的功能測試時,我們會假定這個引擎會在一個應用中使用。`test/dummy`目錄中的應用和你引擎結構差不多。這是因為建立測試環境后,引擎需要一個宿主來測試它的功能,特別是控制器。這意味著你需要在一個控制器功能測試函數中下如下代碼: ``` get :index ``` 這似乎不能稱為函數,因為這個應用不知道如何給引擎發送的請求做響應,除非你明確告訴他怎么做。為此,你必須在請求的參數中加上`:use_route`選項來聲明: ``` get :index, use_route: :blorgh ``` 上述代碼會告訴Rails應用你想讓它的控制器響應一個`GET`請求,并執行`index`動作,但是你最好使用引擎的路徑來代替。 另外一種方法是在你的測試總建立一個setup方法,把`Engine.routes`賦值給變量`@routes` 。 ``` setup do @routes = Engine.routes end ``` 上訴操作也同時保證了引擎的url助手方法在你的測試中正常使用。 ### 6 引擎優化 本章節將介紹在Rails應用中如何添加或重載引擎的MVC功能。 #### 6.1 重載模型和控制器 應用中的公共類可以擴展引擎的模型和控制器的功能。(因為模型和控制器類都繼承了Rails應用的特定功能)應用中的公共類和引擎只是對模型和控制器根據需要進行了擴展。這種模式通常被稱為裝飾模式。 舉個例子,`ActiveSupport::Concern`類使用`Class#class_eval`方法擴展了他的功能。 ##### 6.1.1 裝飾器的特點以及加載代碼 因為裝飾器不是引用Rails應用本身,Rails自動載入系統不會識別和載入你的裝飾器。這意味著你需要用代碼聲明他們。 這是一個簡單的例子: ``` # lib/blorgh/engine.rb module Blorgh class Engine < ::Rails::Engine isolate_namespace Blorgh config.to_prepare do Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c| require_dependency(c) end end end end ``` 上述操作不會應用到當前的裝飾器,但是在引擎中添加的內容不會影響你的應用。 ##### 6.1.2 使用 Class#class_eval 方法實現裝飾模式 **添加** `Article#time_since_created`方法: ``` # MyApp/app/decorators/models/blorgh/article_decorator.rb Blorgh::Article.class_eval do def time_since_created Time.current - created_at end end ``` ``` # Blorgh/app/models/article.rb class Article < ActiveRecord::Base has_many :comments end ``` **重載** `Article#summary`方法: ``` # MyApp/app/decorators/models/blorgh/article_decorator.rb Blorgh::Article.class_eval do def summary "#{title} - #{truncate(text)}" end end ``` ``` # Blorgh/app/models/article.rb class Article < ActiveRecord::Base has_many :comments def summary "#{title}" end end ``` ##### 6.1.3 使用ActiveSupport::Concern類實現裝飾模式 使用`Class#class_eval`方法可以應付一些簡單的修改。但是如果要實現更復雜的操作,你可以考慮使用[`ActiveSupport::Concern`](http://edgeapi.rubyonrails.org/classes/ActiveSupport/Concern.html)。`ActiveSupport::Concern`管理著所有獨立模塊的內部鏈接指令,并且允許你在運行時聲明模塊代碼。 **添加** `Article#time_since_created`方法和**重載** `Article#summary`方法: ``` # MyApp/app/models/blorgh/article.rb class Blorgh::Article < ActiveRecord::Base include Blorgh::Concerns::Models::Article def time_since_created Time.current - created_at end def summary "#{title} - #{truncate(text)}" end end ``` ``` # Blorgh/app/models/article.rb class Article < ActiveRecord::Base include Blorgh::Concerns::Models::Article end ``` ``` # Blorgh/lib/concerns/models/article module Blorgh::Concerns::Models::Article extend ActiveSupport::Concern # 'included do' causes the included code to be evaluated in the # context where it is included (article.rb), rather than being # executed in the module's context (blorgh/concerns/models/article). included do attr_accessor :author_name belongs_to :author, class_name: "User" before_save :set_author private def set_author self.author = User.find_or_create_by(name: author_name) end end def summary "#{title}" end module ClassMethods def some_class_method 'some class method string' end end end ``` #### 6.2 視圖重載 Rails在尋找一個需要渲染的視圖時,首先會去尋找應用的`app/views`目錄下的文件。如果找不到,那么就會去當前應用目錄下的所有引擎中找`app/views`目錄下的內容。 當一個應用被要求為`Blorgh::ArticlesController`的`index`動作渲染視圖時,它首先會在應用目錄下去找`app/views/blorgh/articles/index.html.erb`,如果找不到,它將深入引擎內部尋找。 你可以在應用中創建一個新的`app/views/blorgh/articles/index.html.erb`文件來重載這個視圖。接下來你會看到你改過的視圖內容。 修改`app/views/blorgh/articles/index.html.erb`中的內容,代碼如下: ``` <h1>Articles</h1> <%= link_to "New Article", new_article_path %> <% @articles.each do |article| %> <h2><%= article.title %></h2> <small>By <%= article.author %></small> <%= simple_format(article.text) %> <hr> <% end %> ``` #### 6.3 路徑 引擎中的路徑默認是和Rails應用隔離開的。主要通過`Engine`類的`isolate_namespace`方法 實現的。這意味著引擎和Rails應可以擁有同名的路徑,但卻不會沖突。 引擎內部的`config/routes.rb`中的`Engine`類是這樣綁定路徑的: ``` Blorgh::Engine.routes.draw do resources :articles end ``` 因為擁有相對獨立的路徑,如果你希望在應用內部鏈接到引擎的某個地方,你需要使用引擎的路徑代理方法。如果調用普通的路徑方法,比如`articles_path`等,將不會得到你希望的結果。 舉個例子。下面的`articles_path`方法根據情況自動識別,并渲染來自應用或引擎的內容。 ``` <%= link_to "Blog articles", articles_path %> ``` 為了確保這個路徑使用引擎的`articles_path`方法,我們必須使用路徑代理方法來實現: ``` <%= link_to "Blog articles", blorgh.articles_path %> ``` 如果你希望在引擎內部訪問Rails應用的路徑,可以使用`main_app`方法: ``` <%= link_to "Home", main_app.root_path %> ``` 如果你在引擎中使用了上訴方法,那么這將一直指向Rails應用的根目錄。如果你沒有使用`main_app`的 `routing proxy`路徑代理調用方法,那么會根據調用源來指向引擎或Rails應用的根目錄。 如果你引擎內的模板渲染想調用一個應用的路徑幫助方法,這可能導致一個未定義的方法調用異常。如果你想解決這個問題,必須確保在引擎內部調用Rails應用的路徑幫助方法時加上`main_app`前綴。 #### 6.4 渲染頁面相關的Assets文件 引擎內部的Assets文件位置和Rails應用的的相似。因為引擎類是繼承自`Rails::Engine`的。應用會自動去引擎的`aapp/assets`和`lib/assets`目錄搜索和頁面渲染相關的文件。 像其他引擎組件一樣,assets文件是可以命名空間化的。這意味著如果你有一個名為`style.css`的話,那么他的存放路徑是`app/assets/stylesheets/[engine name]/style.css`, 而非 `app/assets/stylesheets/style.css`. 如果資源文件沒有命名空間化,很有可能引擎的宿主中有一個和引擎同名的資源文件,這就會導致引擎相關的資源文件被忽略或覆蓋。 假如你想在應用的中引用一個名為`app/assets/stylesheets/blorgh/style.css`文件, ,只需要使用`stylesheet_link_tag`就可以了: ``` <%= stylesheet_link_tag "blorgh/style.css" %> ``` 你也可以在Asset Pipeline中聲明你的資源文件是獨立于其他資源文件的: ``` /* *= require blorgh/style */ ``` 提示: 如果你使用的是Sass或CoffeeScript語言,那么需要在你的引擎的`.gemspec`文件中設定相對路徑。 #### 6.5 頁面資源文件分組和預編譯 在某些情況下,你的引擎內部用到的資源文件,在Rails應用宿主中是不會用到的。舉個例子,你為引擎創建了一個管理頁面,它只在引擎內部使用,在這種情況下,Rails應用宿主并不需要用到`admin.css` 和`admin.js`文件,只是gem內部的管理頁面需要用到它們。那么應用宿主就沒必要添加`"blorgh/admin.css"`到他的樣式表文件中 ,這種情況下,你可以預編譯這些文件。這會在你的引擎內部添加一個`rake assets:precompile`任務。 你可以在引擎的`engine.rb`中定義需要預編譯的資源文件: ``` initializer "blorgh.assets.precompile" do |app| app.config.assets.precompile += %w(admin.css admin.js) end ``` 想要了解更多詳情,可以參考 [Asset Pipeline guide](asset_pipeline.html) #### 6.6 其他Gem依賴項 一個引擎的相關依賴項會在引擎的根目錄下的`.gemspec`中聲明。因為引擎也許會被當作一個gem安裝到Rails應用中。如果在`Gemfile`中聲明依賴項,那么這些依賴項就會被認為不是一個普通Gem,所以他們不會被安裝,這會導致引擎發生故障。 為了讓引擎被當作一個普通的Gem安裝,需要聲明他的依賴項已經安裝過了。那么可以在引擎根目錄下的`.gemspec`文件中添加`Gem::Specification`配置項: ``` s.add_dependency "moo" ``` 聲明一個依賴項只作為開發應用時的依賴項,可以這么做: ``` s.add_development_dependency "moo" ``` 所有的依賴項都會在執行`bundle install`命令時安裝。gem開發環境的依賴項僅會在測試時用到。 注意,如果你希望引擎引用依賴項時馬上引用。你應該在引擎初始化時就引用它們,比如: ``` require 'other_engine/engine' require 'yet_another_engine/engine' module MyEngine class Engine < ::Rails::Engine end end ``` ### 反饋 歡迎幫忙改善指南質量。 如發現任何錯誤,歡迎修正。開始貢獻前,可先行閱讀[貢獻指南:文檔](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation)。 翻譯如有錯誤,深感抱歉,歡迎 [Fork](https://github.com/ruby-china/guides/fork) 修正,或至此處[回報](https://github.com/ruby-china/guides/issues/new)。 文章可能有未完成或過時的內容。請先檢查 [Edge Guides](http://edgeguides.rubyonrails.org) 來確定問題在 master 是否已經修掉了。再上 master 補上缺少的文件。內容參考 [Ruby on Rails 指南準則](ruby_on_rails_guides_guidelines.html)來了解行文風格。 最后,任何關于 Ruby on Rails 文檔的討論,歡迎到 [rubyonrails-docs 郵件群組](http://groups.google.com/group/rubyonrails-docs)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看