Created
June 19, 2012 13:00
-
-
Save hawx/2954032 to your computer and use it in GitHub Desktop.
A Journey Into the Meta
This file contains hidden or 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
# 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