On Aug 5, 2005, at 11:30 AM, Ruby Quiz wrote:

> This week's Ruby Quiz is to build this simple tool.

Here's the code used to create the quiz:

#!/usr/local/bin/ruby

require "yaml"
require "soap/rpc/driver"

server = SOAP::RPC::Driver.new( "http://services.xmethods.com/soap",
                                 "urn:xmethods-delayed-quotes" )
server.add_method( "getQuote", "symbol" )

Struct.new("Shares", :symbol, :shares, :price, :date)

file_name = File.join(ENV["HOME"], ".portfolio.yaml")
portfolio = if File.exist? file_name
     File.open(file_name) { |file| YAML.load(file) }
else
     Hash.new
end

if portfolio.empty? or not ARGV.empty? and ARGV.first == "-p"
     loop do
         print "Buy (symbol shares/dollars):  "
         line = $stdin.gets.strip
         break if line.empty?

         ticker, amount = line.split
         price = server.getQuote(ticker)
         if price <= 0
             puts "Unable to fetch a price for that ticker symbol."
             next
         end

         if amount =~ /^\$(\d+)$/
             dollars = Integer($1)
             shares  = (dollars / price).to_i
         elsif amount =~ /^[1-9]\d*$/
             shares  = Integer(amount)
         else
             puts "Unknown amount format."
             next
         end

         if shares.zero?
             puts "You can't purchase any shares for that amount."
             next
         else
             date = Time.now.strftime("%m/%d/%y %H:%M")
             buy  = Struct::Shares.new(ticker, shares, price, date)
             portfolio[buy.symbol] = buy

             puts "You purchased #{buy.shares} shares of # 
{buy.symbol} " +
                  "for $#{"%.2f" % (buy.shares * price)}."
         end
     end

     File.open(file_name, "w") { |file| YAML.dump(portfolio, file) }
end

output = [%w{Symbol Shares Buy\ Price Buy\ Date Current\ Price Gain/ 
Loss}]
portfolio.each do |ticker, details|
     current = server.getQuote(ticker)
     output << [ ticker, details.shares, details.price, details.date,  
current,
                 details.shares * current - details.shares *  
details.price ]
end

widths = Array.new(output.first.size, 0)
output.each do |row|
     row.each_index do |index|
         width         = if row[index].is_a? Float
             ("%.2f" % row[index]).length + 1
         else
             row[index].to_s.length
         end
         widths[index] = width if width > widths[index]
     end
end

border   = "+-" + widths.map { |width| "-" * width }.join("-+-") + "-+"
template = ("| " + output.last.map do |column|
     case column
     when Float
         "$%#{widths.shift - 1}.2f"
     when Integer
         "%#{widths.shift}d"
     else
         "%#{widths.shift}s"
     end
end.join(" | ") + " |").sub(/\A\| %/, "| %-")
puts border
puts template.gsub(/ \| \$?%(\d+)(?:\.\d+)?[sdf]/, " | %\\1s") %  
output.first
output[1..-1].sort.each do |row|
     puts border
     puts template % row
end
puts border

__END__

James Edward Gray II