Techioz Blog

Rails: ハッシュを含む列挙型がハッシュキー:値を返さない

概要

私は、性別やステータスなどの静的なモデル属性を持つ Ruby on Rails アプリケーションを構築しています。モデル内でそれらを列挙型として定義することにしました。列挙型を定義するために Ruby-enum gem を使用しています。

Ruby-enum gem をプロジェクトに追加し、バンドルインストールを実行しました。

gem 'ruby-enum', '~> 0.8.0'

ただし、列挙型は他のさまざまなモデルからアクセスできる必要があるため、モデルの懸念ディレクトリ内のモジュールとして列挙型を定義しました。

# app/models/concerns/status.rb

module Status
  extend ActiveSupport::Concern
  included do
    include Ruby::Enum

    define :ordered, 'Ordered'
    define :cancelled, 'Cancelled'
    define :waiting, 'Waiting'
  end
end

このモジュールを Users モデルに含めました

class User < ApplicationRecord
  include Status
end

そして、私の Users モデルにはステータスの列があります。

create_table "users", force: :cascade do |t|
  t.string "first_name"
  t.string "last_name"
  t.string "status"
  t.datetime "created_at", precision: 6, null: false
  t.datetime "updated_at", precision: 6, null: false
end

ただし、Rails コンソールで Status モジュールをクエリすると、次のようになります。

Status.all

エラーが発生します:

NoMethodError (undefined method `all' for Status:Module)

Rails コンソールでユーザー モデルのすべてのステータスをクエリすると、次のようになります。

User.statuses.all

エラーが発生します:

ArgumentError (wrong number of arguments (given 2, expected 0..1))

これをビューで利用できるようにする方法もわかりません

  # app/views/users/_form.html.erb

  <%= form.label :status %><br />
  <%= form.select :status, User.statuses.keys.collect { |status| status },{} %>  

列挙型を定義し、ユーザーのフォーム ビューで列挙型のキー値を返し、フォームでステータスを選択できるようにするにはどうすればよいですか?

解決策

.all は、ActiveRecord クラスのすべてのレコードを要求します。ステータスは ActiveRecord クラスではありません。それはクラスですらない、モジュールです。

User.statuses.all に関しては、ステータスがどこで定義されているかがわかりません。

Ruby::Enum にはいくつかの定数とメソッドが追加されていますが、Rails はそれらについて知りません。これらを自分で Rails に統合する必要があります。

Rails にはすでに enum が統合されています。

module Status
  extend ActiveSupport::Concern
  included do
    # It will take an Array, but it's good practice to be explicit.
    enum status: {ordered: 0, cancelled: 1, waiting: 2}
  end
end

User.statuses はステータスの HashWithIndependentAccess を返します。

これらの列挙型は整数にマップされ、文字列は列挙型のポイントを無効にすることに注意してください。ステータスを整数として保存すると、潜在的に多くのスペースを節約できます。 Rails は、整数を文字列にマッピングしたり、その逆のマッピングを処理します。

整数のステータスを使用するようにテーブルを必ず変更してください: t.string “status”。ステータスがオンのユーザーを回避するには、デフォルトまたは null のいずれかを定義する必要があります: false。

データの表示方法の詳細はデータベースに含まれていません。最も簡単な方法は、humanize を使用することです。

# ordered becomes Ordered. not_waiting becomes Not waiting.
User.statuses.keys.collect { |status| status.humanize }

それをステータスモジュールにプッシュできます。

module Status
  ...

  class_methods do
    def show_statuses
      statuses.keys.collect { |status| status.humanize }
    end
  end
end

User.show_statuses

これを選択として実行すると…

<%= f.select :status, User.statuses.collect { |status,id| [status.humanize,id] } %>

また、それをステータスにプッシュすることもできます。

module Status
  ...

  class_methods do
    ...

    def statuses_for_select
      statuses.collect { |status,id| [status.humanize,id] }
    end
  end
end

<%= f.select :status, User.statuses_for_select %>

表示の詳細が複雑になると、モデルが太くなります。次に、それをデコレータにプッシュすることを検討する必要があります。