Seven Languages in Seven Weeks Part 2 - Ruby Day 3
by Justin Michalicek on April 14, 2011, 6:29 a.m. UTCFinished up day 3 of Ruby a little while ago. The goal was to implement an each method on a provided class which read a csv. The each method was to return each row of the csv as a new CsvRow class. The CsvRow class needed to let you specify object.rowname and get that column value from the row.
So with a csv like this: one, two, three cake, cookies, pie dog, cat, goat
You could do something like:
my_csv = RubyCsv.new() my_csv.each {|row| puts row.three}
And get the output:
pie goat
And of course the column names shouldn't matter, so any csv, with any column names would work with no changes to the code. This last requirement was handled with overriding the method_missing() method on the CsvRow object so that calling the row names as methods, which don't exist, would trigger method_missing() and it could figure out what to do.
Here's the full solution how I implemented it:
module ActsAsCsv def self.included(base) base.extend ClassMethods end module ClassMethods def acts_as_csv include InstanceMethods end end module InstanceMethods def read @csv_contents = [] filename = self.class.to_s.downcase + '.txt' file = File.new(filename) @headers = file.gets.chomp.split(', ') file.each do |row| @csv_contents << row.chomp.split(', ') end end #I added this method def each @csv_contents.each {|a| yield(CsvRow.new(a,@headers))} end attr_accessor :headers, :csv_contents def initialize read end end end class RubyCsv include ActsAsCsv acts_as_csv end #I added this class class CsvRow attr_accessor :row, :headers def initialize(row = [], headers = []) @row = row @headers = headers end def method_missing(name) mysub = name.to_s index = @headers.find_index(mysub) return @row[index] end end