Ruby Language
Metaprogramacion
Buscar..
Introducción
La metaprogramación se puede describir de dos maneras:
"Programas de computadora que escriben o manipulan otros programas (o ellos mismos) como sus datos, o que hacen parte del trabajo en tiempo de compilación que de otra manera se haría en tiempo de ejecución".
En pocas palabras, la metaprogramación es escribir código que escribe código durante el tiempo de ejecución para hacer su vida más fácil .
Implementando "con" usando evaluación de instancia
Muchos lenguajes cuentan with
una declaración with
que los programadores pueden omitir el receptor de llamadas a métodos.
with
puede ser fácilmente emulado en Ruby usando instance_eval
:
def with(object, &block)
object.instance_eval &block
end
El método with
se puede usar para ejecutar métodos sin problemas en objetos:
hash = Hash.new
with hash do
store :key, :value
has_key? :key # => true
values # => [:value]
end
Definiendo métodos dinámicamente.
Con Ruby puedes modificar la estructura del programa en tiempo de ejecución. Una forma de hacerlo, es definiendo métodos dinámicamente usando el método method_missing
.
Digamos que queremos poder probar si un número es mayor que otro número con la sintaxis 777.is_greater_than_123?
.
# open Numeric class
class Numeric
# override `method_missing`
def method_missing(method_name,*args)
# test if the method_name matches the syntax we want
if method_name.to_s.match /^is_greater_than_(\d+)\?$/
# capture the number in the method_name
the_other_number = $1.to_i
# return whether the number is greater than the other number or not
self > the_other_number
else
# if the method_name doesn't match what we want, let the previous definition of `method_missing` handle it
super
end
end
end
¿Algo importante para recordar cuando se usa method_missing
que también se debe anular respond_to?
método:
class Numeric
def respond_to?(method_name, include_all = false)
method_name.to_s.match(/^is_greater_than_(\d+)\?$/) || super
end
end
Olvidarse de hacerlo conduce a una situación inconsistente, cuando puede llamar con éxito 600.is_greater_than_123
, pero 600.respond_to(:is_greater_than_123)
devuelve falso.
Definiendo métodos en instancias.
En ruby puedes agregar métodos a instancias existentes de cualquier clase. Esto le permite agregar comportamiento y una instancia de una clase sin cambiar el comportamiento del resto de las instancias de esa clase.
class Example
def method1(foo)
puts foo
end
end
#defines method2 on object exp
exp = Example.new
exp.define_method(:method2) {puts "Method2"}
#with method parameters
exp.define_method(:method3) {|name| puts name}
método de enviar ()
send()
se usa para pasar un mensaje a un object
. send()
es un método de instancia de la clase Object
. El primer argumento en send()
es el mensaje que está enviando al objeto, es decir, el nombre de un método. Podría ser una string
o un symbol
pero se prefieren los símbolos . Luego, los argumentos que necesitan pasar en el método, esos serán los argumentos restantes en send()
.
class Hello
def hello(*args)
puts 'Hello ' + args.join(' ')
end
end
h = Hello.new
h.send :hello, 'gentle', 'readers' #=> "Hello gentle readers"
# h.send(:hello, 'gentle', 'readers') #=> Here :hello is method and rest are the arguments to method.
Aquí está el ejemplo más descriptivo.
class Account
attr_accessor :name, :email, :notes, :address
def assign_values(values)
values.each_key do |k, v|
# How send method would look a like
# self.name = value[k]
self.send("#{k}=", values[k])
end
end
end
user_info = {
name: 'Matt',
email: '[email protected]',
address: '132 random st.',
notes: "annoying customer"
}
account = Account.new
If attributes gets increase then we would messup the code
#--------- Bad way --------------
account.name = user_info[:name]
account.address = user_info[:address]
account.email = user_info[:email]
account.notes = user_info[:notes]
# --------- Meta Programing way --------------
account.assign_values(user_info) # With single line we can assign n number of attributes
puts account.inspect
Nota: send()
sí ya no se recomienda. Use __send__()
que tiene el poder de llamar a métodos privados, o (recomendado) public_send()