Ruby: Making sen(d)se of send()

In programming, good code is the code that gets the job done. Great code is the code that gets other other jobs done. If the Golden rule of Ruby is “Don’t Repeat Yourself”, the Silver rule might be to “Be As Abstract As Possible.”

Definition

The send() method acts on an object to invoke the method supplied in send()’s arguments. In layman’s terms, you use the send method to call another method on the same object. You might be wondering, “Isn’t the send method just a middleman? Why can’t I just call the method on the object directly without the send method?” We’ll get to that later.

Syntax

The send() method always takes at least, but is not limited to, 1 argument. This means you could could call on the send() method with 1 argument or even 10 arguments. These arguments can be given as either a symbol or a string.

sample_object = Object.new
sample_object.send(:class) == sample_object.send("class")
#=> true

The first argument supplied is always the method you want to call on the original object. Think of this as the secondary method, which in the case above, is the .class method. This secondary method is then called on, or “sent” to, the original object. The result would be the same if you called on the secondary method directly on the object.

sample_object.send(class)
=> Object
# is equivalent to 
sample_object.class
=> Object

Every argument after the first one will be the arguments supplied to secondary method. In the following example, we set a variable “number” equal to a value of 1 and send the “+” method to “number” with an argument of 2. This is equivalent to doing “number.+ 2” which is just “number + 2”.

number = 1
number.send("+", 2)
=> 3
number.+(2)
=> 3
number + 2
=> 3

The basic syntax of the send method can be described as:

some_object.send(some_method, *arguments)

It acts on some_object, calls some_method on that object, with a number of arguments supplied to that some_method.

The asterisk is the splat operator, which basically means any number of arguments, from 0 to any number.

Uses

Now you’re probably wondering, when is this ever useful. One example is instantiating three classes with different attributes. In our following example, we have 3 classes, an Author class, an Artist class, and a Friend class.

class Author
attr_accessor :pen_name
  def initialize(pen_name)
@pen_name = pen_name
end
end
class Actor
attr_accessor :stage_name

def initialize(stage_name)
@stage_name = stage_name
end
end
class Friend
attr_accessor :nickname
  def initialize(nickname)
@nickname = nickname
end
end

These initialize methods seem pretty similar and they are! They’re all just taking an argument and setting it equal to an attribute of the current class. This method could be abstracted and placed in a superclass which these 3 classes will inherit from. Let’s call this class Person.

class Person 
def initialize(attributes={})
attributes.each do |key, value|
self.send("#{key}=", value)
end
end
end

class Actor < Person
attr_accessor :stage_name
end
class Author < Person
attr_accessor :pen_name
end
class Friend < Person
attr_accessor :nickname
end

The initialize method is abstracted into a super class. In this abstracted initialize method, you’re receiving a hash as an argument, which we called attributes. The keys in this hash are the attributes you want to assign to the classes (in Actor, it would be stage_name) and the values are what you want that attribute to equal. We iterate over attributes and for each key-value pair, we’re using send() to assign a value to each attribute listed in the hash.

We could make instances of the each of the classes like so:

actor = Actor.new(stage_name: "Martin Sheen")
author = Author.new(pen_name: "Mark Twain")
friend = Friend.new(nickname: "Crazy Ian")
actor.stage_name
=> "Martin Sheen"
author.pen_name
=> "Mark Twain"
friend.nickname
=> "Crazy Ian"

Isn’t that cool!? You could make new classes that inherit from the Person class and you just need to include an attr_accessor with the attribute you want to use without writing the initialize method in the class. The MVP here is the send method. Each time you’re using the initialize method for a different class, you’re calling on a different setter method. This shows you how versatile send() is in making abstract code.

# send called on these 3 methods!
actor.stage_name= "Martin Sheen"
author.pen_name= "Mark Twain"
friend.nickname= "Crazy Ian"

Essentially, send() can be translated as:

some_object.send(some_method, *arguments)
actor.send(stage_name=, "Martin Sheen")
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.