その他
    ホーム技術発信DoRubysource_locationでRubyを冒険

    source_locationでRubyを冒険

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

    rubyのMethodやProcにはsource_locationというメソッドがあります。これを使うことでゴーストメソッドでも定義場所を発見できRubyコードの探索を簡単に行うことができます。

    はじめに

    新卒最終日になりました。くろすです。
    今回はメソッド探索に使うと便利な Method#source_location, Proc#source_location の紹介をします。

    Method Class

    メソッドクラスは Object#methodによりオブジェクト化する際に使われるクラスです。
    詳しいことはだいたいドキュメントに書いてありますが、Procオブジェクトと違いレシーバーの情報も持っていて、クロージャーではないというのが大きな特徴です。
    すでに定義してあるメソッドをオブジェクトとして取ってこれるという点で便利だなと思います。

    https://docs.ruby-lang.org/ja/latest/class/Method.html

    source_location

    さてそんなMethodClassの source_location ですが、色々話すより見てもらう方が早いと思います。

    [1] pry(main)> ActiveRecord::Base.methods.grep(/transaction/)
    => [:transaction]
    [2] pry(main)> ActiveRecord::Base.method(:transaction)
    => #<Method: Class(ActiveRecord::Transactions::ClassMethods)#transaction>
    [3] pry(main)> ActiveRecord::Base.method(:transaction).source_location
    => ["path/to/gems/activerecord-4.1.15/lib/active_record/transactions.rb", 206]
    

    使ってるgemが古いのは置いといてgemの中でもrubyで定義してあれば探すことができます。

    [1] pry(main)> class Ghost
    [1] pry(main)*   (1..3).each{|n| define_method("method_#{n}"){ puts n } }
    [1] pry(main)* end
    => 1..3
    [2] pry(main)> g = Ghost.new
    => #<Ghost:0x007f99da931d38>
    [3] pry(main)> g.method_1
    1
    => nil
    [4] pry(main)> g.method(:method_1).source_location
    => ["(pry)", 2]
    

    ご覧の通りdefine_methodでも余裕です。
    が、method_missingを利用したゴーストメソッドはさすがに追い切れないです。

    [1] pry(main)> Hash.method(:new).source_location
    => nil
    

    ruby外で定義されているメソッドは残念ながら探せません。

    終わりに

    ぶっちゃけると define_method 対策として紹介しているので、define_methodを使わないようなコードではあまり使いどころがないと思います。
    僕自身は開発にneovimを使うのでコードジャンプできなかった場合、ターミナルエミュレータを起動、highway、rails consoleからのsouce_location の順番でメソッドを探しています。
    プログラムを読むために検索するというのは日常的なことだと思うのでその検索が少しでも早くなる手助けになればと思います。