Last active
August 29, 2015 14:21
-
-
Save tiagopog/aa90d8b45cd2082f9974 to your computer and use it in GitHub Desktop.
Ruby - Metaprogramming
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Each object in Ruby also has its own metaclass, | |
# a Class that can have methods, but is only attached to the object itself. | |
matz = Object.new | |
def matz.speak | |
"Place your burden to machine's shoulders" | |
end | |
# What's going on here is that we're adding the speak method to matz's | |
# metaclass, and the matz object inherits from its metaclass and then Object. | |
# The reason this is somewhat less clear than ideal is that the metaclass | |
# is invisible in Ruby: | |
matz = Object.new | |
def matz.speak | |
"Place your burden to machine's shoulders" | |
end | |
matz.class #=> Object | |
# In fact, matz's "class" is its invisible metaclass. | |
# We can even get access to the metaclass: | |
metaclass = class << matz; self; end | |
metaclass.instance_methods.grep(/speak/) #=> ["speak"] | |
# Ruby provides a syntax for accessing an object's metaclass directly. | |
# By doing class << Person, we are setting self to Person's metaclass for the | |
# duration of the block. As a result, the species method is added to Person's | |
# metaclass, rather than the class itself: | |
class << Person | |
def species | |
"Homo Sapien" | |
end | |
self.name #=> "" | |
end | |
# Here, we combine several of the techniques. First, we open Person, making self equal | |
# to the Person class. Next, we do class << self, making self equal to Person's | |
# metaclass. When we then define the species method, it is defined | |
# on Person's metaclass: | |
class Person | |
class << self | |
def species | |
"Homo Sapien" | |
end | |
self.name #=> "" | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Person | |
attr_accessor :name | |
def initialize(name) | |
@name = name | |
end | |
end | |
class << Person | |
def race; 'Human' end | |
end | |
p = Person.new('Tiago') | |
puts p.name | |
def p.full_name | |
name + ' Guedes' | |
end | |
foobar = | |
class << p | |
def foobar | |
"Just a method named foobar" | |
end | |
end | |
puts foobar | |
puts foobar.class | |
puts p.full_name | |
puts Person.race | |
metaclass = class << p; self end | |
puts metaclass | |
puts metaclass.instance_methods.grep(/name/) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
foo = Object.new | |
class << foo | |
def name; 'Foo' end | |
end | |
puts foo.name | |
class << Object | |
def cool_name; 'Uga Buga' end | |
end | |
foobar = Object.new | |
puts foo.cool_name rescue nil | |
puts foobar.cool_name rescue nil | |
puts Object.cool_name |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MailTruck | |
# Generate getters and setters. | |
attr_accessor :driver, :route | |
def initialize(driver, route) | |
@driver, @route = driver, route | |
end | |
end | |
m = MailTruck.new('Foobar', ['12 Corrigan Way', '23 Antler Ave']) | |
puts m.class #=> MailTruck | |
# Define instance variables. | |
m.instance_variable_set('@speed', 45) | |
m.instance_variable_set('@driver', 'Tiago') | |
puts m.speed rescue nil #=> nil | |
puts m.driver #=> "Tiago" | |
# Evals and << | |
m.instance_eval do | |
def current_speed | |
"Current speed: #{@speed}" | |
end | |
end | |
puts m.current_speed #=> "Current speed: 45" | |
MailTruck.class_eval do | |
class << self | |
def foo; 'foo'; end | |
end | |
end | |
puts MailTruck.foo #=> "foo" | |
class << MailTruck | |
def bar; 'bar'; end | |
end | |
puts MailTruck.bar | |
# Object IDs | |
puts m.object_id #=> 70129925547360 | |
puts MailTruck.object_id #=> 70129925547660 | |
# Object and Class | |
o = Object.new | |
puts m.class #=> MailTruck | |
puts m.class.class #=> Class | |
puts m.class.superclass #=> Object | |
puts o.class #=> Object | |
puts Object.class #=> Class | |
puts Object.superclass #=> BasicObject | |
puts Object.superclass.superclass #=> nil | |
# Class | |
class Class | |
def awesome_method(*attrs) | |
print attrs | |
end | |
end | |
class MyNewClass | |
awesome_method :foo, :bar #=> [:foo, :bar] | |
end | |
class Class | |
def other_awesome_method(*attrs) | |
print attrs | |
end | |
end | |
class OtherNewClass | |
awesome_method :foo, :bar #=> [:foo, :bar] | |
other_awesome_method :lorem, :ipsum #=> [:lorem, :ipsum] | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Person | |
@@occupations = [] | |
attr_accessor :name, :last_name | |
def initialize(name, last_name = nil) | |
@name, @last_name = name, last_name | |
end | |
def full_name; "#{@name} #{@last_name}" end | |
class << self | |
def occupation(name) | |
@@occupations << name | |
end | |
def occupations; @@occupations end | |
end | |
end | |
p = Person.new('Foo', 'Bar') | |
puts p.full_name #=> Foo Bar | |
class Programmer < Person | |
occupation self.name | |
end | |
prog = Programmer.new('Tiago', 'Guedes') | |
puts prog.full_name | |
class Teacher < Person | |
occupation self.name | |
end | |
puts Person.occupations #=> "Programmer" | |
#=> "Teacher" | |
puts Programmer.occupations #=> "Programmer" | |
#=> "Teacher" | |
puts Teacher.occupations #=> "Programmer" | |
#=> "Teacher" | |
class Foo | |
occupation self.name rescue nil | |
end | |
puts Person.occupations #=> "Programmer" | |
#=> "Teacher" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Object | |
def metaclass | |
class << self; self; end | |
end | |
def meta_eval(&blk) | |
metaclass.instance_eval &blk | |
end | |
def meta_def(name, &blk) | |
meta_eval { define_method name, &blk } | |
end | |
end | |
class Person | |
class << self | |
@@occupations = [] | |
def occupation(name) | |
meta_def :occupation do; @@occupations << name end | |
end | |
end | |
end | |
class Programmer < Person | |
occupation 'Programmer' | |
end | |
# TODO: check why it did not work: | |
puts Person.metaclass.methods.grep(/occ/) | |
puts Programmer.metaclass.methods.grep(/occ/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Constructing a sample of DSL. | |
class Mailer | |
def self.deliver(&block) | |
mail = MailBuilder.new(&block).mail | |
mail.send_mail | |
end | |
Mail = Struct.new(:from, :to, :subject, :body) do | |
def send_mail | |
fib(30) | |
puts "Email from: #{from}" | |
puts "Email to : #{to}" | |
puts "Subject : #{subject}" | |
puts "Body : #{body}" | |
end | |
def fib(n) | |
n < 2 ? n : fib(n-1) + fib(n-2) | |
end | |
end | |
class MailBuilder | |
def initialize(&block) | |
@mail = Mail.new | |
instance_eval(&block) | |
end | |
attr_reader :mail | |
%w(from to subject body).each do |m| | |
define_method(m) do |val| | |
@mail.send("#{m}=", val) | |
end | |
end | |
end | |
end | |
Mailer.deliver do | |
from "[email protected]" | |
to "[email protected]" | |
subject "Threading and Forking" | |
body "Some content" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment