metaprogramación (en ruby): programas que escriben programas
Post on 06-Jul-2015
4.643 Views
Preview:
DESCRIPTION
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/]
Muchas graciassergio.gil@the‐cocktail.com
lacoctelera.com/porras
the‐cocktail.com
top related