write_menu ヘルパー : 小林研 Rails Tips (40)

はじめに

Rails Tips の 40 回目です。リンクは lotfp で簡単に書くことができましたが、メニューページのリンクはリンクだけでなく、説明文や権限の表示なども同時に表示します。これもたくさん記述するのでメニューページの描画を簡単にする write_menu ヘルパーを作成しています。

write_menu の使い方

基本的にはこんな感じでメニューを作成しています。通常はパス名を記述するだけです。

%h2 行事日程関係
%table.bordered
  = menu_link_title
  %tbody
    = write_menu teacher_events_path

lotfp と同様に locales に登録しているものと異なるリンクタイトルにしたい場合には title を指定します。ここでは「(今年度)」という情報を追加したいため title を別途指定しています。

= write_menu list_teacher_syllabus_pages_path(edit_year_id: @now_year.id), title: "最近更新したシラバス(今年度)"

通常は edit action の権限を編集権限で確認しますが、edit action が存在しないコントローラの場合には、edit_action で権限確認する action を指定します。

= write_menu shussekibo_teacher_lectures_path(date: Time.zone.today), edit_action: :create

共通メニューなどは全員に権限があるため、わざわざ権限描画はしません。この場合 no_priv を設定すると権限列を描画しません。

= write_menu teacher_jikanwaris_path, no_priv: true

分類用に1列目に rowspan を追加したいときには、add_rowspan を追記します。

= write_menu teacher_kenshins_path, add_rowspan: ["保健室関連", 2]

menu_link_title の実装

write_menu の前に記述してあった menu_link_title を先に紹介します。基本は、リンク、内容、権限のヘッダを描画するだけです。add_type を true した場合、1列目に分類が追加され、no_priv を true にした場合、権限列がなくなります。

  # @param [Boolean] add_type 分類を追加する時に true
  # @param [Boolean] no_priv 権限を追加しない時に true
  # @return [ActiveSupport::SafeBuffer] メニューリンクタイトル
  def menu_link_title(add_type: false, no_priv: false)
    headers = (add_type ? %w[分類] : []) + %w[リンク 内容] + (no_priv ? [] : %w[権限])
    content_tag :thead do
      content_tag :tr do
        headers.each do |w|
          concat(content_tag :th, w)
        end
      end
    end
  end

write_menu_sub の実装

write_menu はパスからリンクの説明と権限を描画します。それらを取得するサブメソッドである write_menu_sub を作っておきます。write_menu_sub はこれまで紹介してきた detail_from_cap などを呼び出して配列を準備するだけのものです。

  # @param [Hash] session user_session または admin_session
  # @param [String] path URL
  # @param [Symbol, nil] method GET 以外の場合は記述
  # @return [Array<String>] 表示するメニュー要素の配列
  def write_menu_sub(path, method = :get, edit_action: :edit)
    cap = controller_action_and_params(path, method)
    normal = privileges_str_from_cap(cap)
    edit = privileges_str_from_cap cap.merge({action: edit_action})
    [
      detail_from_cap(cap),
      (edit.blank? || normal == edit) ? normal : "#{normal} (編集: #{edit})",
    ]
  end

write_menu の実装

write_menuwrite_menu_sub で取得した情報を使って、content_tag ヘルパーでメニューを作成しています。Rails 5 からは content_tag の代わりに tag ヘルパーを使うことが推奨されているのですが、tag.tr と記述すると RubyMine のエラー(String.tr と混同して引数が足りないとエラーになる)が消えなくなってしまうので、いまだに content_tag を使っています。

# @param [Hash] session user_session または admin_session
  # @param [String] path URL
  # @param [Symbol, nil] method GET 以外の場合は記述
  # @return [String] 表示するメニュー行
  def write_menu(path, method = :get, add_rowspan: nil, title: nil, detail: nil, edit_action: :edit, cond: true,
                 no_priv: false)
    auto_detail, privileges = write_menu_sub(path, method, edit_action: edit_action)
    detail ||= auto_detail
    content_tag :tr do
      concat(content_tag :th, add_rowspan.first, rowspan: add_rowspan.last) if add_rowspan
      concat content_tag :th, lotfp(path, method, title: title, cond: cond), class: "left"
      concat content_tag :td, detail, class: "left"
      concat(content_tag :td, privileges, class: "left") unless no_priv
    end
  end

おわりに

これまでメニューを一つ表示するだけでも haml で5行くらいの手書きをしていたのですが、write_menu だとリンクをひとつ書くだけになりました。おかげでメニューページはかなりスッキリしました。