【Rails】ActiveRecord で属性名に ?(はてな)をつけると true / falseを返すけど、使う際は boolean の属性に限定したほうがいい
※どちらかというと自分への備忘記事になります。
タイトルのままですが、以下のような場合に、
create_table "books", force: :cascade do |t| t.string "title" t.integer "price" t.boolean "published" # ... end
以下のようになります。
[2] pry(main)> book.title => "わがはいはねこである" [3] pry(main)> book.title? => true [4] pry(main)> book.price => 0 [5] pry(main)> book.price? => false [6] pry(main)> book.published => true [7] pry(main)> book.published? => true
これを使って以下のように書けるので、より読みやすいコードにできます。
if book.published? # ... end # ↓と同じ(はてなをとっただけ) if book.published # ... end
仕組み
属性名に ?(はてな)をつけると、query_attribute メソッド(↓)のエイリアスが呼び出されます。
https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Query.html#method-i-query_attribute
ただ、query_attribute メソッドは以下のようになっており、属性値が0の場合など、やや我々の想定と違う動きをするものとなっています。
(Ruby では 0 は true な値ですが、query_attribute メソッドでは false を返します)
そのため、使用する際は boolean の属性に限定したほうがよさそうです。
...という議論が ActiveRecordのプロパティにblank? や present? をする必要はなかった! - Qiita でされていました。
# File activerecord/lib/active_record/attribute_methods/query.rb, line 12 def query_attribute(attr_name) value = self[attr_name] case value when true then true when false, nil then false else if !type_for_attribute(attr_name) { false } if Numeric === value || !value.match?(/[^0-9]/) !value.to_i.zero? else return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value) !value.blank? end elsif value.respond_to?(:zero?) !value.zero? else !value.blank? end end end