API などでクライアント側から渡ってきた "true"
、"false"
、1
、0
などの値を、 Ruby の true
や false
として扱いたいときがある。
Ruby では false
と nil
以外の値はすべて true
として扱われるため、!!
を先頭につけたとしても実現できない。
!!nil #=> false
!!0 #=> true
!!'false' #=> true
そのようなときには ActiveRecord::Type::Boolean#cast
を使う。
ActiveRecord::Type::Boolean.new.cast "true" #=> true
ActiveRecord::Type::Boolean.new.cast 0 #=> false
しかしながら、どんな値でも true
か false
にしてくれるわけではない。
ActiveRecord::Type::Boolean.new.cast nil #=> nil
ActiveRecord::Type::Boolean.new.cast "" #=> nil
結論を先に書くと、
- nil になるもの =>
nil
,""
- false になるもの =>
false
,0
,"0"
,:"0"
,"f"
,:f
,"F"
,:F
,"false"
,:false
,"FALSE"
,:FALSE
,"off"
,:off
,"OFF"
,:OFF
- true になるもの => 上記以外すべて
となる。ロジックの定義箇所は以下を参照。
定義箇所
rails/activerecord/lib/active_record/type.rb
を見てみると、実態は rails/activemodel/lib/active_model/type/boolean.rb
に定義されていることがわかる。
rails/activerecord/lib/active_record/type.rb
#...
module ActiveRecord
module Type
@registry = AdapterSpecificRegistry.new
class << self
attr_accessor :registry # :nodoc:
#...
def register(type_name, klass = nil, **options, &block)
registry.register(type_name, klass, **options, &block)
end
#...
end
#...
Boolean = ActiveModel::Type::Boolean
#...
register(:boolean, Type::Boolean, override: false)
#...
end
end
rails/activemodel/lib/active_model/type/boolean.rb
を見てみると、真偽値の判定ロジックが書いてある。
rails/activemodel/lib/active_model/type/boolean.rb
module ActiveModel
module Type
#...
class Boolean < Value
FALSE_VALUES = [
false, 0,
"0", :"0",
"f", :f,
"F", :F,
"false", :false,
"FALSE", :FALSE,
"off", :off,
"OFF", :OFF,
].to_set.freeze
#...
private
def cast_value(value)
if value == ""
nil
else
!FALSE_VALUES.include?(value)
end
end
end
end
end
rails/activemodel/lib/active_model/type/value.rb
module ActiveModel
module Type
class Value
#...
def cast(value)
cast_value(value) unless value.nil?
end
#...
end
end
end
結論
処理の流れをまとめると以下のようになる。
-
ActiveRecord::Type::Boolean.new.cast(value)
が呼ばれる -
ActiveModel::Type::Boolean.new.cast(value)
が呼ばれる -
value
がnil
でなければActiveModel::Type::Boolean.new.cast_value(value)
が呼ばれる -
value
が空文字ならnil
、FALSE_VALUES
に含まれている値ならfalse
、そうでなければtrue
を返す
よって、冒頭で触れたような結果になる。