読者です 読者をやめる 読者になる 読者になる

Rails Webook

自社のECを開発している会社で働いています。Rails情報やサービスを成長させる方法を書いていきます

RailsのHTMLテンプレートエンジン Haml入門

Rails View Rails gem

はじめに

RailsではView(HTML出力)のためには標準ではERB(.erb)を使用しています。
しかし、erbは汎用的なテンプレートエンジンであり、HTML以外のファイルにRubyコードを
埋め込めることができるます。
そのため、HTML作成という点で見ると、いくらか冗長な箇所があります。

そのため、より生産的にHTMLを作成するために、Railsでは

  • Haml (拡張子 xxx.html.haml)
  • Slim (拡張子 xxx.html.slim)

という2つのHTML用のテンプレートエンジンがよく使われます。


そして、今回はタイトルからも分かるようにHamlについて説明します。

Slimについては、こちらから参照ください。

Hamlは「マークアプは美しくあるべき」という原則に基づいて開発されました。
Hamlを使うことにより、Viewファイルを「綺麗に」「読みやすく」「生産的に」作成することができます。

動作確認

  • Rails 4.1
  • haml 4.0.5
  • haml-rails 0.5.3

目次

  1. Hamlの基礎
  2. RailsにHamlの導入
  3. erbをhamlに書き換えていく
  4. おまけ:Hamlのリファレンスシート
  5. おまけ:HTMLやerbをhamlに変換するツール: erb2haml


1. Hamlの基礎

Hamlはシンプルな記法で難しくはないので、実際にerbとhamlのソースコードを比べて、1つ1つ説明していきます。
Hamlの拡張子は、.hamlにする必要があります。

# app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
  <title>SlimTest</title>
  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
  <%= csrf_meta_tags %>
</head>
<body>

<header id="header">
  <h1 class="title logo">Slim Test</h1>
</header>

<%= yield %>

</body>
</html>
# app/views/layouts/application.html.haml
!!! 5
%html
  %head
    %title HamlTest
    = stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true
    = javascript_include_tag 'application', 'data-turbolinks-track' => true
    = csrf_meta_tags

  %body

  = yield

Hamlの簡単な覚え方

上記のerbとslimを見比べながら、下記のルールを見て下さい。

  1. <、>、<%、%>をタグから削除し。&をタグの直前につける。また、閉じタグは全て削除する
  2. each、ifなどのロジック部分の先頭には、-を記載する(endは必要ない)
  3. <%= ... >=にする
  4. class属性やid属性は、%p.fields%p#contentsなどにする。タグがdivのときはdivを省略し#contentと記載できる
  5. class属性やid属性以外のhrefやimgなどの属性を追加したい場合は、ハッシュで記載する(例:%a{:href => "#"} リンクテキスト
  6. コメントは、/ このコメントはHTMLに変換後に表示されないで記載する


2. RailsにHamlの導入

Railsプロジェクトの作成

まず、いつも通りRailsプロジェクトを作成します。

$ rails new haml_test

次に、商品(Product)のソース一式をScaffoldを使って作成します。

$ cd haml_test
$ rails g scaffold Product name:string description:text price:integer discontinued:boolean

DBマイグレートをします。

$ rake db:migrate

これで、商品(Product)の一覧表示、作成、更新、削除ができました。
$ rails serverでローカルのサーバを起動していくつかデータを入力してみました。

f:id:nipe880324:20140929214513p:plain:w480

HamlをRailsに導入

では、実際にRailsにHamlを導入していきましょう。
Gemfileに下記を追加します。

gem 'haml-rails'

その後、Hamlをインストールします。

$ bundle install

これで、RailsにHamlが導入されたので、以降はxxx.html.hamlという拡張子でViewファイルを作成することでHamlとしてRailsが読み込んでくれます。

ERBHamlで同名のファイルがある場合は、ERBが優先的に読み込まれるので、ERBファイルは削除して下さい。例えば、index.html.erbindex.html.hamlがある場合、index.html.erbが読み込まれてしまうため、削除して下さい。

haml-rails gem をインストール後に、rails generaterでscaffoldやcontrollerを作成すると、Viewファイルは全て.erbの代わりに.hamlで作成されます。



3. erbをhamlに書き換えていく

では、Scaffoldで作成した.erbを1画面ずつ.hamlに変更していきます。
変更後のHamlファイルを見る前に、自分で上記の「1. Hamlの基礎」を確認しながら手を動かしながらHamlファイルを作成することにより、Hamlの書き方に慣れるのでおすすめです。

一覧画面

まずは、一覧画面を変更します。
index.html.erbは次のようになっています。

# app/views/products/index.html.erb
<h1>Listing products</h1>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Description</th>
      <th>Price</th>
      <th>Discontinued</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @products.each do |product| %>
      <tr>
        <td><%= product.name %></td>
        <td><%= product.description %></td>
        <td><%= product.price %></td>
        <td><%= product.discontinued %></td>
        <td><%= link_to 'Show', product %></td>
        <td><%= link_to 'Edit', edit_product_path(product) %></td>
        <td><%= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Product', new_product_path %>


index.html.slimを新しく作成し、これを1つ1つ直していきましょう。

下記が変換後の一覧画面です。

Hamlに変換するポイント
  • h1, tableなどのタグの<と>を削除し、%をタグに付け、閉じタグは削除する
  • id属性、class属性、その他の属性を追加する場合は、{}でハッシュ形式でタグの直後に記載する
  • ifやeachなどロジック部分の行には、先頭に-を記載する
  • 上記をインデントに気をつけながら記載する

では、Hamlファイルを見てみましょう。

# app/views/products/index.html.haml
%h1 Listing products

%table
  %thead
    %tr
      %th Name
      %th Description
      %th Price
      %th Discontinued
      %th{:colspan => "3"}

  %tbody
    - @products.each do |product|
      %tr
        %td= product.name
        %td= product.description
        %td= product.price
        %td= product.discontinued
        %td= link_to 'Show', product
        %td= link_to 'Edit', edit_product_path(product)
        %td= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' }

%br

= link_to 'New Product', new_product_path

.erbファイルが存在するとそちらが優先的に表示されてしまうので、削除します。
サーバーを再起動しなおして、一覧画面を確認してみましょう。
表示は変わっていませんね :)

f:id:nipe880324:20140929214513p:plain:w480

新規/編集画面

次は、新規、編集画面です。

# app/views/products/new.html.erb
<h1>New product</h1>

<%= render 'form' %>

<%= link_to 'Back', products_path %>
# app/views/products/edit.html.erb
<h1>Editing product</h1>

<%= render 'form' %>

<%= link_to 'Show', @product %> |
<%= link_to 'Back', products_path %>
# app/views/products/_form.html.erb
<%= form_for(@product) do |f| %>
  <% if @product.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@product.errors.count, "error") %> prohibited this product from being saved:</h2>

      <ul>
      <% @product.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :description %><br>
    <%= f.text_area :description %>
  </div>
  <div class="field">
    <%= f.label :price %><br>
    <%= f.number_field :price %>
  </div>
  <div class="field">
    <%= f.label :discontinued %><br>
    <%= f.check_box :discontinued %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>


ではHamlに変換します。
ポイントは、

  • プレインテキストは、\をプレインテキストの前後に記載すr
  • 文字列は"{...}"で記載する

です。

# app/views/products/new.html.haml
%h1 New product

= render 'form'

= link_to 'Back', products_path
# app/views/products/edit.html.haml
%h1 Editing product

= render 'form'

= link_to 'Show', @product
\|
= link_to 'Back', products_path
# app/views/products/_form.html.haml
= form_for(@product) do |f|
  - if @product.errors.any?
    #error_explanation
      %h2= "#{pluralize(@product.errors.count, "error")} prohibited this product from being saved:"

      %ul
        - @product.errors.full_messages.each do |message|
          %li= message

  .field
    = f.label :name
    %br
    = f.text_field :name
  .field
    = f.label :description
    %br
    = f.text_area :description
  .field
    = f.label :price
    %br
    = f.number_field :price
  .field
    = f.label :discontinued
    %br
    = f.check_box :discontinued

  .actions
    = f.submit

では、新規画面を開いてみましょう。
上手く行っていますね。順調です。

f:id:nipe880324:20140929220337p:plain:w480

詳細画面

では、この調子で最後に詳細画面を変換していきましょう。
変換にも慣れてきたと思います。

# app/views/products/show.html.erb
<p id="notice"><%= notice %></p>

<p>
  <strong>Name:</strong>
  <%= @product.name %>
</p>

<p>
  <strong>Description:</strong>
  <%= @product.description %>
</p>

<p>
  <strong>Price:</strong>
  <%= @product.price %>
</p>

<p>
  <strong>Discontinued:</strong>
  <%= @product.discontinued %>
</p>

<%= link_to 'Edit', edit_product_path(@product) %> |
<%= link_to 'Back', products_path %>


Hamlへの変換のポイントは

  • id属性は#、class属性は.で記載し、タグの直後に記載する(例:%p.error
  • #.の前にタグ名を記載しない場合(例: .disable)は、div要素として変換されます。
  • 複数のクラスを記載したい場合は、.でつなげる(例:input.btn.btn-default.btn-sm
# app/views/products/show.html.haml
%p#notice= notice

%p
  %strong Name:
  = @product.name

%p
  %strong Description:
  = @product.description

%p
  %strong Price:
  = @product.price

%p
  %strong Discontinued:
  = @product.discontinued

= link_to 'Edit', edit_product_path(@product)
\|
= link_to 'Back', products_path

では、詳細画面を見てみましょう。
これで、一通りの画面をSlimにすることができました :)

f:id:nipe880324:20140929220852p:plain:w480


4. おまけ:Hamlのリファレンスシート

Hamlの変換ルールで困った場合は次のURLを参照して下さい。
http://haml.info/docs/yardoc/file.REFERENCE.html



5. おまけ:ERBをHamlに変換するツール: erb2haml

また、既にerbで多くのViewファイルを作成してしまっている場合は、erbをhamlに変換するerb2hamlというツールがあります。
erb2hamlの使い方

ちなみに、ツールは万能でなく、上手くHamlに変換できてない箇所が存在する可能性もあるので、全ての画面に対してレイアウト崩れのチェックはしてください。

参考文献

以上です。
よく分からない箇所や誤りなどがありましたら、コメントの記入をお願いします。