Wednesday, February 20, 2013

7 Languages in 7 weeks: Ruby day 3


Ruby Day 3: Serious change 

The final day on Ruby is all about metaprogramming. It takes you through:
  • Adding methods to open classes
  • Using method missing to produce very human readable syntax
  • Adding functionality to classes via Modules
Having used Java reflection, Java IDL Dynamic Invocation Interface (DII) and the Java IDL Dynamic Seketon Interface extensively when I used to develop IBM's Websphere Message Broker I know how cumbersome any form of metaprogramming is in Java. So I enjoyed playing around with a language that does it much better!

Now to the Self study. Only one this time! Modify the Csv application to support an each method to return a CsvRow object. E.g for the following csv file:

one, two
lions, tigers

allow an API that works like this:

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

This should print "lions".

Well I decided to work with the following input:

one, two, three
1, 2, 3 
uno, dos, tres

And here is my solution using modules and method missing:

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

    def each(&block)
      @csv_contents.each_index do |index|
        hash = Hash[@headers.zip @csv_contents[index]]
        block.call(CsvRow.new(hash))  
      end
    end
  end

  attr_accessor :headers, :csv_contents
  def initialize
    read
  end
end

class RubyCsv
  include ActsAsCsv
  acts_as_csv
end

class CsvRow
  def initialize(arg)
    @arg = arg
  end
  
  def method_missing name, *args
    key = name.to_s
    return @arg[key]
  end
end


m = RubyCsv.new
m.each do |row| 
  puts row.send(ARGV[0])
end


And that is it for Ruby!

No comments: