Jay,
I liked your idea of searching for the location id, so I added it to my
original submission. If you use the -s option, it will give you a menu
of choices. If it only finds one choice it will just return the
temperature.
Examples:
c:\>ruby c:\current_temp.rb madrid -s
1. Madrid, Nebraska, United States
2. Madrid, New York, United States
3. Madrid, Iowa, United States
4. Madrid, Spain
5. General La Madrid, Argentina
6. New Madrid, Missouri, United States
Please choose your location 4
The current temperature in Madrid, is 37 degrees F.
c:\>ruby current_temp.rb "madrid, spain" -s
The current temperature in Madrid, is 37 degrees F.
# current_temp.rb
require 'net/http'
require 'rexml/document'
require 'optparse'
require "rubygems"
require "highline/import"
require 'cgi'
class LocationSearch
attr_reader :loc
def initialize(string)
city = CGI.escape(string)
h = Net::HTTP.new('weather.yahoo.com', 80)
resp, data = h.get("/search/weather2?p=#{city}", nil)
case resp
when Net::HTTPSuccess then @loc = location_menu(
parse_locations(data) )
when Net::HTTPRedirection then @loc =
get_location(resp['location'])
end
end
def location_menu(hash)
choose do |menu|
menu.prompt = "Please choose your location "
hash.each do |key,val|
menu.choice val do return key end
end
end
end
def parse_locations(data)
a = {}
data.split("\n").each do |i|
a[get_location(i)]=strip_html(i) if /a href="\/forecast/ =~ i
end
a
end
def strip_html(str)
str = str.strip || ''
str.gsub(/<(\/|\s)*[^>]*>/,'')
end
def get_location(string)
string.split(/\/|\./)[2]
end
end
class CurrentTemp
include REXML
def initialize(loc,u='f')
uri = "http://xml.weather.yahoo.com/forecastrss?p=#{loc}&u=#{u}"
@doc = Document.new Net::HTTP.get(URI.parse(uri))
raise "Invalid city, \"#{loc}\"" if /error/i =~
@doc.elements["//description"].to_s
end
def method_missing(methodname)
XPath.match(@doc,"//*[starts-with(name(), 'yweather')]").each
do>elem>
return elem.attributes[methodname.to_s] if
elem.attributes[methodname.to_s]
end
Object.method_missing(methodname)
end
def unit
self.temperature
end
def state
self.region
end
def to_s
"The current temperature in #{self.city}, #{self.state} is
#{self.temp} degrees #{self.unit}."
end
end
begin
opts = OptionParser.new
opts.banner = "Usage:\n\n current_temp.rb city [-u unit]\n\n
"
opts.banner += "city should be a zip code, or a Yahoo Weather
location id.\n\n"
opts.on("-uARG", "--unit ARG","Should be f or c", String) {|val| @u
= val }
opts.on("-s", "--search","Search location") {@search = true}
opts.on("-h", "--help") {puts opts.to_s ; exit 0}
loc = opts.parse!.to_s
@u ||='f'
if @search
loc = LocationSearch.new(loc).loc
end
if loc.empty?
raise "Invalid city, \"#{loc}\""
else
puts
puts CurrentTemp.new(loc,@u)
end
rescue
puts $!
puts opts.to_s
exit 1
end