娯楽開発

娯楽開発

開発は娯楽。遊び心で開発をすすめるブログ。

【rails】viewのerbファイルをslim化パーシャル化

この記事を読んでいるということはみなさんrailsで何かしらのサービスを作っていることでしょう。どうでしょうか、進捗は順調でしょうか。

そしてどうでしょうか。そろそろerbファイル、読む気が失せてきたのではないでしょうか。僕は読みたくなくなりました。少なくとももう<>を見たくない。

というわけで今回はviewの憎きerbファイルを撲滅し、シンプルで読みやすいslimファイルへ移行する方法について書こうと思います。

slim化が思いのほか簡単だったのでついでにパーシャルもやってみました。)

erbは目が痛い

htmlに準じた記法であるerb。コードが少ないうちは特に気にならないですね。

<!DOCTYPE html>
<html>
  <head>
    <title>About | Ruby on Rails Tutorial Sample App</title>
  </head>
  <body>
    <h1>TimeLine</h1>
    <% @posts.each do |post| %>
      <ul>
        <li>投稿ID:<%= post.id %></li>
        <li>投稿者:</li>
        <li>投稿:<%= post.text_content %></li>
      </ul>
    <% end %>
  </body>
</html>

この頃はまだそんなに気になってなかったんですが、これが最近だとこうなりました。

<!DOCTYPE Html>
<html>
  <head>
    <title>About | Ruby on Rails Tutorial Sample App</title>
  </head>
  <body>
    <% if !session[:login_id] %>
      <p>ログインしてください。</p>
      <p><%= link_to "ログイン", login_path %></p>
    <% else %>
      <h1>TimeLine</h1>

      <p><%= link_to "投稿する", new_post_path %></p>

      <ul>
        <% @posts.each do |post| %>
          <%  if post.sharing.present? %>
            <%= User.find(post.user_id).account_id %>さんがシェア
            <% share_post_id = post.id %>
            <% post = post.sharing %>
          <% end %>

          <li>
            <%= link_to User.find(post.user_id).account_id, user_path(id: post.user_id) %>
            <%= post.created_at.strftime("%Y-%m-%d%H:%M:%S") %>
          </li>

          <p>
          <% if post.replying.present? %>
            to: <%= link_to User.find(post.replying.user_id).account_id, user_path(id: User.find(post.replying.user_id).id) %><br>
          <% end %>
          <%= post.content %><br>
          </p>

          <p>
          <%= link_to "詳細", post_path(id: post.id) %>
          <%= link_to "返信", new_post_path(reply: {replying_id: session[:login_id], main_post_id: post.id}) %>: 
          <%= post.replied.length %><br>
          <% if post.shared.map(&:user_id).include?(session[:login_id]) %>
            <%= link_to "シェア", destroy_share_path(post: {id: post.id, share_post_id: share_post_id}) %>: <%= post.shared.length %><br>
          <% else %>
            <%= link_to "シェア", share_path(post: {id: post.id}) %>: <%= post.shared.length %><br>
          <% end %>
          <% if post.likes.find_by(user_id: session[:login_id]).present? %>
            <%= link_to "いいね", like_path(id: post.likes.find_by(user_id: session[:login_id]).id, like: {post_id: post.id}), method: :delete %>: <%= post.likes.length %>
          <% else %>
            <%= link_to "いいね", likes_path(like: {post_id: post.id}), method: :post %>: <%= post.likes.length %><br>
          <% end %>
          </p>

        <% end %>
      </ul>
    <% end %>
  </body>
</html>

コードの拙さは一旦置いといて、流石にちょっと読みづらい。なんというか、全体的にトゲトゲしてて目が痛い。

というわけで早速slimの方を導入していきたいと思います。

slim化する

やることは以下の2ステップです。

  1. Gemfileに記述を追加する
  2. サーバーを再起動する

とてもシンプルですね。

1. Gemfileに記述を追加する

Gemfileに以下の2行を追加します。

gem 'slim-rails'
gem 'html2silm'

僕はここでハイフン(-)をアンダースコア(_)で書いていて10分ほどハマったのでしっかり確認しましょう。

2. サーバーを再起動する

すでにサーバーを立ち上げている方はCtrl + cで一度サーバーを止めましょう。

そしてサーバーを再起動します。

rails s

これで全てのerbファイルがslimファイルに変換されているはずです。

確認

早速さきほどの目が痛かったerbファイルを確認してみましょう。

doctype html
html
  head
    title
      | About | Ruby on Rails Tutorial Sample App
  body
    - if !session[:login_id]
      p
        | ログインしてください。
      p
        = link_to "ログイン", login_path
    - else
      h1
        | TimeLine
      p
        = link_to "投稿する", new_post_path
      ul
        - @posts.each do |post|
          - if post.sharing.present?
            = User.find(post.user_id).account_id
            | さんがシェア
            - share_post_id = post.id
            - post = post.sharing
          li
            = link_to User.find(post.user_id).account_id, user_path(id: post.user_id)
            = post.created_at.strftime("%Y-%m-%d %H:%M:%S")
          p
            - if post.replying.present?
              |  to:
              = link_to User.find(post.replying.user_id).account_id, user_path(id: User.find(post.replying.user_id).id)
              br
            = post.content
            br
          p
            = link_to "詳細", post_path(id: post.id)
            br
            = link_to "返信", new_post_path(reply: {replying_id: session[:login_id], main_post_id: post.id})
            | :
            = post.replied.length
            br
            - if post.shared.map(&:user_id).include?(session[:login_id])
              = link_to "シェア", destroy_share_path(post: {id: post.id, share_post_id: post.shared.find_by(user_id: session[:login_id]).id})
              | :
              = post.shared.length
              br
            - else
              = link_to "シェア", share_path(post: {id: post.id})
              | :
              = post.shared.length
              br
            - if post.likes.find_by(user_id: session[:login_id]).present?
              = link_to "いいね", like_path(id: post.likes.find_by(user_id: session[:login_id]).id, like: {post_id: post.id}), method: :delete
              | :
              = post.likes.length
            - else
              = link_to "いいね", likes_path(like: {post_id: post.id}), method: :post
              | :
              = post.likes.length
              br

はい、ちょっとシンタックスが効かないみたいなので色がつかないんですが、かなりすっきりしたような気がしますね。

これで晴れてerbの目の痛さからも解放されました!

パーシャル化(おまけ)

あまりにも簡単にslim化できてしまったのでこの際ついでにパーシャル化もしてしまおう!という雑な魂胆です。お付き合いください。

パーシャルをざっくり説明すると、viewファイルの一部を別ファイルに切り出し、それをテンプレートとして元のviewファイルからテンプレートファイルを呼び出して表示することです。

百聞は一見に如かず

自分でも何言ってるかよくわからなかったので実際のコードを書いてみます。

元のviewファイルは先ほどslim化したapp/view/posts/index.html.slimを例としてみていきます。

以下が元のapp/view/posts/index.html.slimファイルです。

シンタックスが効いていないですがもうしばらくお付き合いください。

doctype html
html
  head
    title
      | About | Ruby on Rails Tutorial Sample App
  body
    - if !session[:login_id]
      p
        | ログインしてください。
      p
        = link_to "ログイン", login_path
    - else
      h1
        | TimeLine
      p
        = link_to "投稿する", new_post_path
      ul
        - @posts.each do |post|
          - if post.sharing.present?
            = User.find(post.user_id).account_id
            | さんがシェア
            - share_post_id = post.id
            - post = post.sharing
          li
            = link_to User.find(post.user_id).account_id, user_path(id: post.user_id)
            = post.created_at.strftime("%Y-%m-%d %H:%M:%S")
          p
            - if post.replying.present?
              |  to:
              = link_to User.find(post.replying.user_id).account_id, user_path(id: User.find(post.replying.user_id).id)
              br
            = post.content
            br
          p
            = link_to "詳細", post_path(id: post.id)
            br
            = link_to "返信", new_post_path(reply: {replying_id: session[:login_id], main_post_id: post.id})
            | :
            = post.replied.length
            br
            - if post.shared.map(&:user_id).include?(session[:login_id])
              = link_to "シェア", destroy_share_path(post: {id: post.id, share_post_id: post.shared.find_by(user_id: session[:login_id]).id})
              | :
              = post.shared.length
              br
            - else
              = link_to "シェア", share_path(post: {id: post.id})
              | :
              = post.shared.length
              br
            - if post.likes.find_by(user_id: session[:login_id]).present?
              = link_to "いいね", like_path(id: post.likes.find_by(user_id: session[:login_id]).id, like: {post_id: post.id}), method: :delete
              | :
              = post.likes.length
            - else
              = link_to "いいね", likes_path(like: {post_id: post.id}), method: :post
              | :
              = post.likes.length
              br

このファイルのul行以下からは@postsで渡された複数のpostを一覧表示する処理になっています。

いまの状態だとposts.html.slimファイルがごちゃごちゃしてみづらいですし、ul以下はテンプレート化しておけば他の部分でも使い回す機会があるかもしれません。

というわけで、この部分をテンプレート化(パーシャル化)してみましょう。

パーシャル化するには以下のステップを踏みます。

  1. パーシャルとして参照用のファイルを作成する
  2. (元ファイルに)パーシャルを参照する処理を書く

早速見ていきましょう。

1. パーシャルとして参照用のファイルを作成する

元のファイル(app/view/posts/index.html.slim)のul以下の処理を切り出し、app/view/posts/_posts_list.html.slimとして保存します。

以下がapp/view/posts/_posts_list.html.slimです。

ul
  - @posts.each do |post|
    - if post.sharing.present?
      = User.find(post.user_id).account_id
      | さんがシェア
      - share_post_id = post.id
      - post = post.sharing
    li
      = link_to User.find(post.user_id).account_id, user_path(id: post.user_id)
      = post.created_at.strftime("%Y-%m-%d %H:%M:%S")
    p
      - if post.replying.present?
        |  to:
        = link_to User.find(post.replying.user_id).account_id, user_path(id: User.find(post.replying.user_id).id)
        br
      = post.content
      br
    p
      = link_to "詳細", post_path(id: post.id)
      br
      = link_to "返信", new_post_path(reply: {replying_id: session[:login_id], main_post_id: post.id})
      | :
      = post.replied.length
      br
      - if post.shared.map(&:user_id).include?(session[:login_id])
        = link_to "シェア", destroy_share_path(post: {id: post.id, share_post_id: post.shared.find_by(user_id: session[:login_id]).id})
        | :
        = post.shared.length
        br
      - else
        = link_to "シェア", share_path(post: {id: post.id})
        | :
        = post.shared.length
        br
      - if post.likes.find_by(user_id: session[:login_id]).present?
        = link_to "いいね", like_path(id: post.likes.find_by(user_id: session[:login_id]).id, like: {post_id: post.id}), method: :delete
        | :
        = post.likes.length
      - else
        = link_to "いいね", likes_path(like: {post_id: post.id}), method: :post
        | :
        = post.likes.length
        br

パーシャルとなるファイルには、_から始まる名前をつけるのがルールとなっています。

簡単ですが、これでテンプレートとなるパーシャルの準備は完了です。

2. (元ファイルに)パーシャルを参照する処理を書く

続いて元のファイル(app/view/posts/index.html.slim)にパーシャルを参照する処理を書きます。

パーシャルを参照する元のファイルは以下のようになります。

doctype html
html
  head
    title
      | About | Ruby on Rails Tutorial Sample App
  body
    - if !session[:login_id]
      p
        | ログインしてください。
      p
        = link_to "ログイン", login_path
    - else
      h1
        | TimeLine
      p
        = link_to "投稿する", new_post_path
      = render 'posts_list'

とても簡潔になりました。

最後の行に注目してください。

さっきまでulが書いてあった部分に= render 'posts_list'という処理が追加されています。

これがパーシャルを参照する処理になります。

_○○.html.slimというパーシャルを呼び出す際には、= render '○○'のように書きます。_が省略されることに注意してください。

以上でパーシャルの呼び出しは完了です。

結果的には= render 'posts_list'の行から先はapp/view/posts/_posts_list.html.slimの処理が読み込まれるため、元のファイルと全く同じview画面が作成されます。

パーシャルまとめ

パーシャルとしてコードを分けることのメリットには以下のようなものがあると思います。

  • コードが簡潔になることで可読性が上がる
  • 機能単位でパーシャルを作成することで他のページでも簡単に機能を使い回すことができる
  • 複数のページで利用される機能をパーシャルでまとめることで、修正時の修正箇所を抑えることができる

あまりにも細かく分けすぎるのもそれはそれでファイル管理が煩雑になってしまうため考えものですが、基本的に適度に使う分には便利な機能だと思います。

全体まとめ

気がつけばおまけのパーシャルについての方がボリュームが出てしまってますね。まぁそんなこともあります。

slimもパーシャルもストレスなくコードを読むためには便利な機能ですので、ぜひ試してみてください。