前提
調査に使用したRailsとActiveModelのバージョンは以下の通り。
irb(main):101:0> Rails.version
=> "7.0.4.2"
irb(main):102:0> ActiveModel.version
=> Gem::Version.new("7.0.4.2")
調べたことと結論
RailsでBoolean型の属性を持つモデルのインスタンスを生成するときに、以下のように適当な値を入力すると false
になったり true
になったりしたのでどこで定義されてるのか調べてみた。
irb(main):001:0> Book
=> Book(id: integer, is_e_book: boolean, created_at: datetime, updated_at: datetime)
irb(main):002:0> Book.new(is_e_book: true).is_e_book
=> true
irb(main):003:0> Book.new(is_e_book: 0).is_e_book
=> false
irb(main):004:0> Book.new(is_e_book: 10).is_e_book
=> true
irb(main):005:0> Book.new(is_e_book: :true).is_e_book
=> true
irb(main):006:0> Book.new(is_e_book: "").is_e_book
=> nil
irb(main):007:0> Book.new(is_e_book: nil).is_e_book
=> nil
irb(main):008:0> Book.new(is_e_book: "FALSE").is_e_book
=> false
irb(main):009:0> Book.new(is_e_book: "False").is_e_book
=> true
ActiveModel::Type::Boolean
の中にはFALSE_VALUES
が定義されている。
FALSE_VALUES = [
false, 0,
"0", :"0",
"f", :f,
"F", :F,
"false", :false,
"FALSE", :FALSE,
"off", :off,
"OFF", :OFF,
].to_set.freeze
この中に含まれている値でBoolean値を指定した場合にはfalse
になる。
ただし、空文字列またはnil
を指定した場合にはnil
になる。
実験
falseへの変換
irb(main):010:0> ActiveModel::Type::Boolean::FALSE_VALUES
=> #<Set: {false, 0, "0", :"0", "f", :f, "F", :F, "false", :false, "FALSE", :FALSE, "off", :off, "OFF", :OFF}>
irb(main):011:1* ActiveModel::Type::Boolean::FALSE_VALUES.each do |f|
irb(main):012:1* book = Book.new(is_e_book: f)
irb(main):013:1* puts "#{book.object_id} is_e_book => #{book.is_e_book}"
irb(main):014:0> end
1775500 is_e_book => false
1775520 is_e_book => false
1775540 is_e_book => false
1775560 is_e_book => false
1775580 is_e_book => false
1775600 is_e_book => false
1775620 is_e_book => false
1775640 is_e_book => false
1775660 is_e_book => false
1775680 is_e_book => false
1775700 is_e_book => false
1775720 is_e_book => false
1775740 is_e_book => false
1775760 is_e_book => false
1775780 is_e_book => false
1775800 is_e_book => false
上記の実行例で"False"
を指定してもtrue
になってしまったのはActiveModel::Type::Boolean::FALSE_VALUES
に含まれていないから、ということだった。
irb(main):008:0> Book.new(is_e_book: "FALSE").is_e_book
=> false
irb(main):009:0> Book.new(is_e_book: "False").is_e_book
=> true
nilへの変換
ActiveModel::Type::Boolean#L8-L11 のコメントにも書かれているが、空文字列もnil
に変換される。
irb(main):006:0> Book.new(is_e_book: "").is_e_book
=> nil
irb(main):007:0> Book.new(is_e_book: nil).is_e_book
=> nil
ActiveModel::Type::Value#cast
ではnil
以外をキャストするように定義されている。
def cast(value)
cast_value(value) unless value.nil?
end
また、ActiveModel::Type::Value
を継承しているActiveModel::Type::Boolean
ではcast_value
をオーバーライドしていて、空文字列でもnilを返すようになっていた。
private
def cast_value(value)
if value == ""
nil
else
!FALSE_VALUES.include?(value)
end
end
よって、空文字列またはnil
を指定した場合はnil
が返る。
irb(main):110:0> ActiveModel::Type::Boolean.new.cast(nil)
=> nil
irb(main):111:0> ActiveModel::Type::Boolean.new.cast("")
=> nil