Rubyの問題点
カジュアルにファイルスコープのメソッドが作れない
CやGoでは簡単にそのファイル内でのみ使用可能な関数を書くことができますが、
Rubyの場合、たとえprivateメソッドを書いたとしても、別のファイルから__send__
等で呼ぶことができてしまいます。
これでは全てのメソッドがどのファイルからorどのライブラリから呼ばれているか、分かりません。
そのため、ちょっと処理をまとめて読みやすくしたいだけなのに公開されるAPIが増えちゃって他のライブラリに依存されちゃってという問題が起こり得ます。(僕は経験ないけど)
そんなこんなでそのファイルだけで使えるメソッドがあれば、修正範囲が限られているので、メンテナの気を楽にすることが期待できそうです。
ご提案
ちょこっと調べたところ、Rubyでのファイルスコープが使えるのは2つあるようです。
- 一時変数
- Refinements
一時変数
sum = proc{|i, j| i + j }
p sum.call(3,5) #=> 8
require './lib'
p sum.call(3,5) #=> NameError
簡易的にはこれでよさそうです。
しかしながらclass構造だととたんに破綻します。
sum = proc{|i, j| i + j }
class OpenClass
def foo
sum(3, 5) #=> NameError
end
end
違う、そうじゃない。
Refinements
そこでRefinementsを使ってみます。
class OpenClass
module InternalMethods
refine OpenClass do
def bar
puts "bar"
end
end
end
using InternalMethods
def foo
bar
end
end
require './lib'
OpenClass.new.foo #=> "bar"
OpenClass.new.bar #=> NameError
ちょっと書き方がめんどくさいですが、lib.rb
だけで(本当はclass〜endのなかで)有効なOpenClass#bar
というメソッドを作ることができました。
さらに、どのclassでも使えるような関数sum
もこんな風に定義可能です。
module InternalMethods
refine Object do
def sum(i, j)
i + j
end
end
end
using InternalMethods
class OpenClass
def foo
sum(2, 3)
end
end
p sum(3, 5) #=> 8
require './lib'
p OpenClass.new.foo #=> 5
p sum(3, 5) #=> NameError
これでlib.rb
内ではsum
が外で使われることがないことが保証されているので、リファクタリング等が簡単になりそうです。
デメリットは、InternalMethods
みたいなmoduleができてしまう、テストが書きにくい、ぐらいでしょうか。
まとめ
いまいち使いドコロがないRefinementsのカジュアルな使い道として、ファイルスコープメソッドを作ってみるのもいいかもしれません。