labels ヘルパー : 小林研 Rails Tips (31)

はじめに

Rails Tips の 31 回目です。なんだかんだで1月続いてしまいました。昨日から私がよく利用しているヘルパーメソッドをいくつか紹介しています。今日のは昨日の t_ars の応用版です。

labels

昨日の t_ars はモデルの属性名を取得するためのメソッドでした。モデルの index だと属性の一覧を作成することが多いです。そんな時こんな表を作ったりするのではないでしょうか。

西暦 デフォルト年度 制御
2024 false デフォルトに変更
2023 true
2022 false デフォルトに変更
2021 false デフォルトに変更
以下 続く デフォルトに変更

こんなとき、haml だと以下のような記述をするのではないかと思います。

%table.bordered
  %thead
    %tr
      - %w[西暦 デフォルト年度 制御].each do |w|
        %th= w
  %tbody
    (後略)

この部分、昨日の t_ars が使えそうですね。さらに楽をするために labels というヘルパーを作っています。こんな感じで使います。

%table.bordered
  %thead
    = labels Year, %i[year default_year], add_control: true
  %tbody
    (後略)

こちらのテストはこんな感じで書いています。キーワード引数ありの単一メソッド呼び出しはまだ紹介していませんでしたね。一緒に紹介します。

describe ApplicationHelper, type: :helper do
  subject { helper }

  describe "common test" do
    it_behaves_like "単一メソッド呼び出し(キーワード引数あり)" do
      let(:test_set) do
        {
          labels: [
            [Year, %i[year default_year]], {}, "<tr><th>西暦</th><th>デフォルト年度</th></tr>",
            [Year, %i[year default_year]], {add_control: true}, "<tr><th>西暦</th><th>デフォルト年度</th><th>制御</th></tr>",
          ],
        }
      end
    end
  end
end

単一メソッド呼び出し(キーワード引数あり)

以前紹介した単一メソッド呼び出し( 単一メソッド呼び出し shared_example: 小林研 Rails Tips (12) - hkob’s blog)は通常引数のみでキーワード引数には対応していないものでした。test_set を「通常引数」、「キーワード引数」、「検査する結果」の3つの繰り返しに変更したものです。キーワード引数を **k で渡す処理を追加しただけになります。

shared_examples_for "単一メソッド呼び出し(キーワード引数あり)" do
  it "上述のメソッドの結果が正しいこと" do
    test_set.each do |method, answers|
      print "\tmst: #{method}\n" if documentation_formatter?
      answers.each_slice(3) do |(v, k, a)|
        expect(subject.send(method, *v, **k)).to eq a
      end
    end
  end
end

labels の実装

labels は内部で t_ars を呼び出し、content_tag を concat で連結しているだけです。校務支援システムでは制御を最後の列に記述することが多いので、add_control が設定されている時だけ制御列を追加します。

  # @param [Class] model モデルクラス
  # @param [Array<Symbol>] array アトリビュートkeyの配列
  # @param [Boolean] add_control 制御列を追加する時に true
  # @return [ActiveSupport::SafeBuffer] ラベルの行
  def labels(model, array, add_control: false)
    content_tag :tr do
      t_ars(model, array).each do |w|
        concat(content_tag :th, w)
      end
      if add_control
        concat(content_tag :th, I18n.t("common.control"))
      end
    end
  end

おわりに

列の名前は localesmodels/ja.yml から取得します。モデル自体にその値がなくても、この yml 内にキーの名前があれば表示されます。これによって、ページごとに日本語表記が揺れる問題も回避できますね。