Metaprogramming 101Nando Vieira
codeplane.com.br
ode.c om.brhow   toc
O que veremosobject model, method dispatching,evaluation, hooks, DSLs.
selfsempre será o receiver padrão.
person.name!"#"$%"!
class User  attr_accessor :first_name, :last_name  def fullname    "#{first_name} #{last_name}"  endend
class User  attr_accessor :first_name, :last_name  def initialize(options = {})    options.each do |name, value|      send...
selfarmazena as variáveis de instância.
class User  attr_accessor :first_name, :last_nameenduser = User.newuser.first_name = "John"user.last_name = "Doe"user.inst...
variáveis de instânciaisso se aplica a todos os objetos.
class Config  @directory = "/some/directory"  @environment = :productionendConfig.instance_variables#=> ["@environment", "...
singleton_classmetaclasse, eigenclass, ghostclass.
class Config  class << self  endend
RUBY 1.9class Config  singleton_class.class do    # your code here  endend
RUBY 1.8class Object  unless Object.respond_to?(:singleton_class)    def singleton_class      class << self; self; end    ...
class Config  def self.root_dir    @root_dir  end  def self.root_dir=(dir)    @root_dir = dir  endend
class Config  class << self    attr_accessor :root_dir  endend
estendendo o selfadicionando novos métodos.
person = Object.newdef person.name  "John Doe"endperson.name#=> "John Doe"
person.singleton_methods#=> ["name"]
estendendo o selfadicionando novos métodos em classes.
class Config  def Config.root_dir    "/some/path"  endendConfig.root_dir#=> "/some/path"
Config.singleton_methods#=> ["root_dir"]
class Config  puts self == Configend#=> true
class Config  def Config.root_dir    "/some/path"  endendConfig.root_dir#=> "/some/path"
métodos de classeeles não existem no Ruby.
métodoslookup + dispatching.
method lookupup and right.
BasicObject    class << BasicObject    Object     class << Object    Module     class << Module     Class     class << Cla...
NoMethodError
method dispatchingexecução de métodos.
person.name
person.send :name
person.send :say, "hello"
person.__send__ :name
person.public_send :name
class Person  attr_accessor :name, :age, :email  def initialize(options = {})    options.each do |name, value|      send("...
evaluationclass_eval, instance_eval,instance_exec, eval.
class_evala.k.a. module_eval.
class Person; endPerson.class_eval do  puts self == Personend#=> true
class Person; endPerson.class_eval <<-RUBY  puts self == PersonRUBY#=> true
class Person; endPerson.class_eval do  def self.some_class_method  end  def some_instance_method  endend
class Person; endmodule Helpers  # some code hereendPerson.class_eval do  include Helpersend
class Person  class_eval <<-RUBY, __FILE__, __LINE__    def some_method       # some code    end  RUBYend
class Person  class_eval <<-RUBY, __FILE__, __LINE__    def some_method       raise "ffffuuuuuuuuuu"    end  RUBYendbegin ...
instance_evala.k.a. class_eval para instâncias.
Person = Class.newperson = Person.newPerson.respond_to?(:class_eval)      #=> truePerson.respond_to?(:instance_eval)   #=>...
instance_execa.k.a. instance_eval on redbull.
require "ostruct"john = OpenStruct.new(:name => "John Doe")block = proc do |time = nil|  puts name  puts timeendjohn.insta...
evalevaluation com contexto configurável.
def get_binding  name = "John Doe"  bindingendeval("defined?(name)")#=> nileval("defined?(name)", get_binding)#=> "local-v...
hooksinterceptando eventos do Ruby.
módulos & classesincluded, const_missing, extended,inherited, initialize_clone,initialize_copy, initialize_dup.
includedexecutado toda vez que um módulo éincluído.
module Ffffffuuu  def self.included(base)    base.class_eval do      include InstanceMethods      extend ClassMethods    e...
class Person  include FfffffuuuendPerson.singleton_class.included_modules#=> [Ffffffuuu::ClassMethods, Kernel]Person.inclu...
métodosmethod_added, method_missing,method_removed, method_undefined,singleton_method_added,singleton_method_removed,single...
method_missingexecutado toda vez que um método nãofor encontrado.
class Logger  attr_accessor :output  LEVELS = [    :debug, :info, :warn, :error, :critical  ]  def initialize(output)    @...
logger = Logger.new(STDOUT)logger.log :debug, "Fffffuuuuu"
class Logger  attr_accessor :output  LEVELS = [    :debug, :info, :warn, :error, :critical  ]  def initialize(output)    @...
def respond_to?(method, include_private = false)  return true if LEVELS.include?(method.to_sym)  superend
DSLsfuckyeah.
interfaces fluenteschaining.
@message = Message.new@message.to("john").from("mary").text("bring milk.")@message.deliver#=> "mary said: john, bring milk."
class Message  def to(name)    @to = name    self  end  def from(name)    @from = name    self  end  def text(message)    ...
module FluentAttribute  def fluent_attr(*names)    names.each do |name|      class_eval <<-RUBY, __FILE__, __LINE__       ...
class Message  extend FluentAttribute  fluent_attr :from, :to, :textend
dúvidas?
obrigadohttp://nandovieira.com.br.
Metaprogramming 101
Metaprogramming 101
Upcoming SlideShare
Loading in...5
×

Metaprogramming 101

2,924

Published on

Published in: Technology, Sports
0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,924
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
68
Comments
0
Likes
5
Embeds 0
No embeds

No notes for slide

Metaprogramming 101

  1. 1. Metaprogramming 101Nando Vieira
  2. 2. codeplane.com.br
  3. 3. ode.c om.brhow toc
  4. 4. O que veremosobject model, method dispatching,evaluation, hooks, DSLs.
  5. 5. selfsempre será o receiver padrão.
  6. 6. person.name!"#"$%"!
  7. 7. class User attr_accessor :first_name, :last_name def fullname "#{first_name} #{last_name}" endend
  8. 8. class User attr_accessor :first_name, :last_name def initialize(options = {}) options.each do |name, value| send("#{name}=", value) end endendUser.new({ :first_name => "John", :last_name => "Doe"})
  9. 9. selfarmazena as variáveis de instância.
  10. 10. class User attr_accessor :first_name, :last_nameenduser = User.newuser.first_name = "John"user.last_name = "Doe"user.instance_variables#=> ["@last_name", "@first_name"]
  11. 11. variáveis de instânciaisso se aplica a todos os objetos.
  12. 12. class Config @directory = "/some/directory" @environment = :productionendConfig.instance_variables#=> ["@environment", "@directory"]
  13. 13. singleton_classmetaclasse, eigenclass, ghostclass.
  14. 14. class Config class << self endend
  15. 15. RUBY 1.9class Config singleton_class.class do # your code here endend
  16. 16. RUBY 1.8class Object unless Object.respond_to?(:singleton_class) def singleton_class class << self; self; end end endend
  17. 17. class Config def self.root_dir @root_dir end def self.root_dir=(dir) @root_dir = dir endend
  18. 18. class Config class << self attr_accessor :root_dir endend
  19. 19. estendendo o selfadicionando novos métodos.
  20. 20. person = Object.newdef person.name "John Doe"endperson.name#=> "John Doe"
  21. 21. person.singleton_methods#=> ["name"]
  22. 22. estendendo o selfadicionando novos métodos em classes.
  23. 23. class Config def Config.root_dir "/some/path" endendConfig.root_dir#=> "/some/path"
  24. 24. Config.singleton_methods#=> ["root_dir"]
  25. 25. class Config puts self == Configend#=> true
  26. 26. class Config def Config.root_dir "/some/path" endendConfig.root_dir#=> "/some/path"
  27. 27. métodos de classeeles não existem no Ruby.
  28. 28. métodoslookup + dispatching.
  29. 29. method lookupup and right.
  30. 30. BasicObject class << BasicObject Object class << Object Module class << Module Class class << Class Person class << Person person class << person person.name
  31. 31. NoMethodError
  32. 32. method dispatchingexecução de métodos.
  33. 33. person.name
  34. 34. person.send :name
  35. 35. person.send :say, "hello"
  36. 36. person.__send__ :name
  37. 37. person.public_send :name
  38. 38. class Person attr_accessor :name, :age, :email def initialize(options = {}) options.each do |name, value| send("#{name}=", value) "#{name}=" end endend
  39. 39. evaluationclass_eval, instance_eval,instance_exec, eval.
  40. 40. class_evala.k.a. module_eval.
  41. 41. class Person; endPerson.class_eval do puts self == Personend#=> true
  42. 42. class Person; endPerson.class_eval <<-RUBY puts self == PersonRUBY#=> true
  43. 43. class Person; endPerson.class_eval do def self.some_class_method end def some_instance_method endend
  44. 44. class Person; endmodule Helpers # some code hereendPerson.class_eval do include Helpersend
  45. 45. class Person class_eval <<-RUBY, __FILE__, __LINE__ def some_method # some code end RUBYend
  46. 46. class Person class_eval <<-RUBY, __FILE__, __LINE__ def some_method raise "ffffuuuuuuuuuu" end RUBYendbegin Person.new.some_methodrescue Exception => error error.backtrace # ["person.rb:3:in `some_method", "person.rb:10"]end
  47. 47. instance_evala.k.a. class_eval para instâncias.
  48. 48. Person = Class.newperson = Person.newPerson.respond_to?(:class_eval) #=> truePerson.respond_to?(:instance_eval) #=> trueperson.respond_to?(:class_eval) #=> falseperson.respond_to?(:instance_eval) #=> true
  49. 49. instance_execa.k.a. instance_eval on redbull.
  50. 50. require "ostruct"john = OpenStruct.new(:name => "John Doe")block = proc do |time = nil| puts name puts timeendjohn.instance_eval(&block)#=> John Doe#=> #<OpenStruct name="John Doe">john.instance_exec(Time.now, &block)#=> John Doe#=> 2011-07-08 11:44:01 -0300
  51. 51. evalevaluation com contexto configurável.
  52. 52. def get_binding name = "John Doe" bindingendeval("defined?(name)")#=> nileval("defined?(name)", get_binding)#=> "local-variable"eval("name", get_binding)#=> "John Doe"
  53. 53. hooksinterceptando eventos do Ruby.
  54. 54. módulos & classesincluded, const_missing, extended,inherited, initialize_clone,initialize_copy, initialize_dup.
  55. 55. includedexecutado toda vez que um módulo éincluído.
  56. 56. module Ffffffuuu def self.included(base) base.class_eval do include InstanceMethods extend ClassMethods end end module InstanceMethods # some instance methods end module ClassMethods # some class methods endend
  57. 57. class Person include FfffffuuuendPerson.singleton_class.included_modules#=> [Ffffffuuu::ClassMethods, Kernel]Person.included_modules#=> [Ffffffuuu::InstanceMethods, Ffffffuuu, Kernel]
  58. 58. métodosmethod_added, method_missing,method_removed, method_undefined,singleton_method_added,singleton_method_removed,singleton_method_undefined
  59. 59. method_missingexecutado toda vez que um método nãofor encontrado.
  60. 60. class Logger attr_accessor :output LEVELS = [ :debug, :info, :warn, :error, :critical ] def initialize(output) @output = output end def log(level, message) output << "[#{level}] #{message}n" endend
  61. 61. logger = Logger.new(STDOUT)logger.log :debug, "Fffffuuuuu"
  62. 62. class Logger attr_accessor :output LEVELS = [ :debug, :info, :warn, :error, :critical ] def initialize(output) @output = output end def log(level, message) output << "[#{level}] #{message}n" end def method_missing(name, *args, &block) return send(:log, name, args.first) if LEVELS.include?(name) super endend
  63. 63. def respond_to?(method, include_private = false) return true if LEVELS.include?(method.to_sym) superend
  64. 64. DSLsfuckyeah.
  65. 65. interfaces fluenteschaining.
  66. 66. @message = Message.new@message.to("john").from("mary").text("bring milk.")@message.deliver#=> "mary said: john, bring milk."
  67. 67. class Message def to(name) @to = name self end def from(name) @from = name self end def text(message) @text = message self endend
  68. 68. module FluentAttribute def fluent_attr(*names) names.each do |name| class_eval <<-RUBY, __FILE__, __LINE__ def #{name}(value) # def to(value) @#{name} = value # @to = value self # self end # end RUBY end endend
  69. 69. class Message extend FluentAttribute fluent_attr :from, :to, :textend
  70. 70. dúvidas?
  71. 71. obrigadohttp://nandovieira.com.br.
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×