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

73
Metaprogramación Programas que escriben programas (en Ruby) Sergio Gil

Upload: sergio-gil

Post on 06-Jul-2015

4.635 views

Category:

Technology


3 download

DESCRIPTION

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

TRANSCRIPT

Page 1: Metaprogramación (en Ruby): programas que escriben programas

MetaprogramaciónProgramas que escriben programas

(en Ruby)

Sergio Gil

Page 2: Metaprogramación (en Ruby): programas que escriben programas

Sólo para vagos

Page 3: Metaprogramación (en Ruby): programas que escriben programas

“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

Page 4: Metaprogramación (en Ruby): programas que escriben programas

Programar no es una excepción

Page 5: Metaprogramación (en Ruby): programas que escriben programas

Automatización

Page 6: Metaprogramación (en Ruby): programas que escriben programas

Acercar el lenguaje al problema(para resolverlo mejor)

Page 7: Metaprogramación (en Ruby): programas que escriben programas

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

Page 8: Metaprogramación (en Ruby): programas que escriben programas

puts "puts 'hola'"

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

Page 9: Metaprogramación (en Ruby): programas que escriben programas

Programas que escriben otros programas

Programas que modifican su propio comportamiento

(es decir, se escriben a sí mismos)

Page 10: Metaprogramación (en Ruby): programas que escriben programas

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

you also build the language up toward your program.”

Paul Graham

Page 11: Metaprogramación (en Ruby): programas que escriben programas

Tipos de metaprogramación

Page 12: Metaprogramación (en Ruby): programas que escriben programas

Estática / Interna

Page 13: Metaprogramación (en Ruby): programas que escriben programas

Dinámica / Interna

Page 14: Metaprogramación (en Ruby): programas que escriben programas

Metaprogramación en Rails

Page 15: Metaprogramación (en Ruby): programas que escriben programas

Metaprogramación en Rails

• Generadores

Page 16: Metaprogramación (en Ruby): programas que escriben programas

Metaprogramación en Rails

• Generadores

• Métodos mágicos

Page 17: Metaprogramación (en Ruby): programas que escriben programas

Metaprogramación en Rails

• Generadores

• Métodos mágicos

• method_missing

Page 18: Metaprogramación (en Ruby): programas que escriben programas

Metaprogramación en Rails

• Generadores

• Métodos mágicos

• method_missing

• const_missing

Page 19: Metaprogramación (en Ruby): programas que escriben programas

Metaprogramación en Rails

• Generadores

• Métodos mágicos

• method_missing

• const_missing

• Definiciones dinámicas

Page 20: Metaprogramación (en Ruby): programas que escriben programas

Metaprogramación en Rails

• Generadores

• Métodos mágicos

• method_missing

• const_missing

• Definiciones dinámicas

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

Page 21: Metaprogramación (en Ruby): programas que escriben programas

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

Page 22: Metaprogramación (en Ruby): programas que escriben programas

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

Page 23: Metaprogramación (en Ruby): programas que escriben programas

Técnicas en Ruby/Rails

Page 24: Metaprogramación (en Ruby): programas que escriben programas

Generadores

Page 25: Metaprogramación (en Ruby): programas que escriben programas

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

Page 26: Metaprogramación (en Ruby): programas que escriben programas

method_missing

Page 27: Metaprogramación (en Ruby): programas que escriben programas

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

Page 28: Metaprogramación (en Ruby): programas que escriben programas

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"]

Page 29: Metaprogramación (en Ruby): programas que escriben programas

const_missing

Page 30: Metaprogramación (en Ruby): programas que escriben programas

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

Page 31: Metaprogramación (en Ruby): programas que escriben programas

alias

Page 32: Metaprogramación (en Ruby): programas que escriben programas

alias

class String alias :largo :lengthend

puts "hola".largoputs "hola".length

Page 33: Metaprogramación (en Ruby): programas que escriben programas

alias

class String alias :largo :lengthend

puts "hola".largoputs "hola".length

44

Page 34: Metaprogramación (en Ruby): programas que escriben programas

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

Page 35: Metaprogramación (en Ruby): programas que escriben programas

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

Page 36: Metaprogramación (en Ruby): programas que escriben programas

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

Page 37: Metaprogramación (en Ruby): programas que escriben programas

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

Page 38: Metaprogramación (en Ruby): programas que escriben programas

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

Page 39: Metaprogramación (en Ruby): programas que escriben programas

send y define_methodla metaprogramación pata negra

Page 40: Metaprogramación (en Ruby): programas que escriben programas

send

Page 41: Metaprogramación (en Ruby): programas que escriben programas

str = "Metaprogramaciongue"

puts str.upcaseputs str.send(:upcase)

send

Page 42: Metaprogramación (en Ruby): programas que escriben programas

str = "Metaprogramaciongue"

puts str.upcaseputs str.send(:upcase)

METAPROGRAMACIONGUEMETAPROGRAMACIONGUE

send

Page 43: Metaprogramación (en Ruby): programas que escriben programas

str = "Metaprogramaciongue"

puts str.upcaseputs str.send(:upcase)

METAPROGRAMACIONGUEMETAPROGRAMACIONGUE

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

send

Page 44: Metaprogramación (en Ruby): programas que escriben programas

str = "Metaprogramaciongue"

puts str.upcaseputs str.send(:upcase)

METAPROGRAMACIONGUEMETAPROGRAMACIONGUE

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

METAPROGRAMACIONGUEmetaprogramaciongueeugnoicamargorpateM

send

Page 45: Metaprogramación (en Ruby): programas que escriben programas

define_method y def

Page 46: Metaprogramación (en Ruby): programas que escriben programas

define_method y def

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

p = Prueba.newputs p.fooputs p.bar

Page 47: Metaprogramación (en Ruby): programas que escriben programas

define_method y def

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

p = Prueba.newputs p.fooputs p.bar

foobar

Page 48: Metaprogramación (en Ruby): programas que escriben programas

¿¿Y entonces??

Page 49: Metaprogramación (en Ruby): programas que escriben programas

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??

Page 50: Metaprogramación (en Ruby): programas que escriben programas

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??

Page 51: Metaprogramación (en Ruby): programas que escriben programas

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

Page 52: Metaprogramación (en Ruby): programas que escriben programas

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

Page 53: Metaprogramación (en Ruby): programas que escriben programas

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

Page 54: Metaprogramación (en Ruby): programas que escriben programas

Un consejito

Page 55: Metaprogramación (en Ruby): programas que escriben programas

Usa módulos (mixins) para extender clases

Page 56: Metaprogramación (en Ruby): programas que escriben programas

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

Usa módulos (mixins) para extender clases

Page 57: Metaprogramación (en Ruby): programas que escriben programas

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

Page 58: Metaprogramación (en Ruby): programas que escriben programas

¿Y por qué?

Page 59: Metaprogramación (en Ruby): programas que escriben programas

¿Y por qué?

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

Page 60: Metaprogramación (en Ruby): programas que escriben programas

¿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

Page 61: Metaprogramación (en Ruby): programas que escriben programas

¿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

Page 62: Metaprogramación (en Ruby): programas que escriben programas

¿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 ]

Page 63: Metaprogramación (en Ruby): programas que escriben programas

¿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 ]

Page 64: Metaprogramación (en Ruby): programas que escriben programas

Recapitulando

Page 65: Metaprogramación (en Ruby): programas que escriben programas
Page 66: Metaprogramación (en Ruby): programas que escriben programas

1. Sé vago

Page 67: Metaprogramación (en Ruby): programas que escriben programas

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

Page 68: Metaprogramación (en Ruby): programas que escriben programas

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

Page 69: Metaprogramación (en Ruby): programas que escriben programas
Page 70: Metaprogramación (en Ruby): programas que escriben programas

¿Preguntas, dudas?

Page 71: Metaprogramación (en Ruby): programas que escriben programas

¿Preguntas, dudas?

¿Opiniones?

Page 72: Metaprogramación (en Ruby): programas que escriben programas

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/]