Ruby 3

Cover page image

Cay S. Horstmann

Metaprogramming

Preliminaries

Method Missing

Creating New Classes

Creating New Methods

Inheritance Callback

But Wait, There's More

Lab

???

Reading from a File

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

Step 1 - Method Missing

  1. Write a class 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? 

  2. How can you deal with assignments
    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.

Step 2 - XMLBuilder

  1. Start irb. Run
    require "rubygems"
    require "builder"
    xb = Builder::XmlMarkup.new(:target => $stdout, :indent => 1)
    xb.Hello "World!"

    What output do you get?

  2. What is Hello? A field? A method? A parameter? A variable? A class? Something else?
  3. What is "World"? Don't say "a string"—we know that. How is it related to xb.Hello?
  4. Try xb.fred("Wilma"). Clearly the designers of the XML Builder didn't know or care about Fred. How is this implemented?
  5. Try
    xb.Hello "World!", "type" => "global", "state" => "happy"

    What do you get?

  6. How is that implemented?
  7. Now try
    xb.date {
      xb.year "2006"
      xb.month "01"
      xb.day "01"
    }

    What output do you get?

  8. What is going on? What is the {...}? How does the date method process it?
  9. Generate the following XML in Ruby (of course with a loop inside 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>

Step 3 - Building Classes

  1. Write a function 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")
  2. Add a 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
  3. Complete
    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")
  4. Add a method to CSVBase for reading an array of all objects of the class.