Fast Method Source

Description

Fast Method Source is a Ruby library for quering methods, procs and lambdas for their source code and comments.

require 'fast_method_source'
require 'fast_method_source/core_ext'
require 'set'

puts Set.instance_method(:merge).source

Output.

  def merge(enum)
    if enum.instance_of?(self.class)
      @hash.update(enum.instance_variable_get(:@hash))
    else
      do_with_enum(enum) { |o| add(o) }
    end

    self
  end

Installation

All you need is to install the gem.

gem install fast_method_source

Synopsis

Fast Method Source provides similar functionality to method_source, which is being used by the Pry REPL, but with a number of major key differences.

Speed improvements

The main goal of Fast Method Source is to be as fast as possible. In result, the library is much faster than method_source. What's acceptable for Pry, when you query only one method, is not acceptable for other use cases such as querying all the methods defined in your Ruby process. The benchmark showed that Fast Method Source is about 5-10x faster than its competitor.

I'd be remiss if I didn't mention that there's also room for further speed improvements. The benchmarks below represent comparison of both libraries.

#comment_and_source

ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]

This is a utility method and method_source doesn't feature it.

Processor: Intel(R) Core(TM) i5-2410M CPU @ 2.30GHz
Platform: ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]
Counting the number of sample methods...
Sample methods: 19439
Rehearsal ---------------------------------------------------------------------------
FastMethodSource#comment_and_source_for  24.080000   1.900000  25.980000 ( 28.425889)
----------------------------------------------------------------- total: 25.980000sec

                                              user     system      total        real
FastMethodSource#comment_and_source_for  23.130000   1.710000  24.840000 ( 27.097578)

#source

ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]
Processor: Intel(R) Core(TM) i5-2410M CPU @ 2.30GHz
Platform: ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]
Counting the number of sample methods...
Sample methods: 19438
Rehearsal -----------------------------------------------------------
FastMethodSource#source  18.760000   1.290000  20.050000 ( 21.802014)
MethodSource#source     100.100000   0.440000 100.540000 (122.748676)
------------------------------------------------ total: 120.590000sec

                              user     system      total        real
FastMethodSource#source  19.040000   1.170000  20.210000 ( 22.079805)
MethodSource#source      92.740000   0.300000  93.040000 (103.833941)

#comment

ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]
Processor: Intel(R) Core(TM) i5-2410M CPU @ 2.30GHz
Platform: ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]
Counting the number of sample methods...
Sample methods: 19595
Rehearsal ------------------------------------------------------------
FastMethodSource#comment   4.120000   0.520000   4.640000 (  5.188443)
MethodSource#comment      82.480000   0.330000  82.810000 (103.135012)
-------------------------------------------------- total: 87.450000sec

                               user     system      total        real
FastMethodSource#comment   3.580000   0.370000   3.950000 (  4.398557)
MethodSource#comment      73.390000   0.150000  73.540000 ( 81.880220)

Correctness of output

Fast Method Source is capable of displaying source code even for dynamically defined methods (there are some crazy methods in stdlib).

require 'fast_method_source'
require 'rss'

puts RSS::Maker::ChannelBase.instance_method(:dc_description=).source

Output.

            def #{name}=(#{new_value_variable_name})
              #{local_variable_name} =
                #{plural_name}.first || #{plural_name}.new_#{new_name}
              #{additional_setup_code}
              #{local_variable_name}.#{attribute} = #{new_value_variable_name}
            end

RAM consumption

Memory consumption is considerably higher for Fast Method Source (for the benchmarks it used about 800-1500 MB of RES RAM). In fact, since the library is very young, it may have memory leaks.

API

General description

The library provides two methods #source and #comment. There are two ways to use Fast Method Source. One way is to monkey-patch relevant core Ruby classes and use the methods directly.

require 'fast_method_source'
require 'fast_method_source/core_ext'

# With UnboundMethod
Set.instance_method(:merge).source
#=> "  def merge(enum)\n..."

# With Method
Set.method(:[]).source
#=> "  def self.[](*ary)\n..."

# With Proc (or lambda)
myproc = proc { |arg|
  arg + 1
}
myproc.source
#=> "myproc = proc { |arg|\n..."

The other way is by using these methods defined on the library's class directly.

require 'fast_method_source'

# With UnboundMethod
FastMethodSource.source_for(Set.instance_method(:merge))
#=> "  def merge(enum)\n..."

# With Method
FastMethodSource.source_for(Set.method(:[]))
#=> "  def self.[](*ary)\n..."

# With Proc (or lambda)
myproc = proc { |arg|
  arg + 1
}
FastMethodSource.source_for(myproc)
#=> "myproc = proc { |arg|\n..."

Methods

FastMethodSource#source_for(method)

Returns the source code of the given method as a String. Raises FastMethodSource::SourceNotFoundError if:

  • method is defined outside of a file (for example, in a REPL)
  • method doesn't have a source location (typically C methods)

Raises IOError if:

  • the file location of method is inaccessible
FastMethodSource.source_for(Set.instance_method(:merge))
#=> "  def merge(enum)\n..."
FastMethodSource.source_for(Array.instance_method(:pop))
#=> FastMethodSource::SourceNotFoundError

FastMethodSource#comment_for(method)

Returns the comment of the given method as a String. The rest is identical to FastMethodSource#source_for(method).

FastMethodSource#comment_and_source_for(method)

Returns the comment and the source code of the given method as a String (the order is the same as the method's name). The rest is identical to FastMethodSource#source_for(method).

Limitations

Rubies

  • CRuby 2.2.2
  • CRuby 2.3.0dev and higher

Rubies below 2.2.2 were not tested, so in theory if it quacks like Ruby 2, it may work.

OS'es

  • GNU/Linux
  • Mac OS (hopefully)

Roadmap

Further speed improvements

Although Fast Method Source is faster than any of its competitors, it's still very slow. On average, a mature Rails 4 application has at least 45K methods. In order to query all those methods for their source code, you would need to wait for a while and perhaps to drink a cup of tea.

The goal of the project is to be able to query 50K methods in less than 15 seconds. Whether it's possible or not is to be determined.

Decrease memory consumption

I'm not happy about the current rates. To be investigated.

Licence

The project uses Zlib License. See the LICENCE.txt file for more information.