
(from Greek: μετά = "after", "beyond", "with", "adjacent", "self")
0> class Test
1> puts "Hello", self, self.class
1> end
Hello # printed when the class is defined
Test # Inside the class definition, self is the class that is being defined
Class # It is an object of class Class
x = "C++"
define x.to_s # Overrides String#to_s just for this object
"Ruby-=1"
end
method_missing
class Roman
def self.method_missing(name, *args)
r = name.to_s
r.gsub!("IV" , "IIII")
r.gsub!("IX" , "VIIII")
r.gsub!("XL" , "XXXX")
r.gsub!("XC" , "LXXXX")
return r.count("I" ) + r.count("V" ) * 5 + r.count("X" ) * 10 +
r.count("L" ) * 50 + r.count("C" ) * 100
end
end
Roman.LXXIV # 74
*args is the variable-length array of the argumentsmethod_missing is attached to self
(i.e. the Roman class)
That way, we intercept static methods.
xb.Hello("World!", "type" => "global")
<Hello type="global">World!</Hello>
Class.new creates a new class with no nameObject.const_set to give it a name
klass = Class.new # Can't call it class—that's a keyword
Object.const_set("City", klass)
klass.attr_accessor("name", "country")
Rats—that's a private method.
class Base
def self.make_fields(fields)
attr_accessor *fields // *fields turns the array into an argument list
end
end
klass = Class.new(Base) // Argument is superclass of new class klass.make_fields(["name", "country"]) x = klass.new x.name = "Ho Chi Minh City"
define_method to define a method in a class
class Base
def self.make_fields(fields)
attr_accessor *fields
define_method(:initialize) { |*values|
fields.each_with_index { |f,i|
instance_variable_set("@"+f, values[i])
}
}
end
end
Object#instance_variable_set sets an arbitrary fielddef initialize(name, country) @name = name @country = country end
make_class(name, fields) that
makes a basic class out of a list of fields.name,country Ho Chi Minh City,Vietnam Washington,United States
class City < ActiveRecord::Base
class CSVBase
def self.inherited(klass)
# Read first row of CSV file and add fields to klass
end
end

You'll need to know how to read data from a file in Ruby. It's simple. Just make a file City.csv as indicated in the lecture slides, run this code, and see what it does.
filename = "City.csv"
data = File.new(filename)
header = data.gets.chomp
fields = header.split(",")
data.each { |line|
puts line.chomp.split(",")
}
data.close
CSVRecord that can be used as follows:
r = CSVRecord.new("City", 1)
puts r.name # Prints Washington
In the constructor, read the first line from the file with the given name and extension .CSV. Stash away the headers in an instance field. Then read the given record (or, for simplicity, just the first one) into another instance field.
In method_missing, find out the matching field and return
the corresponding value. (Remember not to define it on self in
this case.)
What is your code?
r.name = "Saigon"
Hint: Just print out the method name in method_missing.
You don't have to implement this—just tell me what will work.
irb. Run
require "rubygems" require "builder" xb = Builder::XmlMarkup.new(:target => $stdout, :indent => 1) xb.Hello "World!"
What output do you get?
Hello? A field? A method? A parameter? A variable? A
class? Something else? "World"? Don't say "a string"—we know that.
How is it related to xb.Hello?xb.fred("Wilma"). Clearly the designers of the XML
Builder didn't know or care about Fred. How is this implemented?xb.Hello "World!", "type" => "global", "state" => "happy"
What do you get?
xb.date {
xb.year "2006"
xb.month "01"
xb.day "01"
}
What output do you get?
{...}? How does the
date method process it?xb.values)
<values> <value>1</value> <value>2</value> <value>3</value> <value>4</value> <value>5</value> <value>6</value> <value>7</value> <value>8</value> <value>9</value> </values>
make_class(name, fields) that makes a
subclass of Base out of a list of fields. For example,
make_class("City", ["name", "country"]) // Makes City class
c = new City("Ho Chi Minh City", "Vietnam")
to_s method to the class made by
make_class that yields a string of the form
Classname,field1=value1,field2=value2,.... Example:
City,name=Ho Chi Minh City,country=Vietnam
class CSVBase < Base
def self.inherited(klass)
# Read first row of CSV file and add fields to klass
end
end
To test it, make a file City.csv and run
class City < CSVBase
end
c = City.new("Berlin", "Germany")
CSVBase for reading an array of all objects
of the class.