Object Model and
Metaprogramming in
Ruby
Monday, November 1, 2010
Santiago Pastorino
@spastorino
Monday, November 1, 2010
Monday, November 1, 2010
Monday, November 1, 2010
Monday, November 1, 2010
Monday, November 1, 2010
Monday, November 1, 2010
Monday, November 1, 2010
Monday, November 1, 2010
Ruby
Rails
Monday, November 1, 2010
Java get/set version 1
public class User {
private String name;
public String getName() {
   return name;
   }
   public void setName(String name) {
   this.name = name;
}
}
Monday, November 1, 2010
.NET get/set version 1
class User
{
private string name;
public string Name
{
    get { return name; }
    set { name = value; }
}
}
Monday, November 1, 2010
Ruby get/set version 1
class User
def name
@name
end
def name=(value)
@name = value
end
end
Monday, November 1, 2010
Java get/set version 2
?public property String name;
Monday, November 1, 2010
.NET get/set version 2
class User
{
public string Name { get; set; }
}
Monday, November 1, 2010
Ruby get/set version 2
The right way
class User
attr_accessor :name
end
Monday, November 1, 2010
Metaprogramming
Monday, November 1, 2010
Metaprogramming
“Metaprogramming is writing code that
writes code.”
Monday, November 1, 2010
Metaprogramming
“Metaprogramming is writing code that
writes code.”
Monday, November 1, 2010
Metaprogramming
“Metaprogramming is writing code that
writes code.”
“Metaprogramming is writing code that
manipulates language constructs at runtime.”
Monday, November 1, 2010
Metaprogramming
Monday, November 1, 2010
Metaprogramming
• Classes are always open
• Everything is an Object even classes and
modules
• All methods calls have a receiver
Monday, November 1, 2010
Classes are Open
class Hash
def except!(*keys)
keys.each { |key| delete(key) }
self
end
end
hsh = {:a => 1, :b => 2}
hsh.except!(:a) # => {:b => 2}
Monday, November 1, 2010
Everything is an Object
class User
puts self # => User
puts self.class # => Class
end
User.methods.grep /get/
=> [:const_get,
:class_variable_get,
:instance_variable_get]
Monday, November 1, 2010
All method calls have a
receiver
class User
attr_accessor :name
end
Monday, November 1, 2010
Metaprogramming
•class_eval
• define_method
• method_missing
Monday, November 1, 2010
attr_reader (class_eval)
class User
def initialize(name)
@name = name
end
attr_reader :name
end
u = User.new(‘Santiago’)
u.name # => ‘Santiago’
Monday, November 1, 2010
attr_reader (class_eval)
class Module
def attr_reader(sym)
class_eval “def #{sym}; @#{sym}; end”
end
end
class User
attr_reader :name # def name; @name; end
end
Monday, November 1, 2010
attr_reader (class_eval)
class Module
private
def attr_reader(sym)
class_eval <<-READER, __FILE__, __LINE__ + 1
def #{sym}
@#{sym}
end
READER
end
end
Monday, November 1, 2010
attr_writer (class_eval)
class Module
private
def attr_writer(sym)
class_eval <<-WRITER, __FILE__, __LINE__ + 1
def #{sym}=(value)
@#{sym} = value
end
WRITER
end
end
Monday, November 1, 2010
attr_accessor
class Module
private
def attr_accessor(*syms)
attr_reader(syms)
attr_writer(syms)
end
end
Monday, November 1, 2010
Metaprogramming
• class_eval
•define_method
• method_missing
Monday, November 1, 2010
define_method
class User
attr_accessor_with_default :age, 22
end
user = User.new
user.age # => 22
user.age = 26
user.age # => 26
Monday, November 1, 2010
define_method
class Module
def attr_accessor_with_default(sym, default)
class_eval <<-READER, __FILE__, __LINE__ + 1
def #{sym}
#{default}
end
# + something else
READER
end
end
Monday, November 1, 2010
define_method
class User
attr_accessor_with_default :age, 22
end
user = User.new
user.age # => 22
Monday, November 1, 2010
define_method
class USer
attr_accessor_with_default :complex_obj,
Struct.new(:x, :y)
end
user = User.new
user.complex_obj # => nil
Monday, November 1, 2010
define_method
22.to_s # => “22”
Struct.new(:x, :y).to_s # => "#<Class:0x00000100966210>"
Monday, November 1, 2010
define_method
class Module
def attr_accessor_with_default(sym, default)
class_eval <<-READER, __FILE__, __LINE__ + 1
def #{sym}
default
end
# + something else
READER
end
end
Monday, November 1, 2010
define_method
class User
attr_accessor_with_default :complex_obj,
Struct.new(:x, :y)
end
user = User.new
user.complex_obj
NameError: undefined local variable or method
`default' for #<User:0x00000100978050>
Monday, November 1, 2010
define_method
class Module
def attr_accessor_with_default(sym, default)
define_method(sym, Proc.new { default })
end
end
Monday, November 1, 2010
define_method
class Module
def attr_accessor_with_default(sym, default)
define_method(sym, Proc.new { default })
class_eval(<<-EVAL, __FILE__, __LINE__ + 1)
def #{sym}=(value)
class << self; attr_accessor :#{sym} end
@#{sym} = value
end
EVAL
end
end
Monday, November 1, 2010
define_method
class Person
attr_accessor_with_default :complex_obj,
Struct.new(:x, :y)
end
person = Person.new
person.complex_obj # => #<Class:0x00000101045c70>
person.complex_obj = Struct.new(:a, :b)
person.complex_obj # => #<Class:0x000001009be9d8>
Monday, November 1, 2010
Metaprogramming
• class_eval
• define_method
•method_missing
Monday, November 1, 2010
Method Lookup
BasicObject
Object
XMLx
KernelKernel
Monday, November 1, 2010
method_missing
(An internal DSL)
XML.generate(STDOUT) do
html do
head do
title { ‘pagetitle’ }
comment ‘This is a test’
end
body do
h1 style: ‘font-family: sans-serif‘ do
‘pagetitle’
end
ul id: ‘info’ do
li { Time.now }
li { RUBY_VERSION }
end
end
end
Monday, November 1, 2010
method_missing
(An internal DSL)
<html>
<head>
<title>pagetitle</title>
<!-- This is a test -->
</head>
<body>
<h1 style=”font-family: sans-serif”>
pagetitle
</h1>
<ul id=”info”>
<li>2010-10-30 17:39:45 -0200</li>
<li>1.9.2</li>
</ul>
</body>
</html>
Monday, November 1, 2010
method_missing
(An internal DSL)
class XML
def initialize(out)
@out = out
end
def content(text)
@out << text.to_s
end
def comment(text)
@out << “<!-- #{text} -->”
end
end
Monday, November 1, 2010
method_missing
(An internal DSL)
def method_missing(tagname, attributes={})
@out << “<#{tagname}”
attributes.each { |attr, value|
@out << “#{attr}=’#{value}’”
}
if block_given?
@out << ‘>’
content = yield
if content
@out << content.to_s
end
@out << “</#{tagname}>”
else
@out << ‘/>’
end
end
Monday, November 1, 2010
method_missing
(An internal DSL)
class XML
def self.generate(out, &block)
XML.new(out).instance_eval(&block)
end
end
XML.generate(STDOUT) do
# code
end
Monday, November 1, 2010
method_missing
(An internal DSL)
XML.generate(STDOUT) do
html do
head do
title { ‘pagetitle’ }
comment ‘This is a test’
end
body do
h1 style: ‘font-family: sans-serif‘ do
‘pagetitle’
end
ul id: ‘info’ do
li { Time.now }
li { RUBY_VERSION }
end
end
end
Monday, November 1, 2010
It’s all about the self
Monday, November 1, 2010
Singleton Method
d = “9/5/1982”
def d.to_date
# code here
end
# or
class << d
def to_date
# code here
end
end
Monday, November 1, 2010
Object Model
BasicObject
Object
String
(d)d
KernelKernel
Monday, November 1, 2010
Class Methods?
class String
def self.to_date
# code here
end
# or
class << self
def to_date
# code here
end
end
end
Monday, November 1, 2010
The same
d = “9/5/1982”
def d.to_date
# code here
end
# or
class << d
def to_date
# code here
end
end
Monday, November 1, 2010
Object Model
(BasicObject)BasicObject
Object (Object)
String (String)
Module
Class
Monday, November 1, 2010
Everything about self
class User
p self # => User
class << self
p self # => <Class:User>
end
end
Monday, November 1, 2010
Real life use cases for
fibers?
require 'fiber'
module Eigenclass
  def eigenclass
    class << self; self end
  end
  module_function :eigenclass
  public :eigenclass
end
class Object
  include Eigenclass
end
Monday, November 1, 2010
Real life use cases for
fibers?
class EigenclassesGenerator
  def initialize(obj)
    @eigenclasses = [obj.eigenclass]
    @fiber = Fiber.new do
      loop do
        Fiber.yield @eigenclasses.last
        @eigenclasses[@eigenclasses.length] = @eigenclasses.last.eigenclass
      end
    end
  end
  def [](index)
if index >= @eigenclasses.length
(index - @eigenclasses.length + 2).times { @fiber.resume }
end
    @eigenclasses[index]
  end
end
Monday, November 1, 2010
Rails Magic?
class User < ActiveRecord::Base
belongs_to :bill_address
has_one :role
has_many :orders
validates_presence_of :name, :country
validates_acceptance_of :terms
validates_uniqueness_of :name
end
Monday, November 1, 2010
Questions?
Monday, November 1, 2010
Thank you!
Monday, November 1, 2010

Metaprogramming