Have you ever wanted to pass a method to a function that only takes a block? Or figure out which of your object’s superclasses broke the method you’re trying to call?

Those things are easy to do with the method method. Use it well, and you can learn about your dependencies, save yourself hours of debugging, and get your code to the places it needs to be.

Methods as easy as lambdas

Lots of Ruby methods take blocks or lambdas. But you can’t directly pass a method to another method, the way you would with a lambda. You have to use method first:

1
2
3
4
irb(main):001:0> sin_method = Math.method(:sin)
=> #<Method: Math.sin>
irb(main):002:0> (1..10).map(&sin_method)
=> [0.8414709848078965, 0.9092974268256817, 0.1411200080598672, -0.7568024953079282, -0.9589242746631385, -0.27941549819892586, 0.6569865987187891, 0.9893582466233818, 0.4121184852417566, -0.5440211108893699]

So, what’s happening here?

The first line turns the method Math.sin into a Method object. Just like lambdas, Method objects respond to call. Method objects respond to to_proc (Thanks, Benoit). So they can be used in the same places you’d use a lambda:

1
2
3
4
irb(main):004:0> sin_method = -> (x) { Math.sin(x) }
=> #<Proc:0x007fe9f90a9bd8@(irb):4 (lambda)>
irb(main):005:0> (1..10).map(&sin_method)
=> [0.8414709848078965, 0.9092974268256817, 0.1411200080598672, -0.7568024953079282, -0.9589242746631385, -0.27941549819892586, 0.6569865987187891, 0.9893582466233818, 0.4121184852417566, -0.5440211108893699]

Take a look at the first line again. This code, using a lambda, works the same way as the earlier code, using method.

Depending on how you’ve written your code, getting ahold of a Method object can be a lot easier than wrapping it in a lambda. So when you need a lambda and all you have is a method, remember the method method.

Where did that method come from?

You just called a method on your object, and it does something you didn’t expect. Maybe someone overrode it or monkey-patched it. How do you figure this out?

You could dig through the source code for a few hours. But Method has two methods that can speed things up.

To figure out which class defined the method you’re calling, use owner:

1
2
irb(main):003:0> Task.new.method(:valid?).owner
=> ActiveRecord::Validations

When you want to go a little deeper and figure out exactly where the method was defined, use source_location:

1
2
irb(main):004:0> Task.new.method(:valid?).source_location
=> ["/usr/local/lib/ruby/gems/2.1.0/gems/activerecord-4.2.0.beta2/lib/active_record/validations.rb", 55]

source_location returns an array. The first element is the path to the file where the method was defined, and the second element is the line number. With those, you know exactly where to look next.

Reading the source (without having to dig)

Once you can figure out where a method was defined, you might want to see how it’s defined.

Method can’t do that by itself. But if you install the method_source gem, you can see the source code for many methods right from your console:

1
2
3
4
5
6
7
irb(main):002:0> puts Task.new.method(:valid?).source
    def valid?(context = nil)
      context ||= (new_record? ? :create : :update)
      output = super(context)
      errors.empty? && output
    end
=> nil

You can even see the comments:

1
2
3
4
5
6
7
8
9
10
11
12
irb(main):003:0> puts Task.new.method(:valid?).comment
# Runs all the validations within the specified context. Returns +true+ if
# no errors are found, +false+ otherwise.
#
# Aliased as validate.
#
# If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
# <tt>new_record?</tt> is +true+, and to <tt>:update</tt> if it is not.
#
# Validations with no <tt>:on</tt> option will run no matter the context. Validations with
# some <tt>:on</tt> option will only run in the specified context.
=> nil

Pretty awesome, right? Your documentation is right in the console!

Introspection is awesome

Some of my favorite Ruby classes are those that let you inspect your code from within your code. Classes like Class, Module, and Method. With these, you can learn a ton about your code as it runs, and modify it on the fly. Learn these classes well, study their API, and you’ll be able to do amazing things with Ruby.

Did you like this post? You should read these:

Finished another Rails tutorial and still don’t know how to start?

Have you slogged through the same guide three times and still can't retain enough to write apps on your own?

In my free 7-part course, you’ll discover the fastest way to learn and remember new Rails ideas, so you can use them when you need them. And you'll learn to use what you already know to build your own Rails project.



Comments