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()



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow