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