Ruby でメソッド内で定数を定義する方法

Ruby ではメソッド内で定数を定義することは通常できない。

例:

def k
  K = 1
end

# /Users/aereal/Dropbox/sketches/const.rb:2: dynamic constant assignment
#   K = 1
#      ^

ちなみに dynamic constant assignment は「メソッドの定義内で定数を定義できない」という意味でしかなく、以下の定義はすべて有効であり警告も出力されない ( Ruby 2.2.4 )。

if rand(10).floor.even?
  N = 0
else
  N = 1
end

M = $M

if rand(10).floor.even?
  K = 1
end
# 定数 `K` を参照すると 1/2 の確率で NameError 例外が発生する。

定数なので静的な文脈で定義すればよいのだけれども、既に定義済みの定数を動的に書き換えたいことがある。たとえば OpenSSL::SSL::VERIFY_PEER など……。

さてメソッド内で定数を定義する方法はいくつか考えられる。

define_method を使う

self.class.send(:define_method, :k) { K = 1 }

Kernel#.require で定数を定義する別ファイルを読み込む

const.rb:

K = 1

k.rb:

def k
  require 'const'
  K
end

おわり

メタプログラミングすればだいたいなんとかなる Ruby のことなので、だいたいメタプログラミングなどでなんとかなった。

堅牢さが欲しいと思いつつも、いざとなればメタプログラミングでこじあけられるということは、ブラックボックスである部分が少なくなるということであり、いろいろなライブラリを組み合わせてアプリケーションを書くときにはけっこう助かることも多いですね。