Rails Plugins

从本书开篇,我们就不断提到 Rails 中一直贯彻的约定大于配置思想。而我们已经在最近几章讲解了 Rails 的底层 gem。现在是时候将两者结合起来,Rails 提供了一组实用的默认 gem,而且这些 gem 你也可以自行添加和修改。

在 Rails 中,gem 是将函数插件化的最基本方式。为了不进行抽象的描述,我们会选择一些插件,并通过它们说明插件的安装及功能。事实上插件会为你的日常工作提供莫大帮助!

我们从金钱相关的插件开始。

使用 Active Merchant 处理信用卡流程

在 161 页我们临时处理了信用卡流程,让用户可以正常支付订单。尽管 Rails 的内核中没有相关功能,但有一个 gem 提供对信用卡的支持。

之前已经了解过如何在应用中管理 gem,所以现在要编辑 Gemfile。本章中我们要学习一些插件,所以我们一次性将它们添加至 Gemfile 中。你可以在文件的任何地方配置,我们选择在文件结尾处理。

gem 'activemerchant', '~> 1.31'
gem 'haml', '~> 4.0'
gem 'kaminari', '~> 0.14'

一定要注意指定最小版本号,这是由我们的最佳实践得来,如果提高了版本号限制可能导致兼容问题。

我们添加的几个 gem 会分不同的章节讲解,本节我们只讨论 Active Merchant。

接着使用 bundle 命令安装依赖。

bundle install

要根据自身的操作系统和使用登录用户,也许你需要以 root 身份运行。

bundle 实践上代理了许多工作。它会检查一遍 gem 依赖,查找运行配置,然后下载和安装必要的组件。不过现在我们不关心,我们只是添加了组件,然后再确保 bundler 将 gem 安装成功。

在安装或更新 gem 后还要处理一件事 —— 重启服务器。虽然 Rails 尽忠职守,也对应用进行了相应的更新,但在添加或替换整个 gem 时它也无法做到十全十美。虽然本节中不会用到服务器,但后面会用到。确认服务器正在运行 Depot。

为了展示相应功能,我们要创建一个脚本,可以将此脚本放置在 script 路径中。

credit_card = ActiveMerchant::Billing::CreditCard.new(
  number:     '4111111111111111',
  month:      '8',
  year:       '2009',
  first_name: 'Tobias',
  last_name:  'Luetke',
  verification_value:  '123'
)

puts "Is #{credit_card.number} valid?  #{credit_card.valid?}"

脚本中的代码并不多,它创建了 ActiveMerchant::Billing::CreditCard 类的实例,然后调用了 valid?() 方法,让我们运行脚本。

rails runner script/creditcard.rb
Is 4111111111111111 valid? false

我们也没有额外做什么它就正常运行了。这些并不需要 require 声明,在 Gemfile 中列举的 gem 的函数都可以在应用中使用。

现在你已经了解了如何在应用中使用这些方法。此时,通过 migration 向 Orders 表添加字段,至于向 view 添加字段的方法你是知道的。通过之前使用的 valid?() 方法在 model 中添加验证逻辑。如果你到 Active Merchant 的官网,甚至能找到 authorize()capture() 授权及获得支付信息,尽管实现这个功能需要你登录商务门户网站。当一切准备就绪,你就知道如何调用 controller 中的逻辑了。

仔细想想,其实你只要在 Gemfile 添加一行代码就行。

就像本章开篇说的一样,向 Gemfile 添加 gem 是扩展 Rails 最好的方式。这样做的好处可太多了,首先所有的依赖都由 Bundler 管理,其次要使用时可以立即预加载,最后部署时还能轻松打包。

这里只是简单地添加 gem。接下来让我们看看更重要的事,它为 Rails 依赖的 gem 提供了明确的替代方案。

使用 Haml 进行页面美化

首先让我们看看 Depot 中的 view 界面。下面的例子是首页界面:

<% if notice %>
<p id="notice"><%= notice %></p>
<% end %>

<h1><%= t('.title_html') %></h1>

<% cache ['store', Product.latest] do %>
  <% @products.each do |product| %>
    <% cache ['entry', product] do %>
      <div class="entry">
        <%= image_tag(product.image_url) %>
        <h3><%= product.title %></h3>
        <%= sanitize(product.description) %>
        <div class="price_line">
          <span class="price"><%= number_to_currency(product.price) %></span>
          <%= button_to t('.add_html'), line_items_path(product_id: product),
            remote: true %>
        </div>
      </div>
    <% end %>
  <% end %>
<% end %>

这段代码中包含了 HTML,还有一些 Ruby 代码。在 <%= ... %> 中的表达式结果将被转换为 HTML 并显示。

这并不是最佳解决方案,但许多 Rails 应用确实有这样的需求。而且在本书开篇时假定大家已经了解 HTML 的知识,但其实许多读者常常使用 Ruby 却才第一次接触 Rails。这种情况下只能将 HTML 作为新语言介绍。

但现在你已经试过了学习困难期,让我们探究一下这门新语言,它更加接近于生产环境中的 Ruby 代码,也就是 HTML Abstraction Markup Language(Haml)。

为了启用它,先删除刚才看到的文件

rm app/views/store/index.html.erb

接着在同一个地方创建一个新文件。

- if notice
  %p#notice= notice

%h1= t('.title_html')

- cache ['store', Product.latest] do
  - @products.each do |product|
    - cache ['entry', product] do
      .entry
        = image_tag(product.image_url)
        %h3= product.title
        = sanitize(product.description)
        .price_line
          %span.price= number_to_currency(product.price)
          = button_to t('.add_html'), line_items_path(product_id: product),
            remote: true

注意这里使用了新的扩展名 .html.haml。这表示此模板是一个 Haml 模板而不是 ERB 模板。

你注意到的第一点应该是此文件变小了,下列是对代码中每行首字母作用的一个预览:

  • 横线代表一个 Ruby 声明,并不产生任何输出

  • 百分号(%)表示一个 HTML 元素

  • 等号(=)表示一个 Ruby 表达式,它将产生一个用于显示的输出结果。可以将其处理为单独一行代码,也紧随 HTML 元素之后

  • 点号(.)和井号(#)分别定义 class 和 id 属性。可以与前面的符号结合使用,或者单独使用。如果单独使用,表示为 div 元素。

  • 表达式末尾的逗号表示连续。在上面的例子中,button_to() 的调用横跨了两行

缩进是 Haml 中的重点。if、循环或标签与同级别的缩进匹配。上面的例子中,在 h1 前的段落属于关闭状态,div 前的 h1 也是关闭状态,但 div 元素是内嵌的,分别包含了 h3 元素、span 元素和 button_to()

你也看到了,你熟悉的辅助方法都有效,比如 t()image_tag()button_to()。通过这种方式 Haml 便可以像 ERB 一样集成在应用中。不过你也可以混合使用,一部分使用 ERB 模板,另一部分使用 Haml 模板。

如果你已经安装了 Haml gem,就不需要再做额外工作。现在你只需要直接访问首页即可,显示结果如下图。

Storefront using Haml

如果你觉得它看起来很普通,那是因为之前你已经真切地见识过。如果你仔细思考一下,它就显得不那么简单,因为布局文件依然使用 ERB 模板,而首页却使用 Haml 模板。即使这样,所有的集成依然保持无缝衔接并且影响面小。

尽管相比添加任务或辅助方法这是一个更深层次的集成。接下来我们探究一番改变 Rails 核心对象的插件。

分页

有时,我们有一些商品,一些购物车,以及每个购物车或订单中的商品,但实际上我们拥有无限的订单,并且我们希望在订单页快速地显示所有订单。kaminari 插件正满足此功能。

现在上我们生成一些测试数据。虽然我们可以重复点击但电脑更擅长此事。这并不是 seed 数据,只是临时使用的数据。让我们在 script 路径中创建一个文件。

Order.transaction do
  (1..100).each do |i|
    Order.create(name: "Customer #{i}", address: "#{i} Main Street",
      email: "customer-#{i}@example.com", pay_type: "Check")
  end
end

此代码可创建一百个订单,但其中没有购买商品。如果你愿意也可以修改脚本添加购买商品。注意它是在一个事务中完成了所有工作。虽然并不完全有这个需要,但它可以加速处理流程。

我们并不需要任何 require 声明或者向数据库的初始化。这些工作都交给 Rails 处理。

rails runner script/load_orders.rb

现在准备工作完毕,接下来是向应用做出必要的修改。首先,我们要在 controller 中调用 paginate(),并在页面中传递需要显示的订单。

# controllers/orders_controller.rb

def index
  @orders = Order.order('created_at desc').page(params[:page])
end

接着在 view 底部添加链接。

<h1>Listing orders</h1>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Address</th>
      <th>Email</th>
      <th>Pay type</th>
      <th></th>
      <th></th>
      <th></th>
    </tr>
  </thead>

  <tbody>
    <% @orders.each do |order| %>
      <tr>
        <td><%= order.name %></td>
        <td><%= order.address %></td>
        <td><address%= order.email %></td>
        <td><%= order.pay_type %></td>
        <td><%=pay_type link_to 'Show', order %></td>
        <td><%= link_to 'Edit', edit_order_path(order) %></td>
        <td><%= link_to 'Destroy', order, method: :delete,
          data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Order', new_order_path %>
<!-- Here's new line -->
<p><%= paginate @orders %></p>

这就是所有的内容了。默认每页显示三条数据,只有数据超过一页才会显示链接。

controller 中通过 :per_page 参数设置每页显示的订单数,可以参考下图:

Showing ten orders out of more than a hundred

总结

虽然本章讲解了一些插件,本章的目的并不是深入了解这些插件,只是介绍了一些插件的功能。

如果包括前面章节介绍的 gem,这些插件只要添加了一些新功能(Active Merchant 和 Capistrano),向 model 对象添加方法(kaminari),添加一种新模板语言(Haml),而且添加了新数据库的接口(mysql)。

认真想想,是不是没有插件无法做到的事。

在 RailsPlugins.org 发现更多插件

现在我们已经学习了三种插件。不过还有一些知识需要学习,有以下分类:

  • 一些插件实现了 Rails 核心功能。例如,jQuery,Prototype 库是由之前的 Rails 版本默认支持。这些功能已经移入了 prototype-rails 插件。还有另一个 acts_as_tree 也是非常受欢迎的插件。像 rails_xss 为了辅助 migration 将 Rails 的版本特性进行移植。

  • 有一些插件实现了重要的共用逻辑甚至是用户接口。deviseauthlogic 实现了用户授权和 session 管理。我们在 Depot 中是自己实现了这些功能,不过我们并不推荐这样做。我们已经发现了要学习偷懒,如果有人已经实现了你所需要的插件,你便可以花时间在自己的应用中。

  • 有一些插件替换了大量的 rails 功能。比如,datamapper 替换了 ActiveRecord。cucumberrspecwebrat 即可以结合使用,也可以分开使用,它们可以同时替换测试脚本中的普通文本的测试描述、specification 和浏览器模拟。

  • airbrakeexception_notification 可以在部署服务器中管理错误。

当然,这里只描述了少量的插件。插件还在不断增加,当然在阅读本书时插件的数量肯定又增加了。

最后,你也可以创建自己的插件。不过这已经超出了本书的知识范围,你可以在 Rails 指南和文档中查看更多细节。


本文翻译自《Agile Web Development with Rails 4》,目的为学习所用,如有转载请注明出处。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容