Ruby Metaprogramming

Post on 10-May-2015

3.220 views 0 download

Transcript of Ruby Metaprogramming

RUBY METAPROGRAMMING.

@fnando

AVISOS.

TUDO É OBJETO.INCLUINDO CLASSES.

MUITO CÓDIGO.

VARIÁVEIS DE CLASSE.

class MyLib @@name = "mylib" def self.name @@name endend

MyLib.name#=> “mylib”

class MyOtherLib < MyLib @@name = "myotherlib"end

MyOtherLib.name#=> “myotherlib”

MyLib.name#=> “myotherlib”

VARIÁVEIS DE CLASSE SÃO

COMPARTILHADAS.

VARIÁVEIS DE INSTÂNCIA.

class MyLib @name = "mylib" def self.name @name endend

MyLib.name#=> “mylib”

class MyOtherLib < MyLib @name = "myotherlib"end

MyOtherLib.name#=> “myotherlib”

MyLib.name#=> “mylib”

VARIÁVEIS DE INSTÂNCIA

PERTENCEMAO OBJETO.

METACLASSE.

class MyLib class << self endend

class MyLib # ruby 1.9.2+ singleton_class.class_eval do endend

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

class MyLib class << self attr_accessor :name endend

MyLib.name = "mylib"MyLib.name#=> mylib

BLOCOS.

MÉTODOS PODEM RECEBER BLOCOS.

def run(&block)end

BLOCOS PODEMSER EXECUTADOS.

def run(&block) yield arg1, arg2end

def run(&block) block.call(arg1, arg2)end

def run(&block) block[arg1, arg2]end

def run(&block) block.(arg1, arg2)end

# ruby 1.9+

METACLASSE, BLOCOS E

VARIÁVEL DE INSTÂNCIA.

MyLib.configure do |config| config.name = "mylib"end

class MyLib class << self attr_accessor :name end def self.configure(&block) yield self endend

EVALUATION.

eval, class_eval, e instance_eval

MyLib.class_eval <<-RUBY "running inside class"RUBY#=> “running inside class”

MyLib.class_eval do "running inside class"end#=> “running inside class”

handler = proc { self.kind_of?(MyLib)}

handler.call#=> false

handler = proc { self.kind_of?(MyLib)}

lib.instance_eval(&handler)#=> true

BLOCOS, METACLASSE, VARIÁVEIS DE

INSTÂNCIA, EVALUATION.

MyLib.configure do self.name = "mylib" name #=> “mylib”end

class MyLib class << self attr_accessor :name end def self.configure(&block) instance_eval(&block) endend

DEFINIÇÃO DE MÉTODOS.

MONKEY PATCHING.

class Integer def kbytes self * 1024 endend

128.kbytes#=> 131072

define_method.

MyLib.class_eval do define_method "name" do @name end

define_method "name=" do |name| @name = name endend

lib = MyLib.newlib.name = "mynewname"lib.name#=> “mynewname”

EVALUATION.

MyLib.class_eval <<-RUBY def self.name "mylib" end

def name "mylib's instance" endRUBY

MyLib.class_eval do def self.name "mylib" end def name "mylib's instance" endend

MyLib.name#=> “mylib”

MyLib.new.name#=> “mylib’s instance”

BLOCOS, EVALUATION,

DEFINIÇÃO DE MÉTODOS.

MyLib.class_eval do name "mylib" name #=> “mylib”end

class MyLib def self.accessor(method) class_eval <<-RUBY def self.#{method}(*args) if args.size.zero? @#{method} else @#{method} = args.last end end RUBY end accessor :nameend

MyLib.class_eval do name "mylib" name #=> “mylib”end

configure do name "mylib"

name #=> “mylib”end

def configure(&block) MyLib.instance_eval(&block)end

DISCLAIMER.

METHOD MISSING.

MyLib.new.invalid

NoMethodError: undefined method ‘invalid’ for #<MyLib:0x10017e2f0>

class MyLib NAMES = { :name => "mylib’s instance" }

def method_missing(method, *args) if NAMES.key?(method.to_sym) NAMES[method.to_sym] else super end endend

class MyLib #...

def respond_to?(method, include_private = false) if NAMES.key?(method.to_sym) true else super end endend

lib.name#=> “mylib’s instance”

lib.respond_to?(:name)#=> true

MIXINS.

class MyLib extend Accessor accessor :nameend

class MyOtherLib extend Accessor accessor :nameend

module Accessor def accessor(name) class_eval <<-RUBY def self.#{name}(*args) if args.size.zero? @#{name} else @#{name} = args.last end end RUBY endend

MONKEY PATCHING, MIXINS,

EVALUATION, DYNAMIC

DISPATCHING E HOOKS.

class Conference < ActiveRecord::Base has_permalinkend

"welcome to QConSP".to_permalink#=> “welcome-to-qconsqp”

class String def to_permalink self.downcase.gsub(/[^[a-z0-9]-]/, "-") endend

class Conference < ActiveRecord::Base before_validation :generate_permalink

def generate_permalink write_attribute :permalink, name.to_s.to_permalink endend

module Permalinkend

ActiveRecord::Base.send :include, Permalink

module Permalink def self.included(base) base.send :extend, ClassMethods endend

module Permalink # ... module ClassMethods def has_permalink class_eval do before_validation :generate_permalink include InstanceMethods end end endend

module Permalink # ... module InstanceMethods def generate_permalink write_attribute :permalink, name.to_s.to_permalink end endend

conf = Conference.create(:name => "QConSP 2010")conf.permalink#=> "qconsp-2010"

ENTÃO...

METAPROGRAMMING É

COMPLICADO.

MAS NEM TANTO.

APRENDA RUBY.

OBRIGADO.