Dissecting Method Calls

Sheldon Chi
Dec 13, 2016 · 6 min read

If you’ve been writing Ruby code for a while, you already have a visceral feel of how completely object-oriented Ruby is. In fact, most introductory Ruby books will tell you that writing Ruby programs is all about manipulating objects — using/creating objects, endowing them with abilities through method definitions, inheritance, and mix-ins, and asking them to perform actions through method calls.

Given how prevalent method and method calls are in a Ruby program, every beginning Ruby programmer should get a good grasp of the details of method calls.

overview of method call elements

Let’s start with the building blocks. Method calls may contain any of the following elements:

  • receiver (defaults to self if not provided)
  • dot operator (required if receiver is provided)
  • method name (required)
  • argument list (defaults to ())
  • code block (optional, no default)

There are methods that can be invoked with just the name and there are those that would need more to even run.

puts               # returns nil
[1,2,3].drop # ArgumentError

There are even methods that can be called with two arguments, an argument and a block, or even just one argument, and they’d all run fine.

receiver = "dissecting method calls"receiver.gsub("dissecting", "examining")
receiver.gsub("dissecting", {"dissecting" => "examining"}
receiver.gsub("dissecting") { |match| match + " and examining" }
receiver.gsub("dissecting") #=> Enumerator object

With the myriad of predefined methods that Ruby provides and the number of ways to call each method, how do we make sure that we are calling them the right way?

If it’s a predefined method in Ruby, look at the Ruby documentation. Otherwise, look at the method definition. The image below is lifted from the official Ruby documentation on gsub.

Notice that we invoked gsub in four ways just like the documentation shows.

In fact, Ruby-Doc gives you more than just variations for calling methods. It goes into details and gives a few examples. When in doubt, it’s always a good idea to check the documentation.

Every method belongs to an object and is always invoked on an object, which we call the receiver. A receiver can either be a literal object construct or a variable which represents the object.

string_variable = "RUBY METHODS""RUBY METHODS".downcase        #=> "ruby methods"
string_variable.downcase #=> "ruby methdos"

The two calls to downcase are invoked on a string object — the first one having a literal object construct as a receiver and the other, a variable representing a string. In practice, the receiver is usually a variable standing in for the actual object.

In the absence of an explicit receiver, however, the default value will be self. self is an object much like everything else in Ruby. It represents the “I” in the program, to which methods called without an explicit receiver is addressed.

It is important to note that there is only one self at any point, but who that self is changes depending on where you are in the code.

When an explicit receiver is provided, the dot operator (.) must tag along. In Ruby parlance, it’s called the message-sending operator where the method on the right of the dot is “sent” to the receiver on the left for execution.

In the code example above, what we are effectively saying is, “Hey string object, here’s a message: sort.”

You might ask, “How can we be sure that the receiver knows what to do with the message?” Glad that you asked! In fact, the code above raises an error — NoMethodError. It means that "ruby_methods" does not know what to do with the message.

It’s the equivalent of me handing you this message.


You’d be like…


…unless you know Chinese.

Every object knows how to respond to a certain set of methods — not all methods. If it doesn’t understand, it raises an exception just like what happened above.

Then you might ask, “How do we know which set of methods or messages an object can respond to?” Given that there’s so much that goes into determining the answer to that question including a discussion on inheritance and method lookup paths, I’ll reformulate the question to, “How do we know that an object responds to a certain method?” Through the respond_to? method that Ruby provides.

"ruby methods".respond_to(:sort)       #=> false
"ruby methods".respond_to(:upcase) #=> true
my_array = [1, 2, 3, 4]my_array.respond_to?(:sort) #=> true
my_array.respont_to?(:upcase) #=> false

It returns true if the receiver on the left knows what to do with the method argument of respond_to?, and false otherwise. The argument of the method is a method name prepended with a colon (:).

Methods are just like mathematical functions or, if you hate math, machines. They take in inputs, do something with that input, and then output the result of the previous step — much like below.

In Ruby, however, methods aren’t only used to get outputs, better known as return values in Ruby. Other use cases are to mutate passed inputs, or change the object states.

These inputs are the arguments. To invoke a method with arguments, you put them inside parentheses after the method name although parentheses are optional.

[1, 2, 3, 4].take(2)                #=> [1, 2]
"ruby_methods".split(' ') #=> ["ruby", "methods"]
"ruby_methods"[0, 4] #=> "ruby"

Methods can take in zero, one, two, or any number of arguments. Much like method call elements where some are optional and some are required, method arguments can also be either. They can also be a string, array, hash, or even any object. Figuring this out is important as passing in one less argument or an argument of a different class may spell the difference between an error or the code running.

How do you figure out which is which? You can either look at the method signature at Ruby documentation if it’s part of the standard library or at the method definition if it’s a method that you or someone else defined.

Methods can also be called with a code block. The difference between a method that can or cannot accommodate a code block boils down to its ability to yield. Simply put, if there’s a yield statement inside, the method can take a block. The yield method, literally, yields control to the block passing in any argument that is provided.

def ruby_methods
yield("I'm in the block!")
puts "I'm back in the method."
ruby_methods { |message| p message.size + 1 } #=> 18
#=> I'm back in the method."

The code inside the block is then executed. It can do whatever it wants to the passed in argument given it’s valid in Ruby. It can even ignore the passed in argument like below.

ruby_methods { |message| puts "Where's the message?" }

Control, along with the return value of the block, is then handed back to the original method and execution of code resumes right after yield.

So that’s a quick run through over the syntactic elements of a method call. I’ll delve much deeper on each of them next.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store