Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
13
Help us understand the problem. What is going on with this article?
@natsuokawai

ActiveRecord::Type::Boolean#cast の返り値が nil となるケースについて

More than 1 year has passed since last update.

API などでクライアント側から渡ってきた "true""false"10 などの値を、 Ruby の truefalse として扱いたいときがある。
Ruby では falsenil 以外の値はすべて 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

しかしながら、どんな値でも truefalse にしてくれるわけではない。

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

結論

処理の流れをまとめると以下のようになる。

  1. ActiveRecord::Type::Boolean.new.cast(value) が呼ばれる
  2. ActiveModel::Type::Boolean.new.cast(value) が呼ばれる
  3. valuenil でなければ ActiveModel::Type::Boolean.new.cast_value(value) が呼ばれる
  4. value が空文字なら nilFALSE_VALUES に含まれている値なら false、そうでなければ true を返す

よって、冒頭で触れたような結果になる。

13
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
stmn-co
名古屋のITスタートアップベンチャーです。「一人でも多くの人に、感動を届け、幸せを広める。」の経営理念のもと、 エンゲージメント経営コンサルティング「TUNAG」、オンラインファンサロンアプリ「FANTS」などのサービスを展開しています。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
13
Help us understand the problem. What is going on with this article?