リクエスト中のパラメータにて、true/falseの値が来ることを期待されているデータがある場合に、Booleanへのキャストをしたときの Rails4/5挙動の差異があるのを知ったので、後学のためにメモ。
Rails5から提供されているActiveRecord::Type::Boolean.new.castだと下記。
irb(main):001:0> require 'active_record' => true irb(main):002:0> ActiveRecord.version => #<Gem::Version "5.2.0"> irb(main):003:0> ActiveRecord::Type::Boolean.new.cast(false) => false irb(main):004:0> ActiveRecord::Type::Boolean.new.cast('false') => false irb(main):005:0> ActiveRecord::Type::Boolean.new.cast(true) => true irb(main):006:0> ActiveRecord::Type::Boolean.new.cast('true') => true irb(main):007:0> ActiveRecord::Type::Boolean.new.cast('a') => true irb(main):008:0> ActiveRecord::Type::Boolean.new.cast('\t') => true
Rails4で提供されているActiveRecord::Type::Boolean.new.type_cast_from_userだと、true/falseでもないものが来た時はfalseになる。 Rails5からはtrueになる。
irb(main):001:0> require 'active_record' => true irb(main):002:0> ActiveRecord.version => #<Gem::Version "4.2.7.1"> irb(main):003:0> require 'active_support/core_ext' => true irb(main):005:0> ActiveRecord::Type::Boolean.new.type_cast_from_user(false) => false irb(main):006:0> ActiveRecord::Type::Boolean.new.type_cast_from_user('false') => false irb(main):007:0> ActiveRecord::Type::Boolean.new.type_cast_from_user(true) => true irb(main):008:0> ActiveRecord::Type::Boolean.new.type_cast_from_user('true') => true irb(main):009:0> ActiveRecord::Type::Boolean.new.type_cast_from_user('a') DEPRECATION WARNING: You attempted to assign a value which is not explicitly `true` or `false` ("a") to a boolean column. Currently this value casts to `false`. This will change to match Ruby's semantics, and will cast to `true` in Rails 5. If you would like to maintain the current behavior, you should explicitly handle the values you would like cast to `false`. (called from irb_binding at (irb):9) => false irb(main):010:0> ActiveRecord::Type::Boolean.new.type_cast_from_user('\t') DEPRECATION WARNING: You attempted to assign a value which is not explicitly `true` or `false` ("\\t") to a boolean column. Currently this value casts to `false`. This will change to match Ruby's semantics, and will cast to `true` in Rails 5. If you would like to maintain the current behavior, you should explicitly handle the values you would like cast to `false`. (called from irb_binding at (irb):10) => false
というか、ちゃんとWARNINGもでるのでえらいなあと思う次第です。
上記で何が困るかというと、パラメータのデフォルトの値がtrueなんだけど、実はユーザがinvalidな値を送って来ていた場合に、ユーザから見た時にRails4 -> 5で意図せずfalse -> true変わってしまうように見えてしまう、ということが起こり得る、という話。
こういう挙動の違いを見てると、デベロッパーの人は大変やな・・・と思うので、感謝の気持ちが絶えないですね。