A Ruby program that emails the weather from multiple cities to me each day

By Alvin J. Alexander, devdaily.com

I just finished a Ruby program that gets the current weather from multiple cities, using the Yahoo! Weather RSS Feed, and then mails the output to me.

When I receive the email message this is what the body of the message looks like:

Santa Fe, NM:
Currently 39 degrees, 33% humidity, 13 mph winds, Partly Cloudy.
Forecast: 17 low, 36 high.
Sunrise: 6:17 am, sunset: 6:09 pm.

Savannah, GA:
Currently 77 degrees, 65% humidity, 9 mph winds, Partly Cloudy.
Forecast: 64 low, 84 high.
Sunrise: 6:37 am, sunset: 6:31 pm.

Seward, AK:
Currently 24 degrees, 34% humidity, 12 mph winds, Fair.
Forecast: 16 low, 29 high.
Sunrise: 7:20 am, sunset: 6:56 pm.

Virginia Beach, VA:
Currently 83 degrees, 39% humidity, 24 mph winds, Mostly Cloudy/Windy.
Forecast: 63 low, 85 high.
Sunrise: 6:18 am, sunset: 6:10 pm.

And without any delay here's the Ruby source code that gets the weather information and generates this email message:

# a ruby program pulled together by Al Alexander, http://devdaily.com
# thanks to Jay Anderson and many others

require 'rexml/document'
require 'open-uri'
require 'net/smtp'

# yahoo weather url info
# http://developer.yahoo.net/weather/#examples

#Returns a hash containing the location and temperature information
#Accepts US zip codes or Yahoo location id's
def yahoo_weather_query(loc_id, units)
    h = {}
    open("http://xml.weather.yahoo.com/forecastrss?p=#{loc_id}&u=#{units}") do |http|
        response = http.read
        doc = REXML::Document.new(response)
        root              = doc.root
        channel           = root.elements['channel']
        location          = channel.elements['yweather:location']
        h[:city]          = location.attributes["city"]
        h[:region]        = location.attributes["region"]
        h[:country]       = location.attributes["country"]
        h[:temp]          = channel.elements["item"].elements["yweather:condition"].attributes["temp"]
        h[:text]          = channel.elements["item"].elements["yweather:condition"].attributes["text"]
        h[:wind_speed]    = channel.elements['yweather:wind'].attributes['speed']
        h[:humidity]      = channel.elements['yweather:atmosphere'].attributes['humidity']
        h[:sunrise]       = channel.elements['yweather:astronomy'].attributes['sunrise']
        h[:sunset]        = channel.elements['yweather:astronomy'].attributes['sunset']
        h[:forecast_low]  = channel.elements["item"].elements['yweather:forecast'].attributes['low']
        h[:forecast_high] = channel.elements["item"].elements['yweather:forecast'].attributes['high']
    end
    return h       # not cool, i know
end

def get_weather_for_city(city_code,units)
  weather_info = yahoo_weather_query(city_code, units)
  city          = weather_info[:city]
  region        = weather_info[:region]
  country       = weather_info[:country]
  temp          = weather_info[:temp]
  wind_speed    = weather_info[:wind_speed]
  humidity      = weather_info[:humidity]
  text          = weather_info[:text]
  sunrise       = weather_info[:sunrise]
  sunset        = weather_info[:sunset]
  forecast_low  = weather_info[:forecast_low]
  forecast_high = weather_info[:forecast_high]

  return "#{city}, #{region}:\n" +
         "   Currently #{temp} degrees, #{humidity}% humidity, #{wind_speed} mph winds, #{text}.\n" +
         "   Forecast: #{forecast_low} low, #{forecast_high} high.\n" +
         "   Sunrise: #{sunrise}, sunset: #{sunset}.\n"
end

class SendMail < Net::SMTP
  def initialize(options)
      super
      @user     = options["user"]
      @from     = options["from"]
      @to       = options["to"].to_a
      @pass     = options["pass"]
      @server   = options["server"]
      @subject  = options["subject"]
  end

  def body=(mail_body)
      time_foo = Time.new.httpdate
      # BUILD HEADERS
      @body =  "From: #{@from} <#{@from}>\n"
      @body << "To: #{@to}<#{@to}>\n"
      @body << "Subject: #{@subject}\n"
      @body << "Date: #{time_foo}\n"
      @body << "Importance:high\n"
      @body << "MIME-Version:1.0\n"
      @body << "\n\n\n"
      # MESSAGE BODY
      @body << mail_body
  end

  def send
    smtp = Net::SMTP.start(@server, 25)
    smtp.send_message  @body, @from, @to
    smtp.finish
  end
end

##########
#  MAIN  #
##########

# 'USAK0216' (seward, ak)
# 'USNM0292' (santa fe, nm)
# 'USVA0797' (va beach, va)
# 'USGA0506' (savannah, ga)
# 'USRI0050' (providence, ri)

weather_string = ''
units = 'f'
['USRI0050', 'USNM0292', 'USGA0506', 'USAK0216', 'USVA0797'].each {|city|
  weather_string = weather_string + get_weather_for_city(city,'f') + "\n"
}

o=Hash.new
o["from"]    = ""                        # populate this with my email address
o["to"]      = ""                        # use the same email address here
o["server"]  = ""                        # put my smtp server name here
o["subject"] = "Today's Weather in Places I'd Like to Live"
mail=SendMail.new(o)
mail.body=weather_string
mail.send

Many thanks to Jay Anderson, who created and posted 'get weather' code at ruby-forum.com. And thanks to Yahoo! for providing this service free of charge.


devdaily logo