LoginSignup
2
2

More than 5 years have passed since last update.

Rubyでファイルスコープのメソッドを作る

Posted at

Rubyの問題点

カジュアルにファイルスコープのメソッドが作れない

CやGoでは簡単にそのファイル内でのみ使用可能な関数を書くことができますが、

Rubyの場合、たとえprivateメソッドを書いたとしても、別のファイルから__send__等で呼ぶことができてしまいます。

これでは全てのメソッドがどのファイルからorどのライブラリから呼ばれているか、分かりません。

そのため、ちょっと処理をまとめて読みやすくしたいだけなのに公開されるAPIが増えちゃって他のライブラリに依存されちゃってという問題が起こり得ます。(僕は経験ないけど)

そんなこんなでそのファイルだけで使えるメソッドがあれば、修正範囲が限られているので、メンテナの気を楽にすることが期待できそうです。

ご提案

ちょこっと調べたところ、Rubyでのファイルスコープが使えるのは2つあるようです。

  • 一時変数
  • Refinements

一時変数

lib.rb
sum = proc{|i, j| i + j }
p sum.call(3,5) #=> 8
main.rb
require './lib'
p sum.call(3,5) #=> NameError

簡易的にはこれでよさそうです。

しかしながらclass構造だととたんに破綻します。

lib.rb
sum = proc{|i, j| i + j }
class OpenClass
  def foo
    sum(3, 5) #=> NameError
  end
end

違う、そうじゃない。

Refinements

そこでRefinementsを使ってみます。

lib.rb
class OpenClass
  module InternalMethods
    refine OpenClass do
      def bar
        puts "bar"
      end
    end
  end
  using InternalMethods

  def foo
    bar
  end
end
main.rb
require './lib'
OpenClass.new.foo #=> "bar"
OpenClass.new.bar #=> NameError

ちょっと書き方がめんどくさいですが、lib.rbだけで(本当はclass〜endのなかで)有効なOpenClass#barというメソッドを作ることができました。

さらに、どのclassでも使えるような関数sumもこんな風に定義可能です。

lib.rb
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
main.rb
require './lib'

p OpenClass.new.foo #=> 5
p sum(3, 5) #=> NameError

これでlib.rb内ではsumが外で使われることがないことが保証されているので、リファクタリング等が簡単になりそうです。

デメリットは、InternalMethodsみたいなmoduleができてしまう、テストが書きにくい、ぐらいでしょうか。

まとめ

いまいち使いドコロがないRefinementsのカジュアルな使い道として、ファイルスコープメソッドを作ってみるのもいいかもしれません。

2
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2