✂️

【Rails】脱・ifネスト地獄!ビューをスッキリさせる2つのテクニック

に公開

はじめに

Rails アプリを開発していると、ビューで複数の条件分岐(if/elsif/else)が深くネストし、コードが読みづらくなることがあります。特に Slim のようなインデントベースのテンプレートでは、ネストが深まるほど視認性が低下し、メンテナンス性も悪化します。
本記事では「元の仕様を変えず」「Slim+Rails標準機能だけで」可読性を高める方法を2つ紹介します。


問題のコード例

以下は「注文(Order)」モデルの状態に応じてボタンを切り替える深いネスト例です。
3段階以上の入れ子になっているため、条件が増えると「なにを表示しているのか」が一目でわかりません。

.order-actions
  - if order.pending?
    - if order.expedited?
      - if order.insurance_added?
        = link_to urgent_shipping_order_path(order), class: "btn btn-danger" do
          | 急便を手配
    - elsif order.paid?
      .btn.btn-secondary.disabled
        | 支払い完了
      p.note
        | ※ 支払い後に出荷手続きを行います
  - elsif order.shipped?
    = link_to track_order_path(order), class: "btn btn-info" do
      | 発送状況を確認
  - else
    = link_to order_receipt_path(order), class: "btn btn-success" do
      | 領収書を見る

アプローチ1: Partialに切り出す

ディレクトリ構成

注文のアクション表示をpartial化し、状態ごとにファイルを分割します。

app/views/orders/actions/
  _cancelable.html.slim
  _shippable.html.slim
  _trackable.html.slim
  _completed.html.slim

メインビューからの呼び出し

.order-actions
  - if order.pending?
    = render 'orders/actions/cancelable', order: order
  - elsif order.paid?
    = render 'orders/actions/shippable', order: order
  - elsif order.shipped?
    = render 'orders/actions/trackable', order: order
  - else
    = render 'orders/actions/completed', order: order

partial の例

app/views/orders/actions/_cancelable.html.slim
= link_to cancel_order_path(order), method: :post, class: "btn btn-warning" do
  | 注文をキャンセル
app/views/orders/actions/_shippable.html.slim
.btn.btn-secondary.disabled
  | 出荷準備中
p.note
  | ※ 支払い完了後に出荷手続きを開始します
app/views/orders/actions/_trackable.html.slim
= link_to track_order_path(order), class: "btn btn-info" do
  | 発送状況を確認
app/views/orders/actions/_completed.html.slim
= link_to order_receipt_path(order), class: "btn btn-success" do
  | 領収書を見る

メリット

  • メインビューは render のみでシンプル
  • 各状態ごとの partial に閉じこめるため、編集・把握がしやすい

デメリット

  • partial ファイルの数が増えていく

アプローチ2: Helperメソッドにロジック移譲

ヘルパーの実装例

# app/helpers/orders_helper.rb
module OrdersHelper
  def order_action_state(order)
    if order.pending?
      :cancelable
    elsif order.paid?
      :shippable
    elsif order.shipped?
      :trackable
    else
      :completed
    end
  end
end

ビュー側

.order-actions
  - state = order_action_state(order)

  - case state
    - when :cancelable
      = link_to cancel_order_path(order), method: :post, class: "btn btn-warning" do
        | 注文をキャンセル

    - when :shippable
      .btn.btn-secondary.disabled
        | 出荷準備中
      p.note
        | ※ 支払い完了後に出荷手続きを開始します

    - when :trackable
      = link_to track_order_path(order), class: "btn btn-info" do
        | 発送状況を確認

    - when :completed
      = link_to order_receipt_path(order), class: "btn btn-success" do
        | 領収書を見る

メリット

  • 状態判定ロジックが一カ所にまとまり、テストもしやすい
  • 分岐が増えても、ビューは case 文だけを追えばOK

デメリット

  • ビュー内に case/when が残るため、完全には平坦化できない

比較と選び方

方法 可読性 拡張性 導入コスト
Partial 切り出し
Helper 移譲
  • 簡易な分岐は Partial 切り出しでビューをすっきり
  • 複雑な状態判定は Helper 移譲でロジックを集中

まとめと次のステップ

今回紹介した2つの手法を使えば、ビューの深いネストを解消し、可読性・保守性を向上させられます。
要件やチームの開発フローに合わせて最適な手法を選び、ぜひ試してみてください。
さらに ViewComponent や Presenter パターンなど、他のアプローチも検討するとより柔軟な設計が可能です。

ラブグラフのエンジニアブログ

Discussion