Rails ActiveSupport の String#blank? について

github.com

現在、ActiveSupportString#blank? は以下のような方式で判定を行っており、Stringに不正なエンコードの文字が含まれていると Regexp#match から ArgumentError が発生する。

class String
  def blank?
    empty? || /\A[[:space:]]*\z/.match?(self)
  end
end

"".blank?     # => true
" ".blank?    # => true
"hoge".blank? # => false
"\xAE".blank? # => ArgumentError: invalid byte sequence in UTF-8

それに対し、文字列の中に「不正な文字」が含まれているのであれば、それは空白で構成されている文字列ではないから false と判定してもいいのでは、という要旨で Pull-Request を送りつけたのが冒頭のリンクとなる。

# 差分はこんな感じ
-    empty? || /\A[[:space:]]*\z/.match?(self)
+    empty? || valid_encoding? && /\A[[:space:]]*\z/.match?(self)

この変更に対しての懸念がコアメンバー各位からコメントされ、

  1. 他の Stringインスタンスメソッド (例えば、 String#strip ) と挙動が変わってしまう
  2. 不正なエンコード文字が含まれた文字列 ( ill-formed ) の場合 String#blank? の挙動は未定義のため、現状から変更しないほうがいい

の大きく2つを指摘された。(英語が難しいので、他にもある場合は指摘してほしい)

これに対しては、

  1. String#empty? のように評価を行うメソッドと比較すべきで、実際にエラーを発生させずに評価を行うことは可能である
  2. String#blank? は対象オブジェクトのエンコードにおいての空白文字のみで文字列が構成されているかどうかを判定するメソッドなので、 ill-formed な文字列を特別に例外とするのはおかしい
  3. そもそも、Stringの各メソッドから ArgumentError が発生することはドキュメントに書かれていないので、将来的にこのような挙動になることを保証されていないししていない
  4. だいたい、 ArgumentErrorRegexp#match が送出するエラーでは

という反論を考えた。 ここで、各Stringの文字列とメソッドの挙動を整理してみると、

String #empty? #valid_encoding? #blank? #strip #scrub
"" true true true "" ""
" " false true true "" " "
"a" false true false "a" "a"
"\xAE" false false ArgumentError ArgumentError "�"

となり、この中では #blank? のみが ActiveSupport のメソッドであるが、基本的に素のStringは処理/評価を行えるものはそれを行うような方針であるように見える。

また、コメントにおける ill-formed な文字列を特別視するという方針を改めてしまえば、空白文字の集合の否定には ill-formed 文字が含まれるので、 #valid_encoding? はそのまま ill-formed な文字列の包有確認に使用することができると考える。(もちろん、 #valid_encoding?false なら自動的に #blank?false になる。)

というところまでが自身の考えの整理ですが、この Pull-Request についての応援や反論などがあれば、Pull-Request のコメント欄やエモート、または直接私に Twitter などで語りかけてもらえるとうれしいです。 (本音: 英語がしんどい)