(en Ruby)
Metaprogramación
 Programas que escriben programas


              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,
    ...
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 quot;puts 'hola'quot;




$ ruby -e quot;`ruby hola.rb`quot;
Programas que escriben otros programas




  Programas que modifican su propio
          comportamiento
       (es decir, s...
“In Lisp, you don’t just write your
program down toward the language,
   you also build the language up
       toward your...
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
      •...
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
      •...
Técnicas en Ruby/Rails
Generadores
Generadores



class ModelGenerator < Rails::Generator::NamedBase
  def manifest
    record do |m|
      m.directory File....
method_missing
method_missing

class Array
  def method_missing(meth, *args, &blk)
    if meth.to_s =~ /^map_(.+)$/
      map {|i| i.send...
method_missing

class Array
  def method_missing(meth, *args, &blk)
    if meth.to_s =~ /^map_(.+)$/
      map {|i| i.send...
const_missing
const_missing


class Module
  alias :normal_const_missing :const_missing

  def const_missing(cname)
    return normal_co...
alias
alias

class String
  alias :largo :length
end

puts quot;holaquot;.largo
puts quot;holaquot;.length
alias

class String
  alias :largo :length
end

puts quot;holaquot;.largo                4
puts quot;holaquot;.length     ...
alias

class String
  alias :largo :length
end

puts quot;holaquot;.largo                     4
puts quot;holaquot;.length...
alias

class String
  alias :largo :length
end

puts quot;holaquot;.largo                     4
puts quot;holaquot;.length...
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_...
alias_method_chain
(el estándar de Rails para añadir funcionalidad a un método preexistente)



class String
  def length_...
send y define_method
la metaprogramación pata negra
send
send


str = quot;Metaprogramacionguequot;

puts str.upcase
puts str.send(:upcase)
send


str = quot;Metaprogramacionguequot;

puts str.upcase                      METAPROGRAMACIONGUE
puts str.send(:upcase...
send


str = quot;Metaprogramacionguequot;

puts str.upcase                                METAPROGRAMACIONGUE
puts str.se...
send


str = quot;Metaprogramacionguequot;

puts str.upcase                                METAPROGRAMACIONGUE
puts str.se...
define_method y def
define_method y def


class Prueba
  def foo
    quot;fooquot;
  end
  define_method(:bar) do
    quot;barquot;
  end
end
...
define_method y def


class Prueba
  def foo
    quot;fooquot;
  end
  define_method(:bar) do
    quot;barquot;
  end
end
...
¿¿Y entonces??
¿¿Y entonces??


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


class Prueba
  [ :foo, :bar, :jander, :klander ].each do |m|
    define_method(m) do
      m.to_s
    end...
Versión para realmente vagos


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

class Prueba
  M.each do |m|
    define_method(m) do...
Un ejemplo pequeño
(pero real) de algunas
 de estas cosas juntas
VALIDATION_METHODS = [:presence, :numericality, :format, :length, :acceptance, :confirmation]
VALIDATION_METHODS.each do |...
Un consejito
Usa módulos (mixins) para extender clases
Usa módulos (mixins) para extender clases




class String
  def italianize
    self.gsub(/[aeiou]/, 'i')
  end
end
Usa módulos (mixins) para extender clases




                                module Italianization
class String
         ...
¿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...
¿Y por qué?



           1. Gracias al const_missing de
              Rails, no importa el orden en
              que car...
¿Y por qué?



           1. Gracias al const_missing de
              Rails, no importa el orden en
              que car...
¿Y por qué?



           1. Gracias al const_missing de
              Rails, no importa el orden en
              que car...
Recapitulando
1. Sé vago
1. Sé vago
2. Pero no te pases de listo
1. Sé vago
2. Pero no te pases de listo
3. Y testea
¿Preguntas, dudas?
¿Preguntas, dudas?
   ¿Opiniones?
Referencias

quot;Metaprogramming Ruby: Domain-Specific Languages for Programmersquot;, Glenn Vanderburg [www.vanderburg.or...
Muchas gracias
   sergio.gil@the‐cocktail.com
     lacoctelera.com/porras
        the‐cocktail.com
Metaprogramación (en Ruby): programas que escriben programas
Metaprogramación (en Ruby): programas que escriben programas
Upcoming SlideShare
Loading in …5
×

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

5,106 views
4,957 views

Published on

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

Published in: Technology
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
5,106
On SlideShare
0
From Embeds
0
Number of Embeds
84
Actions
Shares
0
Downloads
120
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

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

  1. 1. (en Ruby) Metaprogramación Programas que escriben programas Sergio Gil
  2. 2. Sólo para vagos
  3. 3. “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
  4. 4. Programar no es una excepción
  5. 5. Automatización
  6. 6. Acercar el lenguaje al problema (para resolverlo mejor)
  7. 7. Qué es la metaprogramación y de dónde ha salido semejante cosa
  8. 8. puts quot;puts 'hola'quot; $ ruby -e quot;`ruby hola.rb`quot;
  9. 9. Programas que escriben otros programas Programas que modifican su propio comportamiento (es decir, se escriben a sí mismos)
  10. 10. “In Lisp, you don’t just write your program down toward the language, you also build the language up toward your program.” Paul Graham
  11. 11. Tipos de metaprogramación
  12. 12. Estática / Interna
  13. 13. Dinámica / Interna
  14. 14. Metaprogramación en Rails
  15. 15. Metaprogramación en Rails • Generadores
  16. 16. Metaprogramación en Rails • Generadores • Métodos mágicos
  17. 17. Metaprogramación en Rails • Generadores • Métodos mágicos • method_missing
  18. 18. Metaprogramación en Rails • Generadores • Métodos mágicos • method_missing • const_missing
  19. 19. Metaprogramación en Rails • Generadores • Métodos mágicos • method_missing • const_missing • Definiciones dinámicas
  20. 20. Metaprogramación en Rails • Generadores • Métodos mágicos • method_missing • const_missing • Definiciones dinámicas $ grep -r method_missing vendor/rails/ | wc -l 72
  21. 21. Metaprogramación en Rails • Generadores • Métodos mágicos • method_missing • const_missing • Definiciones dinámicas $ grep -r method_missingvendor/rails/ ||wc -l const_missing vendor/rails/ wc -l 41 72
  22. 22. Metaprogramación en Rails • Generadores • Métodos mágicos • method_missing • const_missing • Definiciones dinámicas $ grep -r method_missingvendor/rails/ ||wc -l define_method const_missing vendor/rails/ wc -l 67 41 72
  23. 23. Técnicas en Ruby/Rails
  24. 24. Generadores
  25. 25. 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, quot;#{file_name}.rbquot;) m.template 'unit_test.rb', File.join('test/unit', class_path, quot;#{file_name}_test.rbquot;) m.template 'fixtures.yml', File.join('test/fixtures', quot;#{table_name}.ymlquot;) m.migration_template 'migration.rb', 'db/migrate', :assigns => { :migration_name => quot;Create#{class_name.pluralize.gsub(/::/, '')}quot; }, :migration_file_name => quot;create_#{file_path.gsub(///, '_').pluralize}quot; end end end
  26. 26. method_missing
  27. 27. method_missing class Array def method_missing(meth, *args, &blk) if meth.to_s =~ /^map_(.+)$/ map {|i| i.send($1)} else super end end end (1..5).to_a.map_to_s
  28. 28. method_missing class Array def method_missing(meth, *args, &blk) if meth.to_s =~ /^map_(.+)$/ map {|i| i.send($1)} else super end end end (1..5).to_a.map_to_s >> [quot;1quot;, quot;2quot;, quot;3quot;, quot;4quot;, quot;5quot;]
  29. 29. const_missing
  30. 30. 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(quot;uninitialized constant #{cname}quot;) end klass = Class.new(ActiveRecord::Base) const_set cname, klass klass.set_table_name table_name klass end end
  31. 31. alias
  32. 32. alias class String alias :largo :length end puts quot;holaquot;.largo puts quot;holaquot;.length
  33. 33. alias class String alias :largo :length end puts quot;holaquot;.largo 4 puts quot;holaquot;.length 4
  34. 34. alias class String alias :largo :length end puts quot;holaquot;.largo 4 puts quot;holaquot;.length 4 class String alias :old_length :length def length old_length + 2 end end puts quot;holaquot;.length
  35. 35. alias class String alias :largo :length end puts quot;holaquot;.largo 4 puts quot;holaquot;.length 4 class String alias :old_length :length def length old_length + 2 end end puts quot;holaquot;.length 6
  36. 36. alias_method_chain (el estándar de Rails para añadir funcionalidad a un método preexistente)
  37. 37. alias_method_chain (el estándar de Rails para añadir funcionalidad a un método preexistente) class String def length_with_message puts quot;Calculando longitud de #{self}quot; length_without_message end alias_method_chain :length, :message end puts quot;holaquot;.length
  38. 38. alias_method_chain (el estándar de Rails para añadir funcionalidad a un método preexistente) class String def length_with_message puts quot;Calculando longitud de #{self}quot; length_without_message end alias_method_chain :length, :message end puts quot;holaquot;.length Calculando longitud de hola 4
  39. 39. send y define_method la metaprogramación pata negra
  40. 40. send
  41. 41. send str = quot;Metaprogramacionguequot; puts str.upcase puts str.send(:upcase)
  42. 42. send str = quot;Metaprogramacionguequot; puts str.upcase METAPROGRAMACIONGUE puts str.send(:upcase) METAPROGRAMACIONGUE
  43. 43. send str = quot;Metaprogramacionguequot; puts str.upcase METAPROGRAMACIONGUE puts str.send(:upcase) METAPROGRAMACIONGUE [ :upcase, :downcase, :reverse ].each do |m| puts str.send(m) end
  44. 44. send str = quot;Metaprogramacionguequot; puts str.upcase METAPROGRAMACIONGUE puts str.send(:upcase) METAPROGRAMACIONGUE [ :upcase, :downcase, :reverse ].each do |m| METAPROGRAMACIONGUE puts str.send(m) metaprogramaciongue end eugnoicamargorpateM
  45. 45. define_method y def
  46. 46. define_method y def class Prueba def foo quot;fooquot; end define_method(:bar) do quot;barquot; end end p = Prueba.new puts p.foo puts p.bar
  47. 47. define_method y def class Prueba def foo quot;fooquot; end define_method(:bar) do quot;barquot; end end p = Prueba.new puts p.foo foo puts p.bar bar
  48. 48. ¿¿Y entonces??
  49. 49. ¿¿Y entonces?? class Prueba [ :foo, :bar, :jander, :klander ].each do |m| define_method(m) do m.to_s end end end p = Prueba.new puts p.foo puts p.bar puts p.jander puts p.klander
  50. 50. ¿¿Y entonces?? class Prueba [ :foo, :bar, :jander, :klander ].each do |m| define_method(m) do m.to_s end end end p = Prueba.new foo puts p.foo bar puts p.bar jander puts p.jander klander puts p.klander
  51. 51. Versión para realmente vagos M = [ :foo, :bar, :jander, :klander ] class Prueba M.each do |m| define_method(m) do m.to_s end end end foo bar p = Prueba.new jander M.each do |m| klander puts p.send(m) end
  52. 52. Un ejemplo pequeño (pero real) de algunas de estas cosas juntas
  53. 53. VALIDATION_METHODS = [:presence, :numericality, :format, :length, :acceptance, :confirmation] VALIDATION_METHODS.each do |type| define_method quot;validates_#{type}_of_with_live_validationsquot;.to_sym do |*attr_names| send quot;validates_#{type}_of_without_live_validationsquot;.to_sym, *attr_names define_validations(type, attr_names) end alias_method_chain quot;validates_#{type}_ofquot;.to_sym, :live_validations end
  54. 54. Un consejito
  55. 55. Usa módulos (mixins) para extender clases
  56. 56. Usa módulos (mixins) para extender clases class String def italianize self.gsub(/[aeiou]/, 'i') end end
  57. 57. Usa módulos (mixins) para extender clases module Italianization class String def italianize def italianize self.gsub(/[aeiou]/, 'i') self.gsub(/[aeiou]/, 'i') end end end end String.send(:include, Italianization)
  58. 58. ¿Y por qué?
  59. 59. ¿Y por qué? 1. Gracias al const_missing de Rails, no importa el orden en que carguen las definiciones
  60. 60. ¿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
  61. 61. ¿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
  62. 62. ¿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 ]
  63. 63. ¿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 ]
  64. 64. Recapitulando
  65. 65. 1. Sé vago
  66. 66. 1. Sé vago 2. Pero no te pases de listo
  67. 67. 1. Sé vago 2. Pero no te pases de listo 3. Y testea
  68. 68. ¿Preguntas, dudas?
  69. 69. ¿Preguntas, dudas? ¿Opiniones?
  70. 70. Referencias quot;Metaprogramming Ruby: Domain-Specific Languages for Programmersquot;, Glenn Vanderburg [www.vanderburg.org/Speaking/Stu /oscon05.pdf] quot;The art of metaprogrammingquot;, 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/]
  71. 71. Muchas gracias sergio.gil@the‐cocktail.com lacoctelera.com/porras the‐cocktail.com

×