metaprogramming 101

73
Metaprogramming 101 Nando Vieira

Upload: nando-vieira

Post on 08-May-2015

2.862 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Metaprogramming 101

Metaprogramming 101Nando Vieira

Page 2: Metaprogramming 101
Page 3: Metaprogramming 101

codeplane.com.br

Page 4: Metaprogramming 101

howtocode.com.br

Page 5: Metaprogramming 101

O que veremosobject model, method dispatching, evaluation, hooks, DSLs.

Page 6: Metaprogramming 101

selfsempre será o receiver padrão.

Page 7: Metaprogramming 101

person.name

!"#"$%"!

Page 8: Metaprogramming 101

class User attr_accessor :first_name, :last_name def fullname "#{first_name} #{last_name}" endend

Page 9: Metaprogramming 101

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"})

Page 10: Metaprogramming 101

selfarmazena as variáveis de instância.

Page 11: Metaprogramming 101

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

Page 12: Metaprogramming 101

variáveis de instânciaisso se aplica a todos os objetos.

Page 13: Metaprogramming 101

class Config @directory = "/some/directory" @environment = :productionend

Config.instance_variables#=> ["@environment", "@directory"]

Page 14: Metaprogramming 101

singleton_classmetaclasse, eigenclass, ghostclass.

Page 15: Metaprogramming 101

class Config class << self endend

Page 16: Metaprogramming 101

class Config singleton_class.class do # your code here endend

RUBY 1.9

Page 17: Metaprogramming 101

class Object unless Object.respond_to?(:singleton_class) def singleton_class class << self; self; end end endend

RUBY 1.8

Page 18: Metaprogramming 101

class Config def self.root_dir @root_dir end def self.root_dir=(dir) @root_dir = dir endend

Page 19: Metaprogramming 101

class Config class << self attr_accessor :root_dir endend

Page 20: Metaprogramming 101

estendendo o selfadicionando novos métodos.

Page 21: Metaprogramming 101

person = Object.new

def person.name "John Doe"end

person.name#=> "John Doe"

Page 22: Metaprogramming 101

person.singleton_methods#=> ["name"]

Page 23: Metaprogramming 101

estendendo o selfadicionando novos métodos em classes.

Page 24: Metaprogramming 101

class Config def Config.root_dir "/some/path" endend

Config.root_dir#=> "/some/path"

Page 25: Metaprogramming 101

Config.singleton_methods#=> ["root_dir"]

Page 26: Metaprogramming 101

class Config puts self == Configend

#=> true

Page 27: Metaprogramming 101

class Config def Config.root_dir "/some/path" endend

Config.root_dir#=> "/some/path"

Page 28: Metaprogramming 101

métodos de classeeles não existem no Ruby.

Page 29: Metaprogramming 101

métodoslookup + dispatching.

Page 30: Metaprogramming 101

method lookupup and right.

Page 31: Metaprogramming 101

person.name

person class << person

Person class << Person

Class class << Class

Module class << Module

Object class << Object

BasicObject class << BasicObject

Page 32: Metaprogramming 101

NoMethodError

Page 33: Metaprogramming 101

method dispatchingexecução de métodos.

Page 34: Metaprogramming 101

person.name

Page 35: Metaprogramming 101

person.send :name

Page 36: Metaprogramming 101

person.send :say, "hello"

Page 37: Metaprogramming 101

person.__send__ :name

Page 38: Metaprogramming 101

person.public_send :name

Page 39: Metaprogramming 101

class Person attr_accessor :name, :age, :email def initialize(options = {}) options.each do |name, value| send("#{name}=", value) end endend

"#{name}="

Page 40: Metaprogramming 101

evaluationclass_eval, instance_eval, instance_exec, eval.

Page 41: Metaprogramming 101

class_evala.k.a. module_eval.

Page 42: Metaprogramming 101

class Person; end

Person.class_eval do puts self == Personend

#=> true

Page 43: Metaprogramming 101

class Person; end

Person.class_eval <<-RUBY puts self == PersonRUBY

#=> true

Page 44: Metaprogramming 101

class Person; end

Person.class_eval do def self.some_class_method end def some_instance_method endend

Page 45: Metaprogramming 101

class Person; end

module Helpers # some code hereend

Person.class_eval do include Helpersend

Person.class_eval do include Helpersend

Page 46: Metaprogramming 101

class Person class_eval <<-RUBY, __FILE__, __LINE__ def some_method # some code end RUBYend

__FILE__, __LINE__

Page 47: Metaprogramming 101

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

Page 48: Metaprogramming 101

instance_evala.k.a. class_eval para instâncias.

Page 49: Metaprogramming 101

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

Page 50: Metaprogramming 101

instance_execa.k.a. instance_eval on redbull.

Page 51: Metaprogramming 101

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

Page 52: Metaprogramming 101

evalevaluation com contexto configurável.

Page 53: Metaprogramming 101

def get_binding name = "John Doe" bindingend

eval("defined?(name)")#=> nil

eval("defined?(name)", get_binding)#=> "local-variable"

eval("name", get_binding)#=> "John Doe"

Page 54: Metaprogramming 101

hooksinterceptando eventos do Ruby.

Page 55: Metaprogramming 101

módulos & classesincluded, const_missing, extended, inherited, initialize_clone, initialize_copy, initialize_dup.

Page 56: Metaprogramming 101

includedexecutado toda vez que um módulo é incluído.

Page 57: Metaprogramming 101

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

Page 58: Metaprogramming 101

class Person include Ffffffuuuend

Person.singleton_class.included_modules#=> [Ffffffuuu::ClassMethods, Kernel]

Person.included_modules#=> [Ffffffuuu::InstanceMethods, Ffffffuuu, Kernel]

Page 59: Metaprogramming 101

métodosmethod_added, method_missing, method_removed, method_undefined, singleton_method_added, singleton_method_removed, singleton_method_undefined

Page 60: Metaprogramming 101

method_missingexecutado toda vez que um método não for encontrado.

Page 61: Metaprogramming 101

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

Page 62: Metaprogramming 101

logger = Logger.new(STDOUT)logger.log :debug, "Fffffuuuuu"

Page 63: Metaprogramming 101

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

Page 64: Metaprogramming 101

def respond_to?(method, include_private = false) return true if LEVELS.include?(method.to_sym) superend

Page 65: Metaprogramming 101

DSLsfuckyeah.

Page 66: Metaprogramming 101

interfaces fluenteschaining.

Page 67: Metaprogramming 101

@message = [email protected]("john").from("mary").text("bring milk.")@message.deliver#=> "mary said: john, bring milk."

Page 68: Metaprogramming 101

class Message def to(name) @to = name self end

def from(name) @from = name self end

def text(message) @text = message self endend

Page 69: Metaprogramming 101
Page 70: Metaprogramming 101

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

Page 71: Metaprogramming 101

class Message extend FluentAttribute fluent_attr :from, :to, :textend

Page 72: Metaprogramming 101

dúvidas?

Page 73: Metaprogramming 101

obrigadohttp://nandovieira.com.br.