Seven Languages in Seven Weeks: Day 3

The emphasis for the final day of Ruby was on metaprogramming using open classes and mixins. So without further ado, here's a CSV reader that provides a mixin that lets you access the values of a CSV file using dynamic properties (Whee!):

module ActsAsCsv
    class CsvRow
        def initialize(csv, fields)
            @fields = fields
            @csv = csv
            @map = Hash[@csv.headers.zip(fields)]
        end

        def method_missing(key)
            @map[key.to_s]
        end
    end

    def self.included(base)
        base.extend ClassMethods
    end

    module ClassMethods
        def acts_as_csv
            include InstanceMethods
        end
    end

    module InstanceMethods
        include Enumerable

        attr_accessor :headers, :csv_contents

        def initialize
            read
        end

        def each
            @csv_contents.each do |row|
                yield row
            end
        end

        def parse(s)
            s.chomp.split(', ')
        end

        def read
            @csv_contents = []
            filename = self.class.to_s.downcase + ".csv"
            file = File.new(filename)
            @headers = parse(file.gets)

            file.each do |row|
                fields = parse(row)
                @csv_contents << CsvRow.new(self, fields)
            end
        end
    end
end

Given a CSV file like this:

msg, response
Hello!, Hi!

... we could read it like this:

class Example
    include ActsAsCsv
    acts_as_csv
end

Example.new.each do |row|
    puts row.msg, row.response
end