LoginSignup
3
0

More than 1 year has passed since last update.

Railsでモデル名.newしたときのBoolean型の変換について調べた

Last updated at Posted at 2023-01-31

前提

調査に使用したRailsとActiveModelのバージョンは以下の通り。

rails console
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 になったりしたのでどこで定義されてるのか調べてみた。

rails console
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が定義されている。

rails/activemodel/lib/active_model/type/boolean.rb#L13-L22
  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への変換

rails console
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 に含まれていないから、ということだった。

rails console
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に変換される。

rails console
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以外をキャストするように定義されている。

ActiveModel::Type::Value#L55-L57
def cast(value)
    cast_value(value) unless value.nil?
end

また、ActiveModel::Type::Valueを継承しているActiveModel::Type::Booleanではcast_valueをオーバーライドしていて、空文字列でもnilを返すようになっていた。

ActiveModel::Type::Boolean#L36-L43
 private
    def cast_value(value)
      if value == ""
        nil
      else
        !FALSE_VALUES.include?(value)
      end
    end

よって、空文字列またはnilを指定した場合はnilが返る。

rails console
irb(main):110:0> ActiveModel::Type::Boolean.new.cast(nil)
=> nil
irb(main):111:0> ActiveModel::Type::Boolean.new.cast("")
=> nil

調査にあたって参考にした記事

Ruby 3.2 のIRBの新機能
debug.gemを使ったことがない方に向けて、いくつかの機能を紹介する

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0