Transcript
Metaprogramming 101Nando Vieira
codeplane.com.br
howtocode.com.br
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("#{name}=", value) end endend
User.new({ :first_name => "John", :last_name => "Doe"})
selfarmazena as variáveis de instância.
class User attr_accessor :first_name, :last_nameend
user = User.newuser.first_name = "John"user.last_name = "Doe"
user.instance_variables#=> ["@last_name", "@first_name"]
variáveis de instânciaisso se aplica a todos os objetos.
class Config @directory = "/some/directory" @environment = :productionend
Config.instance_variables#=> ["@environment", "@directory"]
singleton_classmetaclasse, eigenclass, ghostclass.
class Config class << self endend
class Config singleton_class.class do # your code here endend
RUBY 1.9
class Object unless Object.respond_to?(:singleton_class) def singleton_class class << self; self; end end endend
RUBY 1.8
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.new
def person.name "John Doe"end
person.name#=> "John Doe"
person.singleton_methods#=> ["name"]
estendendo o selfadicionando novos métodos em classes.
class Config def Config.root_dir "/some/path" endend
Config.root_dir#=> "/some/path"
Config.singleton_methods#=> ["root_dir"]
class Config puts self == Configend
#=> true
class Config def Config.root_dir "/some/path" endend
Config.root_dir#=> "/some/path"
métodos de classeeles não existem no Ruby.
métodoslookup + dispatching.
method lookupup and right.
person.name
person class << person
Person class << Person
Class class << Class
Module class << Module
Object class << Object
BasicObject class << BasicObject
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("#{name}=", value) end endend
"#{name}="
evaluationclass_eval, instance_eval, instance_exec, eval.
class_evala.k.a. module_eval.
class Person; end
Person.class_eval do puts self == Personend
#=> true
class Person; end
Person.class_eval <<-RUBY puts self == PersonRUBY
#=> true
class Person; end
Person.class_eval do def self.some_class_method end def some_instance_method endend
class Person; end
module Helpers # some code hereend
Person.class_eval do include Helpersend
Person.class_eval do include Helpersend
class Person class_eval <<-RUBY, __FILE__, __LINE__ def some_method # some code end RUBYend
__FILE__, __LINE__
class Person class_eval <<-RUBY, __FILE__, __LINE__ def some_method raise "ffffuuuuuuuuuu" end RUBYend
begin Person.new.some_methodrescue Exception => error error.backtrace # ["person.rb:3:in `some_method'", "person.rb:10"]end # ["person.rb:3:in `some_method'", "person.rb:10"]
instance_evala.k.a. class_eval para instâncias.
Person = Class.newperson = Person.new
Person.respond_to?(:class_eval) #=> truePerson.respond_to?(:instance_eval) #=> true
person.respond_to?(:class_eval) #=> falseperson.respond_to?(:instance_eval) #=> true
instance_execa.k.a. instance_eval on redbull.
require "ostruct"
john = OpenStruct.new(:name => "John Doe")
block = proc do |time = nil| puts name puts timeend
john.instance_eval(&block)#=> John Doe#=> #<OpenStruct name="John Doe">
john.instance_exec(Time.now, &block)#=> John Doe#=> 2011-07-08 11:44:01 -0300
evalevaluation com contexto configurável.
def get_binding name = "John Doe" bindingend
eval("defined?(name)")#=> nil
eval("defined?(name)", get_binding)#=> "local-variable"
eval("name", get_binding)#=> "John Doe"
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 end end module InstanceMethods # some instance methods end module ClassMethods # some class methods endend
base.class_eval do include InstanceMethods extend ClassMethods end
class Person include Ffffffuuuend
Person.singleton_class.included_modules#=> [Ffffffuuu::ClassMethods, Kernel]
Person.included_modules#=> [Ffffffuuu::InstanceMethods, Ffffffuuu, Kernel]
métodosmethod_added, method_missing, method_removed, method_undefined, singleton_method_added, singleton_method_removed, singleton_method_undefined
method_missingexecutado toda vez que um método não for encontrado.
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
logger = Logger.new(STDOUT)logger.log :debug, "Fffffuuuuu"
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
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) @text = message self endend
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
class Message extend FluentAttribute fluent_attr :from, :to, :textend
dúvidas?
obrigadohttp://nandovieira.com.br.
top related