17 May 2011

Last day on Ruby!

Retrospectively, I didn’t knew Ruby before (and I still don’t know much about it, admittedly), but it’s been fun.

I’m not a big fan after what I saw. I still find it a bit messy, but that’s maybe because I’m not used to it enough. The “you can rewrite anything and change basic behavior” is very nice, but I seriously wonder how many times it has bitten back developers. If someone rewrites one of the basic methods in a big codebase, I can only imagine the consequences, if you are luck enough to see them right away…

However, I enjoyed scripting with Ruby very much, and it was a good brainteaser to try to find the effective way of doing those things in a language I completely ignored so far.

Anyway, here is my last homework!

Do

So, the goal here is to modify a class that reads CSV files and add an each method that will perform a block on a CsvRow object, object that is to be written. This object should have its method_missing overwritten so that you can access columns as if they were methods:

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 + '.csv'
      file = File.new(filename)
      @headers = file.gets.chomp.split(', ')

      file.each do |row|
        @csv_contents << row.chomp.split(', ')
      end
    end
    
    attr_accessor :headers, :csv_contents
    
    def initialize
      read 
    end
    
    def each(&block)
      @csv_contents.each { |line| block.call CsvRow.new(@headers, line) }
    end

  end

end

class RubyCsv  # no inheritance! You can mix it in
  include ActsAsCsv
  acts_as_csv
end

class CsvRow
  attr_accessor :headings, :values
  
  def initialize(headings, values)
    @headings = headings
    @values = Hash[headings.zip(values).map { |e| [e[0], e[1]] }]
  end
  
  def method_missing(name, *args)
    col = name.to_s
    @values[col]
  end
end

csv = RubyCsv.new
csv.each { |row| puts row.two }


blog comments powered by Disqus