Hatena::ブログ(Diary)

ursmの日記

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>&lt;hello&gt;</p>
%p #{'<hello>'} <world>  #=> <p>&lt;hello&gt; <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 にネタを譲ってもらいました。ありがとうございます。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証