Top Banner
Metaprogramming 101 Nando Vieira
73

Metaprogramming 101

May 08, 2015

Download

Technology

Nando Vieira
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
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.