Ruby: 定数に再代入したら例外を発生させる

背景

Rubyでは定数に再代入した場合、warningは出るが処理がとまることはない。 警告メッセージだけだと見逃すことがあるので例外を発生させたい。

Ruby本来の動作

a.rb

DUP = 'file a'

b.rb

DUP = 'file b'

main.rb

require_relative 'a'
require_relative 'b'

上記のようなファイル構成のときmain.rbを実行すると以下のような出力になる。

出力

b.rb:1: warning: already initialized constant DUP
a.rb:1: warning: previous definition of DUP was here

解決策

GitHub - jeremyevans/ruby-warning: Add custom processing for warnings

このwarningというgemを導入すると Warning.ignore という特定の警告を非表示にする機能と Warning.process という警告をキャッチして任意の処理を実行する機能が使えるようになる。

今回は Warning.process を使いmain.rbを以下のように書き換え、定数上書きの警告メッセージをキャッチして例外を発生させるようにする。

main.rb

require 'warning'

Warning.process do |warning|
  next :raise if warning.match?(/previous definition of \w+ was here/)

  :default
end

require_relative 'a'
require_relative 'b'

出力

b.rb:1: warning: already initialized constant DUP
Traceback (most recent call last):
        3: from b:10:in `<main>'
        2: from b:10:in `require_relative'
        1: from b.rb:1:in `<top (required)>'
~/.gem/ruby/2.7.0/gems/warning-1.1.0/lib/warning.rb:213:in `warn': a.rb:1: warning: previous definition of DUP was here (RuntimeError)

Warning.process のブロック引数には警告メッセージの文字列が渡される。 このブロックは戻り値が :raise のとき例外を発生させる。 戻り値が :default の場合は本来の挙動と同じく警告メッセージを出力する。

定数上書き時は2つのメッセージが出るが、1つ目で例外を発生させてしまうと、片方のメッセージしか表示されず原因が分かりづらくなるので少し注意が必要だ。