Doubt about using send() in ruby

Asked

Viewed 381 times

2

The general idea of OO is that each object can react in a different way to the same message. In ruby, I noticed that it is possible to use the send() method and that every Ruby object has this method.

So I can do something like: Object.send(:method)

But what I don’t understand is: what is the advantage of passing a symbol to the method? Symbols are immutable, and save a key to the method? key type => value? Or allocate and leave in memory the variables used in the method for as long as the program is running?

1 answer

5

The method send allows you to call methods on any object dynamically.

For example, imagine you have a class defined as follows:

class Usuario
  def initialize(nome, email)
    @nome = nome
    @email = email
  end

  def nome
    @nome
  end

  def email
    @email
  end
end

This class defines 3 methods: one initialization (which takes 2 arguments: name and email), and 2 accessors for the name and email attributes.

We can use it as follows:

marcos = Usuario.new('Marcos', '[email protected]')

marcos.name # => 'Marcos'
marcos.email # => '[email protected]'

The method send can be used as follows:

marcos.send(:name) # => "Marcos"
marcos.send(:email) # => "[email protected]"

We can call any method of the object this way:

marcos.send(:class) # => Usuario
'100'.send(:to_f) # => 100.0
'Stack Overflow'.send(:upcase) # => "STACK OVERFLOW"
'Stack Overflow'.send(:downcase) # => "stack overflow"

This allows some interesting techniques, such as calling a different method on an object based on some input, without the need for a string of if's and else's. For example, this code:

def acessar_atributo_1(nome_atributo)
  if nome_atributo == 'nome'
    objeto.nome
  elsif nome_atributo == 'email'
    objeto.email
  elsif nome_atributo == 'idade'
    objeto.idade
  end
end

could be replaced by that much more concise version:

def acesso_atributo_dinamico(nome_atributo)
  objeto.send(nome_atributo.to_sym) # Convertemos o argumento do tipo String para um Symbol antes de passar adiante
end

This code however presents a loophole: the first version (with if's) only allowed the 3 methods (nome, email and idade were accessed); the second however allows any method name passed to be accessed, which can be an unwanted condition. A good practice is, before calling the method send, make sure that the argument passed is the name of a method which we must allow access to:

def acesso_atributo_dinamico(nome_atributo)
  if [:nome, :email, :idade].include?(nome_atributo)
    objeto.send(nome_atributo.to_sym) # Convertemos o argumento do tipo String para um Symbol antes de passar adiante
  end
end

Browser other questions tagged

You are not signed in. Login or sign up in order to post.