Ruby
Rails
rake

rakeタスクで使用するメソッドの共通化

rakeファイルのメソッド共通化

DRYなコードを書く上で、共通化した処理をどこに書くかで結構悩むかと思います。
今回はrakeタスクを書いていて、別ファイルに逃がすまではしたくないけど、共通化したい場合があって調べてみたら同じ境遇の方がいたので備忘録として残しておきます。

namespace :hoge do

  top_level = self

  using Module.new {
    refine(top_level.singleton_class) do
      def fuga
        p 'hogehoge'
      end
    end
  }

  desc 'test task'
  task test_task: :environment do |task, args|
    fuga
  end

end

オブジェクトの確認

[1] pry(main)> top_level
=> main
[2] pry(main)> top_level.singleton_class
=> #<Class:#<Object:0x007fa9470be378>>
[3] pry(main)> top_level.class
=> Object

実行

$ bin/rails hoge:test_task
=> "hogehoge"

トップレベルでオブジェクトを生成すると、Objectクラスのmainオブジェクトを生成する。
rubyにおけるトップレベル

refinements

引数 klass で指定したクラスだけに対して、ブロックで指定した機能を提供で きるモジュールを定義します。
定義した機能は Module#refine を使用せずに直 接 klass に対して変更を行う場合と異なり、限られた範囲のみ有効にできます。
定義した機能は main.using, Module#using を実行した場合のみ 有効になります。
ドキュメント

class C
  def foo
    puts "C#foo"
  end
end

module M
  refine C do
    def foo
      puts "C#foo in M"
    end
  end
end

x = C.new
x.foo # => "C#foo"

using M

x = C.new
x.foo # => "C#foo in M"

refine, using便利だな〜
組み込みクラスとか拡張するときには、逆にこの機能を使わないと不安なレベルではあるかなと。(滅多にないとは思うが...)

参考

rakeタスクの中だけで使うメソッドはRefinementsで定義するとべんり

Ruby の refinements の使い途

usingのスコープ

ドキュメント