Ruby
Rails
mongoid

Mongoid6のモデルでRegexpを使うと妙なことが起きる

Mongoidを使っているRailsアプリを、Rails4から5にあげるときに奇妙なことが起きた。
Rails4から5にあげるのに伴って、Mongoidを5.xから6.xに更新したのだが、テストが通らなくなった。

問題の箇所を抜粋すると以下のようなコードになる。

my_model.rb
class MyModel
  include Mongoid::Document
  field :key, type: String
  field :default, type: String
  field :format, type: String

  validates :key, presence: true, uniqueness: true, format: {with: /\A\w+\z/}
  validate :default_value_conform_to_format

  private
  def default_value_conform_to_format
    regexp = Regexp.new(self.format.to_s)
    unless regexp =~ self.default.to_s
      errors.add(:default, "does not match regexp #{self.format}")
    end
  end
end

Mongoidを6にあげるとdefault_value_conform_to_formatでvalidationエラーが発生するようになる。
ちなみにテストケースでは、defaultformatもどちらもnilになっている。
空の正規表現は、空文字列にマッチするはずなので、エラーが起きるはずはないと思っていた。

// =~ ""  #=> 0

問題となっていたのは、実はRubyの標準ライブラリのRegexpではなくて、Mongoidの中のMongoid::Matchable::Regexpというクラスを参照するように変わってしまっていたことだった。
どうやらMongoidが勝手にこのような名前のクラスを追加してしまっていたようだ。しかもタチの悪いことに標準のRegexpと挙動が異なる。

-    regexp = Regexp.new(self.format.to_s)
+    regexp = ::Regexp.new(self.format.to_s)

としたところ解決した。