Skip to content

Instantly share code, notes, and snippets.

@hawx
Created June 19, 2012 13:00
Show Gist options
  • Save hawx/2954032 to your computer and use it in GitHub Desktop.
Save hawx/2954032 to your computer and use it in GitHub Desktop.
A Journey Into the Meta
# Ruby _is_ meta-programming. Well maybe not, but I read somewhere that you
# should always start with a strong statement, even if not exactly true, then
# clarify it later.
#
# Anyway, meta-programming is amazing. Let's start with some stupid recursive
# definition thing...
class SelfDefining
def define(&block)
self.class.send(:define_method, :define, &block)
end
end
s = SelfDefining.new
s.define {|&block| self.class.send(:define_method, :define, &block) }
s.define {|i| i * 5 }
puts s.define(10)
# Can you guess what's printed? Of course it prints 50. Lost? Let's take it a
# step at a time. SelfDefining creates a class with the method #define. This
# method takes a block and defines a new method called #define (overwriting the
# previous definition). I began by calling #define with a block which redefines
# the same method. Then I call #define again to define a method which multiplies
# it's argument by 5 before finally calling #define one last time with 10 as an
# argument.
#
# Stupid right? Who'd want to create a method which defines itself.
module Types
class Type
# More on this in a bit
def self.meta
class << self; self; end
end
def self.match(regex)
meta.send(:define_method, :match) {|test| !!regex.match(test) }
end
def self.convert(sym)
meta.send(:define_method, :convert, &sym)
end
end
class Integer < Type
match /\A\d+\Z/
convert :to_i
end
class Float < Type
match /\A\d+|\d*\.\d+\Z/
convert :to_f
end
end
i = Types::Integer
f = Types::Float
puts i.match 'not a number'
#=> false
puts i.match '123'
#=> true
puts i.convert '123'
#=> 123
puts f.convert '123.56'
#=> 123.56
# What was that! A semi-useful application of a method defining itself, is what.
# Now for the discussion of the .meta method:
#
# class << self; self; end
#
# What this is actually doing is opening the singleton class of self. self in the
# class refers to the classes name, here Type. Now maybe you're thinking does it
# have to be self, what about [insert object] instead? Let's take a look,
my_string = 'Hey guys'
class << my_string
def crazy
split(' ').join(' crazy ')
end
end
puts my_string.crazy
#=> 'Hey crazy guys'
# There are rumours that some old skool rubyists use this style most of the time,
# only occasionally turning to creating classes. This is __Object__ Oriented
# Programming at its purist, with none of those distracting class things.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment