metaprogramación (en ruby): programas que escriben programas

Post on 06-Jul-2015

4.637 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

DESCRIPTION

Ponencia sobre metaprogramación en la Conferencia Rails '2007 (Madrid 22 y 23 de noviembre), por Sergio Gil

TRANSCRIPT

MetaprogramaciónProgramas que escriben programas

(en Ruby)

Sergio Gil

Sólo para vagos

“Para qué voy a hacer [tarea X] si puedo escribir un programa que lo

haga por mí”Cualquier programador, en cualquier momento,

ante cualquier situación

Programar no es una excepción

Automatización

Acercar el lenguaje al problema(para resolverlo mejor)

Qué es la metaprogramación y de dónde ha salido semejante cosa

puts "puts 'hola'"

$ ruby -e "`ruby hola.rb`"

Programas que escriben otros programas

Programas que modifican su propio comportamiento

(es decir, se escriben a sí mismos)

“In Lisp, you don’t just write your program down toward the language,

you also build the language up toward your program.”

Paul Graham

Tipos de metaprogramación

Estática / Interna

Dinámica / Interna

Metaprogramación en Rails

Metaprogramación en Rails

• Generadores

Metaprogramación en Rails

• Generadores

• Métodos mágicos

Metaprogramación en Rails

• Generadores

• Métodos mágicos

• method_missing

Metaprogramación en Rails

• Generadores

• Métodos mágicos

• method_missing

• const_missing

Metaprogramación en Rails

• Generadores

• Métodos mágicos

• method_missing

• const_missing

• Definiciones dinámicas

Metaprogramación en Rails

• Generadores

• Métodos mágicos

• method_missing

• const_missing

• Definiciones dinámicas

$ grep -r method_missing vendor/rails/ | wc -l72

Metaprogramación en Rails

• Generadores

• Métodos mágicos

• method_missing

• const_missing

• Definiciones dinámicas

$ grep -r method_missing vendor/rails/ | wc -l72$ grep -r const_missing vendor/rails/ | wc -l41

Metaprogramación en Rails

• Generadores

• Métodos mágicos

• method_missing

• const_missing

• Definiciones dinámicas

$ grep -r method_missing vendor/rails/ | wc -l72$ grep -r const_missing vendor/rails/ | wc -l41$ grep -r define_method vendor/rails/ | wc -l67

Técnicas en Ruby/Rails

Generadores

Generadores

class ModelGenerator < Rails::Generator::NamedBase def manifest record do |m| m.directory File.join('app/models', class_path) m.directory File.join('test/unit', class_path) m.directory File.join('test/fixtures', class_path) m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb") m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb") m.template 'fixtures.yml', File.join('test/fixtures', "#{table_name}.yml") m.migration_template 'migration.rb', 'db/migrate', :assigns => { :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}" }, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}" end endend

method_missing

class Array def method_missing(meth, *args, &blk) if meth.to_s =~ /^map_(.+)$/ map {|i| i.send($1)} else super end endend(1..5).to_a.map_to_s

method_missing

class Array def method_missing(meth, *args, &blk) if meth.to_s =~ /^map_(.+)$/ map {|i| i.send($1)} else super end endend(1..5).to_a.map_to_s

method_missing

>> ["1", "2", "3", "4", "5"]

const_missing

class Module alias :normal_const_missing :const_missing

def const_missing(cname) return normal_const_missing(cname) rescue nil unless table_name = SchemaLookup.models[cname] raise NameError.new("uninitialized constant #{cname}") end klass = Class.new(ActiveRecord::Base) const_set cname, klass klass.set_table_name table_name klass end end

const_missing

alias

alias

class String alias :largo :lengthend

puts "hola".largoputs "hola".length

alias

class String alias :largo :lengthend

puts "hola".largoputs "hola".length

44

alias

class String alias :largo :lengthend

puts "hola".largoputs "hola".length

class String alias :old_length :length def length old_length + 2 endend

puts "hola".length

44

alias

class String alias :largo :lengthend

puts "hola".largoputs "hola".length

class String alias :old_length :length def length old_length + 2 endend

puts "hola".length

44

6

alias_method_chain(el estándar de Rails para añadir funcionalidad a un método preexistente)

alias_method_chain(el estándar de Rails para añadir funcionalidad a un método preexistente)

class String def length_with_message puts "Calculando longitud de #{self}" length_without_message end alias_method_chain :length, :messageendputs "hola".length

alias_method_chain(el estándar de Rails para añadir funcionalidad a un método preexistente)

class String def length_with_message puts "Calculando longitud de #{self}" length_without_message end alias_method_chain :length, :messageendputs "hola".length

Calculando longitud de hola4

send y define_methodla metaprogramación pata negra

send

str = "Metaprogramaciongue"

puts str.upcaseputs str.send(:upcase)

send

str = "Metaprogramaciongue"

puts str.upcaseputs str.send(:upcase)

METAPROGRAMACIONGUEMETAPROGRAMACIONGUE

send

str = "Metaprogramaciongue"

puts str.upcaseputs str.send(:upcase)

METAPROGRAMACIONGUEMETAPROGRAMACIONGUE

[ :upcase, :downcase, :reverse ].each do |m| puts str.send(m)end

send

str = "Metaprogramaciongue"

puts str.upcaseputs str.send(:upcase)

METAPROGRAMACIONGUEMETAPROGRAMACIONGUE

[ :upcase, :downcase, :reverse ].each do |m| puts str.send(m)end

METAPROGRAMACIONGUEmetaprogramaciongueeugnoicamargorpateM

send

define_method y def

define_method y def

class Prueba def foo "foo" end define_method(:bar) do "bar" endend

p = Prueba.newputs p.fooputs p.bar

define_method y def

class Prueba def foo "foo" end define_method(:bar) do "bar" endend

p = Prueba.newputs p.fooputs p.bar

foobar

¿¿Y entonces??

class Prueba [ :foo, :bar, :jander, :klander ].each do |m| define_method(m) do m.to_s end endend

p = Prueba.newputs p.fooputs p.barputs p.janderputs p.klander

¿¿Y entonces??

class Prueba [ :foo, :bar, :jander, :klander ].each do |m| define_method(m) do m.to_s end endend

p = Prueba.newputs p.fooputs p.barputs p.janderputs p.klander

foobarjanderklander

¿¿Y entonces??

foobarjanderklander

Versión para realmente vagos

M = [ :foo, :bar, :jander, :klander ]

class Prueba M.each do |m| define_method(m) do m.to_s end endend

p = Prueba.newM.each do |m| puts p.send(m)end

Un ejemplo pequeño (pero real) de algunas de estas cosas juntas

VALIDATION_METHODS = [:presence, :numericality, :format, :length, :acceptance, :confirmation]VALIDATION_METHODS.each do |type| define_method "validates_#{type}_of_with_live_validations".to_sym do |*attr_names| send "validates_#{type}_of_without_live_validations".to_sym, *attr_names define_validations(type, attr_names) end alias_method_chain "validates_#{type}_of".to_sym, :live_validationsend

Un consejito

Usa módulos (mixins) para extender clases

class String def italianize self.gsub(/[aeiou]/, 'i') endend

Usa módulos (mixins) para extender clases

class String def italianize self.gsub(/[aeiou]/, 'i') endend

module Italianization def italianize self.gsub(/[aeiou]/, 'i') end endString.send(:include, Italianization)

Usa módulos (mixins) para extender clases

¿Y por qué?

¿Y por qué?

1. Gracias al const_missing de Rails, no importa el orden en que carguen las definiciones

¿Y por qué?

1. Gracias al const_missing de Rails, no importa el orden en que carguen las definiciones

2. Más fácil de depurar

¿Y por qué?

1. Gracias al const_missing de Rails, no importa el orden en que carguen las definiciones

2. Más fácil de depurar

String.ancestors

¿Y por qué?

1. Gracias al const_missing de Rails, no importa el orden en que carguen las definiciones

2. Más fácil de depurar

String.ancestors>> [ String, Enumerable, Comparable, Object, Kernel ]

¿Y por qué?

1. Gracias al const_missing de Rails, no importa el orden en que carguen las definiciones

2. Más fácil de depurar

String.ancestors>> [ String, Enumerable, Comparable, Object, Kernel ]>> [ String, Italianization, Enumerable, Comparable, Object, Kernel ]

Recapitulando

1. Sé vago

1. Sé vago2. Pero no te pases de listo

1. Sé vago2. Pero no te pases de listo3. Y testea

¿Preguntas, dudas?

¿Preguntas, dudas?

¿Opiniones?

Referencias

"Metaprogramming Ruby: Domain-Specific Languages for Programmers", Glenn Vanderburg [www.vanderburg.org/Speaking/Stu!/oscon05.pdf]"The art of metaprogramming", Jonhathan Bartlett [http://www-128.ibm.com/developerworks/linux/library/l-metaprog1.html]C2.com Wiki [http://c2.com/cgi/wiki?MetaProgramming]http://api.rubyonrails.org/Ola Bini [http://ola-bini.blogspot.com/]Jay Fields [http://blog.jayfields.com/]Nic Williams [http://drnicwilliams.com/]Lambda the Ultimate [http://lambda-the-ultimate.org/]LiveValidation Plugin [http://livevalidation.rubyforge.org]Sofá Naranja [http://sofanaranja.com/2007/09/19/elogio-de-la-vagancia/]

top related