本課時(shí)結(jié)合商品頁(yè)面,講解如何使用簡(jiǎn)潔安全的模板引擎,以及如何更改郵件模板。
前面的章節(jié)里,我們一直使用 erb 作為視圖模板,erb 可以讓我們?cè)?html 中簽入 Ruby 代碼。這樣做的好處是,我們拿到的頁(yè)面和設(shè)計(jì)師提供的頁(yè)面幾乎無(wú)任何差別,可以直接增加上我們用 Ruby 寫(xiě)的的邏輯。稍微不好的一點(diǎn)是,html 太多了,稍微處理不好,會(huì)缺失標(biāo)簽,而且不易察覺(jué)。
這時(shí)我們可以使用其他一些方案,haml 是比較常用的一個(gè)。
我們?cè)?Gemfile 中安裝 haml:
gem 'haml'
我們看一下用 haml 寫(xiě)的代碼:
%section.container
%h1= post.title
%h2= post.subtitle
.content
= post.content
下面是 erb 的寫(xiě)法。
<section class=”container”>
<h1><%= post.title %></h1>
<h2><%= post.subtitle %></h2>
<div class=”content”>
<%= post.content %>
</div>
</section>
可見(jiàn) haml 節(jié)省了我們大量的代碼,而且更接近 Ruby 語(yǔ)法。我們看幾個(gè) haml 常用的寫(xiě)法:
.title= "I am Title"
#title= "I am Title"
它會(huì)輸出為:
<div class="title">I am Title</div>
<div id="title">I am Title</div>
下面是顯示 ul 列表,注意,haml 的縮進(jìn)是2個(gè)空格:
%ul
%li Salt
%li Pepper
這是循環(huán)的例子:
- (42...47).each do |i|
%p= i
%p See, I can count!
我們?nèi)绻朐陧?xiàng)目里使用 haml 文件,只需要?jiǎng)?chuàng)建一個(gè) xxx.html.haml 文件即可,這是一個(gè)完整的 haml 例子:
!!!
%html{html_attrs}
%head
%title Hampton Catlin Is Totally Awesome
%meta{"http-equiv" => "Content-Type", :content => "text/html; charset=utf-8"}
%body
%h1
This is very much like the standard template,
except that it has some ActionView-specific stuff.
It's only used for benchmarking.
.crazy_partials= render :partial => 'templates/av_partial_1'
/ You're In my house now!
.header
Yes, ladies and gentileman. He is just that egotistical.
Fantastic! This should be multi-line output
The question is if this would translate! Ahah!
= 1 + 9 + 8 + 2 #numbers should work and this should be ignored
#body= " Quotes should be loved! Just like people!"
- 120.times do |number|
- number
Wow.|
%p
= "Holy cow " + |
"multiline " + |
"tags! " + |
"A pipe (|) even!" |
= [1, 2, 3].collect { |n| "PipesIgnored|" }
= [1, 2, 3].collect { |n| |
n.to_s |
}.join("|") |
%div.silent
- foo = String.new
- foo << "this"
- foo << " shouldn't"
- foo << " evaluate"
= foo + " but now it should!"
-# Woah crap a comment!
-# That was a line that shouldn't close everything.
%ul.really.cool
- ('a'..'f').each do |a|
%li= a
#combo.of_divs_with_underscore= @should_eval = "with this text"
= [ 104, 101, 108, 108, 111 ].map do |byte|
- byte.chr
.footer
%strong.shout= "This is a really long ruby quote. It should be loved and wrapped because its more than 50 characters. This value may change in the future and this test may look stupid. \nSo, I'm just making it *really* long. God, I hope this works"
這個(gè)文件來(lái)自 這里,你可以在這里找到它的源碼。
在 這里 還有一份文檔。
如果打算把現(xiàn)有的 erb 轉(zhuǎn)成 haml,可以使用 haml 提供的一個(gè)命令行工具 html2haml,我們?cè)?Gemfile 里安裝臺(tái):
gem 'html2haml'
在命令行里,直接使用它:
% html2haml -e index.erb index.haml
-e 參數(shù),可以把 erb 模板轉(zhuǎn)成 haml。
這里有一個(gè) 網(wǎng)站,是在線把 html/erb 轉(zhuǎn)成 haml。如果需要把 haml 轉(zhuǎn)回 erb 模板,可是試試 這個(gè)網(wǎng)站。
為了方便的使用 haml,尤其在使用 scaffold 或者 generate 創(chuàng)建文件的時(shí)候,自動(dòng)創(chuàng)建 haml,而不是 erb,可以安裝 haml-rails:
gem "haml-rails"
它為我們提供了一個(gè)快速轉(zhuǎn)換的工具:
rake haml:erb2haml
它可以把所有 views 文件夾下的 erb 模板,轉(zhuǎn)成 haml。
和 haml 類似,slim 更加的簡(jiǎn)潔,從它的官網(wǎng)首頁(yè)可以看到它的代碼風(fēng)格,而且比 haml 又少了一些分隔符。
doctype html
html
head
title Slim Examples
meta name="keywords" content="template language"
meta name="author" content=author
javascript:
alert('Slim supports embedded javascript!')
body
h1 Markup examples
#content
p This example shows you how a basic Slim file looks like.
== yield
- unless items.empty?
table
- for item in items do
tr
td.name = item.name
td.price = item.price
- else
p
| No items found. Please add some inventory.
Thank you!
div id="footer"
= render 'footer'
| Copyright ? #{year} #{author}
這是官網(wǎng)首頁(yè)給出的代碼示例,這里有它詳盡的使用手冊(cè)。
slim 為我們提供了兩個(gè)工具:html2slim 可以把 html/erb 轉(zhuǎn)換成 slim,haml2slim 把 haml 轉(zhuǎn)成 slim。
和 haml-rails 一樣,slim-rails 可以默認(rèn)生成 slim 模板。
在這里,有一個(gè) 在線工具,把 html 轉(zhuǎn)換成 slim。
上面兩個(gè)模板引擎(template engine)是針對(duì)開(kāi)發(fā)者的,因?yàn)槲覀兙帉?xiě)的代碼是不會(huì)交付給使用者的,但是,如果我們需要把頁(yè)面開(kāi)放給使用者隨意編輯,以上提到的 erb,haml,slim 是絕對(duì)不可以的,因?yàn)槭褂谜呖梢栽陧?yè)面里這么寫(xiě):
<%= User.destroy_all %>
那么,如何給使用者一個(gè)安全的模板來(lái)自由編輯呢? liquid 是一個(gè)很好的方案。liquid 是著名的電商網(wǎng)站 Shopify 設(shè)計(jì)并開(kāi)源的安全模板引擎。
liquid 不允許執(zhí)行危險(xiǎn)的代碼,所以可以隨意交給使用者編輯并且直接渲染成頁(yè)面,它還可以保存到數(shù)據(jù)里,這樣可以實(shí)現(xiàn)在線編輯模板,它將邏輯代碼和表現(xiàn)代碼分開(kāi),如果你熟悉 php 的 smarty 模板,那么你會(huì)發(fā)現(xiàn) liquid 就是 Ruby 版的 smarty。
我們看一個(gè)例子:
<ul id="products">
{% for product in products %}
<li>
<h2>{{ product.name }}</h2>
Only {{ product.price | price }}
{{ product.description | prettyprint | paragraph }}
</li>
{% endfor %}
</ul>
這是我們的 liquid 模板,我們把 Product 的 Model 也改寫(xiě)一下,讓 liquid 可以讀取它的屬性:
class Product < ActiveRecord::Base
def to_liquid
{
"name" => name, [1] 這里要用 string 的寫(xiě)法,不要使用 symbol。
"price" => price
}
end
end
我們來(lái)渲染(Render)這個(gè)模板:
require "liquid"
template = Liquid::Template.parse(template) # template 就是上面的代碼,可以直接從數(shù)據(jù)庫(kù)讀取出來(lái)。
template.render('products' => Product.all) # 傳入 products 變量,這是由我們控制傳入的,用戶可以在模板中隨意調(diào)用。
liquid 的源碼在 https://github.com/Shopify/liquid,在 https://github.com/Shopify/liquid/wiki/Liquid-for-Designers,有非常詳盡的使用方法。
和 haml-rails,slim-rails 一樣,liquid 也有自己的:
gem 'liquid-rails'
它可以方便的使用 Rails 中的 Helper 和 Tag,實(shí)現(xiàn) Drop Class,并且編寫(xiě) Rspec 測(cè)試。
Rails 使用 Tilt 這個(gè) Gem 來(lái)處理各種模板引擎,Tilt 是一個(gè)接口,支持幾十個(gè)模板引擎,我們只是提到了其中較常用的三個(gè)。
使用 tilt 方便我們?cè)?Rails 集成各種模板引擎,不過(guò)實(shí)際開(kāi)發(fā)的時(shí)候,我們要注意他們的效率問(wèn)題。這里有一份測(cè)試數(shù)據(jù):
# Linux + Ruby 1.9.2, 1000 iterations
user system total real
(1) erb 0.680000 0.000000 0.680000 ( 0.810375)
(1) erubis 0.510000 0.000000 0.510000 ( 0.547548)
(1) fast erubis 0.530000 0.000000 0.530000 ( 0.583134)
(1) slim 4.330000 0.020000 4.350000 ( 4.495633)
(1) haml 4.680000 0.020000 4.700000 ( 4.747019)
(1) haml ugly 4.530000 0.020000 4.550000 ( 4.592425)
(2) erb 0.240000 0.000000 0.240000 ( 0.235896)
(2) erubis 0.180000 0.000000 0.180000 ( 0.185349)
(2) fast erubis 0.150000 0.000000 0.150000 ( 0.154970)
(2) slim 0.050000 0.000000 0.050000 ( 0.046685)
(2) haml 0.490000 0.000000 0.490000 ( 0.497864)
(2) haml ugly 0.420000 0.000000 0.420000 ( 0.428596)
(3) erb 0.030000 0.000000 0.030000 ( 0.033979)
(3) erubis 0.030000 0.000000 0.030000 ( 0.030705)
(3) fast erubis 0.040000 0.000000 0.040000 ( 0.035229)
(3) slim 0.040000 0.000000 0.040000 ( 0.036249)
(3) haml 0.160000 0.000000 0.160000 ( 0.165024)
(3) haml ugly 0.150000 0.000000 0.150000 ( 0.146130)
(4) erb 0.060000 0.000000 0.060000 ( 0.059847)
(4) erubis 0.040000 0.000000 0.040000 ( 0.040770)
(4) slim 0.040000 0.000000 0.040000 ( 0.047389)
(4) haml 0.190000 0.000000 0.190000 ( 0.188837)
(4) haml ugly 0.170000 0.000000 0.170000 ( 0.175378)
1. Uncached benchmark. Template is parsed every time.
Activate this benchmark with slow=1.
2. Cached benchmark. Template is parsed before the benchmark.
The ruby code generated by the template engine might be evaluated every time.
This benchmark uses the standard API of the template engine.
3. Compiled benchmark. Template is parsed before the benchmark and
generated ruby code is compiled into a method.
This is the fastest evaluation strategy because it benchmarks
pure execution speed of the generated ruby code.
4. Compiled Tilt benchmark. Template is compiled with Tilt, which gives a more
accurate result of the performance in production mode in frameworks like
Sinatra, Ramaze and Camping. (Rails still uses its own template
compilation.)
該數(shù)據(jù)來(lái)自:https://ruby-china.org/topics/634
選擇哪個(gè)模板引擎,還是直接使用 erb,需要視情況而定了。
Devise 除了提供用戶注冊(cè)和登錄功能,還可以通過(guò)郵件激活用戶。這里,我們可以自己定義郵件模板中的內(nèi)容。
我們修改一下 User 中關(guān)于 Devise 的配置:
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable
我們?cè)黾恿艘粋€(gè)新的選項(xiàng)::confirmable。
我們配置下郵件發(fā)送的信息,這里我們只在開(kāi)發(fā)環(huán)境(development)配置,我們打開(kāi) config/environments/development.rb:
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: 'smtp.163.com',
port: 25,
domain: '...',
user_name: '...@163.com',
password: '...',
authentication: :plain,
enable_starttls_auto: true
}
同時(shí),我們還需要修改 user 創(chuàng)建時(shí)的 migration 文件,打開(kāi) db/migrate/xxxx_devise_create_users.rb,我們?nèi)∠⑨屵@個(gè)部分:
## Confirmable
t.string :confirmation_token
t.datetime :confirmed_at
t.datetime :confirmation_sent_at
t.string :unconfirmed_email # Only if using reconfirmable
注意,xxxx 是日期時(shí)間戳,表示這個(gè)文件創(chuàng)建的日期。devise 已經(jīng)為我們添加了 confirmable 需要的字段,我們不必自己添加。這里有一個(gè)問(wèn)題,我們已經(jīng)運(yùn)行過(guò)數(shù)據(jù)庫(kù)文件了,這里是修改舊文件,我們不能直接更新文件,這里我們可以刪掉舊的數(shù)據(jù)庫(kù),其實(shí)在開(kāi)發(fā)環(huán)境,我們可以經(jīng)常重建數(shù)據(jù)庫(kù):
rake db:drop
rake db:create
rake db:migrate
rake db:seed
還記得 db/seeds.rb 這個(gè)文件吧,我們可以把一些默認(rèn)數(shù)據(jù)寫(xiě)到 seed 里,或者一些測(cè)試數(shù)據(jù),比如我們添加50個(gè)商品信息,來(lái)測(cè)試分頁(yè)等效果是否正確,或者初始化幾十個(gè)商品類別等。這樣,重建數(shù)據(jù)庫(kù)時(shí),不必?fù)?dān)心默認(rèn)數(shù)據(jù)的丟失。
我們?cè)倬庉嬒?devise 的配置文件,它在 config/initializers/devise.rb:
config.mailer_sender = '...@163.com'
...
config.mailer = 'Devise::Mailer'
我們重新啟動(dòng) Rails 服務(wù),注冊(cè)一個(gè)賬號(hào),這時(shí)我們觀察終端,可以看到郵件發(fā)送的信息:
Sent mail to hi@liwei.me (2468.6ms)
Date: Mon, 02 Mar 2015 14:04:57 +0800
From: master@...
Reply-To: master@...
To: hi@liwei.me
Message-ID: <54f3fd89f0f84_62213ffdf44a34e08208d@macbook.local.mail>
Subject: =?UTF-8?Q?=E6=9D=A5=E8=87=AAezcms=E7=9A=84=E6=B3=A8=E5=86=8C=E7=A1=AE=E8=AE=A4=E9=82=AE=E4=BB=B6?=
Mime-Version: 1.0
Content-Type: text/html;
charset=UTF-8
Content-Transfer-Encoding: 7bit
<p>Welcome hi@liwei.me!</p>
<p>You can confirm your account email through the link below:</p>
<p><a href="http://localhost:3000/users/confirmation?confirmation_token=FNMGvy_VnNfyhHKz_LKY">Confirm my account</a></p>
我們頁(yè)面上,也會(huì)得到這樣的提示:
http://wiki.jikexueyuan.com/project/rails-practice/images/chapter_3/11.png" alt="" />
為了讓我們的郵件看起來(lái)更友好,我們編輯 app/views/users/mailer/confirmation_instructions.html.erb:
<p>你好 <%= @email %>!</p>
<p>請(qǐng)點(diǎn)擊下面的確認(rèn)鏈接,驗(yàn)證您的郵箱:</p>
<p><%= link_to "驗(yàn)證我的郵箱", confirmation_url(@resource, confirmation_token: @token) %></p>
你可以再試試看。不過(guò)這種配置可能會(huì)被當(dāng)做垃圾郵件拒收,或者直接被放到垃圾郵件中。在后面的章節(jié)里,我們會(huì)介紹其他的方式發(fā)送郵件。
如果你不能通過(guò)郵件激活這個(gè)用戶,比如那些在 seed 中添加的用戶,沒(méi)關(guān)系,rails c 進(jìn)入控制臺(tái):
u = User.last [1]
u.confirm! [2]
confirm! 方法激活用戶更多郵件配置,可以查看 http://guides.rubyonrails.org/configuring.html#configuring-action-mailer