Ruby Ruby Foundations Modules Extend and Include: Part 2

What does the Ruby send method do?

I'm unclear as to what the send method is doing in the Formatter module of formatter.rb. Here's the code:

module Formatter
  def display
    self.class.format_attributes.each do |attribute|
      puts "[#{attribute.to_s.upcase}] #{send(attribute)}"
    end
  end
end

I read some Ruby documentation on the send method, but it still doesn't make sense to me. What is it doing and why is it necessary here?

2 Answers

Every time you call a method on an object you're "sending" that object a "message" where the message is the name of the method you called. If you call #to_s on a FixNum you're sending the message to_s to the instance of the class FixNum.

The send method allows you to send a message (call a method) when you won't know the name of that method until runtime. In this particular example you're getting a list of attributes, printing each attribute name as well as its value. The only way to get the value is to actually call the method.

Hope that helps!

Thanks, that made it a lot clearer for me.

Antoine Boillot
Antoine Boillot
10,466 Points

Hi all,

For those having a hard time understanding Geoff's answer (no offense Geoff), I did a few web searches and managed to understand the following :

As Jason already explained when writing the following :

attr_accessor :name

what the program is really doing is the following :

def name
    @name 
end

def name=(str)
    @name = str
end

The send methods allows to call a method using the following syntax :

send(:method_to_call)

Meaning, using an example, that :

class Person

    def name
        @name
    end

    def name=(str)
        @name = str
    end

end

person = Person.new
person.name = "Test"
puts person.name

is equivalent to typing the following :

class Person

    def name
        @name
    end

    def name=(str)
        @name = str
    end

end

person = Person.new
person.send(:name=, "Test")
puts person.send(:name)

Now if we go back to the code Jason is typing in this example. The following...

def display
    self.class.format_attributes.each do |attribute|
        puts "[#{attribute.to_s.upcase}] #{send(attribute)}"
    end
end

...calls

format_attributes

which is a class method hence you do:

self.class.format_attributes

and since this method is suppose to return an array of attributes, which are passed as symbols:

formats :name, :phone_number, :email, :experience

Hence

@format_attributes = [:name, :phone_number, :email, :experience]

Now in each block with attribute, you have:

puts "[#{attribute.to_s.upcase}] #{send(attribute)}"

Which can be translated to, for an instance:

puts "[#{:name.to_s.upcase}] #{send(:name)}"

And by using the send method on :name, the program will call the following methods

def name
    @name
end

That's why you manage to get the value of the attribute, the instance variable @name being already defined through

resume.name = "Jason"

Hope this helps.

Cheers,

Allen Wang
Allen Wang
3,439 Points

Wow that's a great explanation! Thx for really clearing things up. I would be completely lost without this!

Víctor Rico
Víctor Rico
17,808 Points

Thanks Antoine Boillot. Very well explained.

I wonder why Jason didn't explained those concepts.