すがブロ

sugamasaoのhatenablogだよ

Rails 4.1のenumの挙動(2)

Rails 4.1 rc2での動作です。 前回の続きです。

前回、キーの項目と数値を定義したのだけど、実はキーの項目だけ指定しても数値として保存されるようだ(キーだけだと文字列で保存されるのかと思っていた……)。

class Product < ActiveRecord::Base
  enum status: %w(normal sale empty)
end

こんな感じで定義してから ./bin/rails cして確認する。 まずはどのようにデータが保存されるか。

Loading development environment (Rails 4.1.0.rc2)
irb(main):001:0> Product.create(status: 1)
   (0.2ms)  begin transaction
  SQL (0.9ms)  INSERT INTO "products" ("created_at", "status", "updated_at") VALUES (?, ?, ?)  [["created_at", "2014-04-06 02:53:47.039739"], ["status", 1], ["updated_at", "2014-04-06 02:53:47.039739"]]
   (1.3ms)  commit transaction
=> #<Product id: 1, name: nil, status: 1, created_at: "2014-04-06 02:53:47", updated_at: "2014-04-06 02:53:47">
irb(main):002:0> Product.create(status: "sale")
   (0.2ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "products" ("created_at", "status", "updated_at") VALUES (?, ?, ?)  [["created_at", "2014-04-06 02:53:57.177845"], ["status", 1], ["updated_at", "2014-04-06 02:53:57.177845"]]
   (1.0ms)  commit transaction
=> #<Product id: 2, name: nil, status: 1, created_at: "2014-04-06 02:53:57", updated_at: "2014-04-06 02:53:57">
irb(main):003:0> Product.create(status: :sale)
   (0.2ms)  begin transaction
  SQL (0.6ms)  INSERT INTO "products" ("created_at", "status", "updated_at") VALUES (?, ?, ?)  [["created_at", "2014-04-06 02:54:05.389452"], ["status", 1], ["updated_at", "2014-04-06 02:54:05.389452"]]
   (1.3ms)  commit transaction

ついでに、前回は書かなかったんだけど述語メソッドも生えているのでそっちも確認しておく。ここで保存したのはsaleの値なので、sale?で問い合わせすることができる。

irb(main):013:0> pro = Product.where(status: 1).first
  Product Load (0.3ms)  SELECT  "products".* FROM "products"  WHERE "products"."status" = 1  ORDER BY "products"."id" ASC LIMIT 1
=> #<Product id: 1, name: nil, status: 1, created_at: "2014-04-06 02:53:47", updated_at: "2014-04-06 02:53:47">
irb(main):014:0> pro.sale?
=> true

ここではどうでも良いけど、emptyってステータスはメソッド呼び出しだとちょっと誤解を招きそうなので辞めたほうが良いですね。

検索する場合、ステータス毎のクラスメソッドがある。当然、メソッドチェインでwhere区等もつなげることができる。

irb(main):003:0* Product.sale
  Product Load (3.3ms)  SELECT "products".* FROM "products"  WHERE "products"."status" = 1
=> #<ActiveRecord::Relation [#<Product id: 1, name: nil, status: 1, created_at: "2014-04-06 02:53:47", updated_at: "2014-04-06 02:53:47">, #<Product id: 2, name: nil, status: 1, created_at: "2014-04-06 02:53:57", updated_at: "2014-04-06 02:53:57">, #<Product id: 3, name: nil, status: 1, created_at: "2014-04-06 02:54:05", updated_at: "2014-04-06 02:54:05">]>
irb(main):004:0> Product.sale.where('id > 2')
  Product Load (0.4ms)  SELECT "products".* FROM "products"  WHERE "products"."status" = 1 AND (id > 2)
=> #<ActiveRecord::Relation [#<Product id: 3, name: nil, status: 1, created_at: "2014-04-06 02:54:05", updated_at: "2014-04-06 02:54:05">]>

ただ、where区に文字列やシンボルで渡しても想定通りの動作にはならない。シンボルはともかく、文字列を渡すと0で検索しちゃうのはウッカリハマる可能性がありそう……。

irb(main):008:0> Product.where(status: 'sale')
  Product Load (0.3ms)  SELECT "products".* FROM "products"  WHERE "products"."status" = 0
=> #<ActiveRecord::Relation []>
irb(main):009:0> Product.where(status: :sale)
  Product Load (0.4ms)  SELECT "products".* FROM "products"  WHERE "products"."status" = 'sale'
=> #<ActiveRecord::Relation []>

私からは以上です。