2009-12-23
Haml レシピブック 11の技
(Ruby Advent Calendar jp: 2009 の23日目の記事です。昨日は flexfranx さんで、明日は tomoya さんです)
Haml のちょっとした Tips を揃えてみました。
レシピ1: 属性をすっきり書く
Haml 2.2 で導入された HTML-Style Attribute Syntax を使うと気持ち読みやすくなります。
/ 従来の記法
%style{:type => 'text/css', :href => '/application.css', :media => 'screen'}
/ HTML-Style Attribute Syntax
%style(type='text/css' href='/application.css' media='screen')
単純な式評価はできますが、メソッド呼び出しや演算はできません。
/ OK
%p(class=@type) ...
/ NG
%p(class=@item.type) ...
/ これは OK
%p(class='#{@item.type}') ...
従来の記法と混ぜたりもできます。
%script(type='application/javascript'){:src => '/application.js'}
「静的な要素は HTML-Style Attribute Syntax、それ以外は従来の記法」のように適宜使い分けるのがいいでしょう。
なお、Haml を Ruby 1.9 で動作させる場合は Hash の新しい記法が使えます。
%style{type: 'text/css', href: '/application.css', media: 'screen'}
レシピ2: 複雑な属性を生成する
属性の部分には Hash を返す式なら何でも書けます。条件に応じて属性を生成するような場合、適切な Hash を返すメソッドを用意して呼び出せばいいのです。
def item_attrs(item) if item.recommended? {:class => 'item recommended'} else {:class => 'item'} end end
%p{item_attrs(@item)} ...
式は複数指定できます。この場合は Hash が前から後に向かって merge されます。
%p{{:a => 1, :b => 2}, {:b => 3, :c => 4}, :c => 5} ...
/=> <p a='1' b='3' c='5'>...</p>
レシピ3: インライン要素をすっきり書く
%p Powered by %br %strong Ruby and %strong Haml
要素ごとに改行するのが嫌な場合はタグをベタ書きできます。
%p Powered by<br /> <strong>Ruby</strong> and <strong>Haml</strong>
見やすさと記述量の兼ね合いを考えて使い分けましょう。
レシピ4: 式展開を使う
%p 1 + 2 = = 1 + 2
地の文に式の結果を埋め込みたい場合は、式展開を使うとすっきり書けます。
%p 1 + 2 = #{1 + 2}
Rails の link_to メソッドを文の途中で使いたいときなどにも便利です。
レシピ5: デフォルトで HTML エスケープする
以下のように設定すると = や式展開がデフォルトで HTML エスケープされるようになります。
- Rails
- Haml::Template::options[:escape_html] = true
- Merb
- Merb::Plugin.config[:haml][:escape_html] = true
- Sinatra
- set :haml, :escape_html => true
エスケープしてほしくない場合は != と ! を使います。
%p= '<hello>' #=> <p><hello></p>
%p #{'<hello>'} <world> #=> <p><hello> <world></p>
%p!= '<hello>' #=> <p><hello></p>
%p! #{'<hello>'} <world> #=> <p><hello> <world></p>
/ helper や partial はエスケープされると困る
%h2!= link_to h(@user.name), user_path(@user)
!= render :partial => @user
XSS 脆弱性に繋がるうっかりミスを格段に減らせます。
レシピ6: Haml で HTML5 を使う
Haml は今話題の HTML5 をサポートしています。
- Rails
- Haml::Template::options[:format] = :html5
- Merb
- Merb::Plugin.config[:haml][:format] = :html5
- Sinatra
- set :haml, :format => :html5
と言っても DOCTYPE 宣言と空要素終端の / の扱いが変わるだけですが。
!!! /=> <!DOCTYPE html> %img(:src='cat.png') /=> <img src='cat.png'>
レシピ7: HTML 以外にも Haml を使う
Haml は HTML/XHTML だけでなく任意の XML を出力できます。名前空間も問題なく扱えます。
例えば Haml を使って FOAF を書くとこんな感じです。
!!! XML
%rdf:RDF(xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
xmlns:foaf='http://xmlns.com/foaf/0.1/')
%foaf:Person
%foaf:mbox(rdf:resource='mailto:ursm@ursm.jp')/
%foaf:nick ursm
↓
<?xml version='1.0' encoding='utf-8' ?> <rdf:RDF xmlns:foaf='http://xmlns.com/foaf/0.1/' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'> <foaf:Person> <foaf:mbox rdf:resource='mailto:ursm@ursm.jp' /> <foaf:nick>ursm</foaf:nick> </foaf:Person> </rdf:RDF>
要素名が長ければ長いほど Haml の「要素を閉じなくていい」特性が効いてきます。
レシピ8: Rails の helper で Haml を使う
文字列処理でタグを組み立てていると悲しい気分になってくるので Haml を使いましょう。
module UsersHelper def list_users(users) render :inline => <<-HAML, :type => :haml, :locals => {:users => users} %ul - users.each do |u| %li!= link_to h(u.name), u HAML end end
好みの問題ですが、数行程度の partial なら helper になっている方が (ファイルが分散しないので) 見通しがいいと思います。
レシピ9: Haml::Helpers を使う
Haml::Helpers には、Haml の評価コンテキストから使えるユーティリティメソッドが定義されています。いくつか代表的なものをご紹介します。
capture_haml
ブロック以下の Haml を評価した結果を文字列として返すメソッドです。ページ中に同じものを複数回出力したいときに便利です。
table の上下に同じヘッダを出す例です。
%table
!= header = capture_haml do
%tr
%th Name
%th Email
- @users.each do |u|
%tr
%td= u.name
%td= u.email
!= header
list_of
リストの各要素に対してブロックを評価し、結果を <li>...</li> で囲って出力します。
%ul
!= list_of @users do |u|
= u.name
↓
<ul> <li>user_0</li> <li>user_1</li> <li>user_2</li> </ul>
普通に each で書いた方が早いような気がしますが、複数行の出力を行う場合にインデントを1段浅くできるメリットがあります。
/ each
%ul
- @users.each do |u|
%li
= u.name
= u.email
/ list_of
%ul
!= list_of @users do |u|
= u.name
= u.email
このメソッドを使っている人はかなりの Haml 使いと見て間違いないでしょう。
レシピ10: 長いメソッド引数をすっきり書く
Haml の式評価は途中で改行できないという制限があります。メソッドの引数がとても長い場合など、一行が長くなってしまってよくありません。
!= link_to 'Users', :controller => 'users', :action => 'index', :page => params[:page], :sort => 'name'
こんなときは Ruby filter で一旦変数に入れてやると読みやすくなります。
:ruby
opts = {
:controller => 'users',
:action => 'index',
:page => params[:page],
:sort => 'name'
}
!= link_to 'Users', opts
レシピ11: ERB から段階的に移行する
巨大な ERB ファイルを一気に Haml へ書き換えるのが難しい場合、ファイルの中身を徐々に Haml に置き換えていくことができます。
ファイルの拡張子を .haml にして先頭に :erb と書き、ERB の部分をインデントします。これで妥当な Haml ドキュメントになります。
:erb
<!DOCTYPE html>
<html>
<head>
<title>...</title>
</head>
<body>
...
</body>
</html>
先頭から Haml に書き換えて、終わったところまで :erb の行を移動させます。これを繰り返します。
!!!
%html
:erb
<head>
<title>...</title>
</head>
<body>
...
</body>
↓
!!!
%html
%head
%title ...
:erb
<body>
...
</body>
↓
!!!
%html
%head
%title ...
%body
...
謝辞
Haml/Sass で履歴書を書いたことで有名な id:Sixeight にネタを譲ってもらいました。ありがとうございます。
- 48 http://reader.livedoor.com/reader/
- 32 http://atnd.org/events/2351
- 22 http://pipes.yahoo.com/pipes/pipe.info?_id=faa858a20082ef6d25ad27557e37e011
- 18 http://twitter.com/
- 12 http://www.google.com/reader/view/
- 10 http://blog.longkey1.net/archives/696
- 9 http://b.hatena.ne.jp/hotentry/it
- 9 http://route477.net/d/?date=20091227
- 9 http://route477.net/e/?date=20091228
- 9 http://www.google.co.jp/reader/view/

