Permalink
Browse files

Reworking to use nokogiri.

- upped library to version 1.2.0 to reflect changes.

- use new Yahoo Weather API url at
  http://weather.yahooapis.com/forecastrss

- shift xml parsing to use nokogiri rather than xml-simple.

- shift from Response.image_url to Response.image which
  contains the new YahooWeather::Image record with the old image url
  plus more detail as now provided in the API response.
  • Loading branch information...
Walter Korman
Walter Korman committed Dec 25, 2009
1 parent 3d2b9ad commit 8eb35606a2072f4f4d2f6e4f98da52d0e3064dcc
View
@@ -1,3 +1,14 @@
+* [shaper] upped library to version 1.2.0 to reflect changes.
+
+* [shaper] use new Yahoo Weather API at
+ http://weather.yahooapis.com/forecastrss
+
+* [shaper] shift xml parsing to use nokogiri rather than xml-simple.
+
+* [shaper] shift from Response.image_url to Response.image which
+ contains the new YahooWeather::Image record with the old image url
+ plus more detail as now provided in the API response.
+
* [shaper] refactor for single class per file.
* [shaper] fix km/h unit speed in test.
View
@@ -48,7 +48,7 @@ A simple example program:
# sample html output
print <<edoc
<div>
- <img src="#{response.image_url}"><br/>
+ <img src="#{response.image.url}"><br/>
#{response.condition.temp} degrees #{response.units.temperature}<br/>
#{response.condition.text}<br>
Forecast:<br/>
View
@@ -8,7 +8,7 @@ Hoe.spec('yahoo-weather') do |p|
self.rubyforge_name = 'yahoo-weather'
self.author = 'Walter Korman'
self.email = 'shaper@fatgoose.com'
- self.extra_deps << [ 'xml-simple', '>= 1.0.9' ]
+ self.extra_deps << [ 'nokogiri', '>= 1.4.1' ]
self.summary = 'A Ruby object-oriented interface to the Yahoo! Weather service.'
self.description = <<EDOC
The yahoo-weather rubygem provides a Ruby object-oriented interface to the
@@ -17,7 +17,6 @@ Yahoo! Weather service described in detail at:
http://developer.yahoo.com/weather
EDOC
self.url = 'http://rubyforge.org/projects/yahoo-weather'
- self.changes = p.paragraphs_of('CHANGELOG.rdoc', 0..1).join("\n\n")
self.remote_rdoc_dir = '' # Release to root
self.readme_file = 'README.rdoc'
self.history_file = 'CHANGELOG.rdoc'
View
@@ -16,7 +16,7 @@
# html output
print <<edoc
<div>
- <img src="#{response.image_url}"><br/>
+ <img src="#{response.image.url}"><br/>
#{response.condition.temp} degrees #{response.units.temperature}<br/>
#{response.condition.text}<br>
Forecast:<br/>
View
@@ -17,13 +17,11 @@
require 'net/http'
require 'time'
-require 'xmlsimple'
+require 'nokogiri'
class YahooWeather
+ VERSION = '1.2.0'
- VERSION = '1.1.0'
-
- private
def self._parse_time (text)
(text) ? Time.parse(text) : nil
end
@@ -34,6 +32,7 @@ def self._parse_time (text)
require 'yahoo-weather/client'
require 'yahoo-weather/condition'
require 'yahoo-weather/forecast'
+require 'yahoo-weather/image'
require 'yahoo-weather/location'
require 'yahoo-weather/response'
require 'yahoo-weather/units'
@@ -1,7 +1,7 @@
# The main client object through which the Yahoo! Weather service may be accessed.
class YahooWeather::Client
# the url with which we obtain weather information from yahoo
- @@API_URL = "http://xml.weather.yahoo.com/forecastrss"
+ @@API_URL = "http://weather.yahooapis.com/forecastrss"
def initialize (api_url = @@API_URL)
@api_url = api_url
@@ -17,25 +17,30 @@ def initialize (api_url = @@API_URL)
# 'USWA0395.html', so the location code for Seattle is 'USWA0395'.
#
# +units+ allows specifying whether to retrieve information in
- # +Fahrenheit+ as +f+, or +Celsius+ as +c+, and defaults to +f+.
+ # +Fahrenheit+ as +YahooWeather::Units::FAHRENHEIT+, or +Celsius+ as
+ # +YahooWeather::Units::CELSIUS+, and defaults to fahrenheit.
+ #
def lookup_location (location, units = 'f')
# query the service to grab the xml data
url = _request_url(location, units)
begin
- response = Net::HTTP.get_response(URI.parse(url)).body.to_s
+ response = Net::HTTP.get_response(URI.parse(url)).body.to_s
+
rescue
raise "failed to get weather via '#{url}': " + $!
end
# create the response object
- response = XmlSimple.xml_in(response)
- YahooWeather::Response.new(location, url, response)
+ doc = Nokogiri::XML.parse(response)
+ YahooWeather::Response.new(location, url, doc)
end
private
def _request_url (location, units)
@api_url + '?p=' + URI.encode(location) + '&u=' + URI.encode(units)
+# TODO: for WOEID lookup, use 'w' rather than 'p' for query param
+# @api_url + '?w=' + URI.encode( location ) + '&u=' + URI.encode( units )
end
end
View
@@ -0,0 +1,25 @@
+class YahooWeather::Image
+ # the height of the image in pixels.
+ attr_reader :height
+
+ # the url intended to be used as a link wrapping the image, for
+ # instance, to send the user to the main Yahoo Weather home page.
+ attr_reader :link
+
+ # the title of hte image.
+ attr_reader :title
+
+ # the full url to the image.
+ attr_reader :url
+
+ # the width of the image in pixels.
+ attr_reader :width
+
+ def initialize (payload)
+ @title = payload.xpath('title').first.content
+ @link = payload.xpath('link').first.content
+ @url = payload.xpath('url').first.content
+ @height = payload.xpath('height').first.content.to_i
+ @width = payload.xpath('width').first.content.to_i
+ end
+end
@@ -32,9 +32,9 @@ class YahooWeather::Response
# weather conditions for the requested location.
attr_reader :description
- # a link to the Yahoo! Weather image icon representing the current weather
- # conditions visually.
- attr_reader :image_url
+ # a YahooWeather::Image record describing an image icon
+ # representing the current weather.
+ attr_reader :image
# the latitude of the location for which weather is detailed.
attr_reader :latitude
@@ -55,32 +55,31 @@ class YahooWeather::Response
# the prose descriptive title of the weather information.
attr_reader :title
- # regular expression used to pull the weather image icon from full
- # description text.
- @@REGEXP_IMAGE = Regexp.new(/img src="([^"]+)"/)
-
- def initialize (request_location, request_url, payload)
+ def initialize (request_location, request_url, doc)
+ # save off the request params
@request_location = request_location
@request_url = request_url
- root = payload['channel'].first
- @astronomy = YahooWeather::Astronomy.new root['astronomy'].first
- @location = YahooWeather::Location.new root['location'].first
- @units = YahooWeather::Units.new root['units'].first
- @wind = YahooWeather::Wind.new root['wind'].first
- @atmosphere = YahooWeather::Atmosphere.new root['atmosphere'].first
+ # parse the nokogiri xml document to gather response data
+ root = doc.xpath('/rss/channel').first
+
+ @astronomy = YahooWeather::Astronomy.new(root.xpath('yweather:astronomy').first)
+ @location = YahooWeather::Location.new(root.xpath('yweather:location').first)
+ @units = YahooWeather::Units.new(root.xpath('yweather:units').first)
+ @wind = YahooWeather::Wind.new(root.xpath('yweather:wind').first)
+ @atmosphere = YahooWeather::Atmosphere.new(root.xpath('yweather:atmosphere').first)
+ @image = YahooWeather::Image.new(root.xpath('image').first)
- item = root['item'].first
- @condition = YahooWeather::Condition.new item['condition'].first
+ item = root.xpath('item').first
+ @condition = YahooWeather::Condition.
+ new(item.xpath('yweather:condition').first)
@forecasts = []
- item['forecast'].each { |forecast| @forecasts << YahooWeather::Forecast.new(forecast) }
- @latitude = item['lat'].first.to_f
- @longitude = item['long'].first.to_f
- @page_url = item['link'].first
- @title = item['title'].first
- @description = item['description'].first
-
- match_data = @@REGEXP_IMAGE.match(description)
- @image_url = (match_data) ? match_data[1] : nil
+ item.xpath('yweather:forecast').each { |forecast|
+ @forecasts << YahooWeather::Forecast.new(forecast) }
+ @latitude = item.xpath('geo:lat').first.content.to_f
+ @longitude = item.xpath('geo:long').first.content.to_f
+ @page_url = item.xpath('link').first.content
+ @title = item.xpath('title').first.content
+ @description = item.xpath('description').first.content
end
end
@@ -1,5 +1,8 @@
# Describes the units of measure with which weather information is provided.
class YahooWeather::Units
+ FAHRENHEIT = 'f'
+ CELSIUS = 'c'
+
# the units in which temperature is measured, e.g. +F+ for +Fahrenheit+ or +C+ for +Celsius+.
attr_reader :temperature
View
@@ -42,9 +42,10 @@ def test_units
def _assert_valid_response (response, request_location, units, city, region, country)
assert_not_nil response
- # check the request location and url explicitly against what we know they should be
+ # check the request location and url explicitly against what we
+ # know they should be
assert_equal response.request_location, request_location
- assert_equal response.request_url, "http://xml.weather.yahoo.com/forecastrss?p=#{request_location}&u=#{units}"
+ assert_equal response.request_url, "http://weather.yahooapis.com/forecastrss?p=#{request_location}&u=#{units}"
# check the astronomy info
assert_instance_of YahooWeather::Astronomy, response.astronomy
@@ -66,7 +67,7 @@ def _assert_valid_response (response, request_location, units, city, region, cou
assert(response.wind.chill <= response.condition.temp)
assert_kind_of Numeric, response.wind.direction
assert_kind_of Numeric, response.wind.speed
-
+
# check the atmosphere info
assert_instance_of YahooWeather::Atmosphere, response.atmosphere
assert_kind_of Numeric, response.atmosphere.humidity
@@ -95,21 +96,21 @@ def _assert_valid_response (response, request_location, units, city, region, cou
assert(forecast.text && forecast.text.length > 0)
_assert_valid_weather_code forecast.code
end
-
+
# check the basic attributes
assert(response.description && response.description.length > 0)
- assert(response.image_url =~ /yimg\.com/)
+ assert(response.image.url =~ /yimg\.com/)
assert_kind_of Numeric, response.latitude
assert_kind_of Numeric, response.longitude
assert(response.page_url =~ /\.html$/)
assert(response.title && response.title.length > 0)
- assert_not_nil (response.title.index("#{response.location.city}, #{response.location.region}"))
+ assert_not_nil(response.title.index("#{response.location.city}, #{response.location.region}"))
end
-
+
def _assert_valid_weather_code (code)
(((code >= 0) && (code <= 47)) || (code == 3200))
end
-
+
def _assert_valid_units (units, fahrenheit_based)
assert_instance_of YahooWeather::Units, units
if fahrenheit_based

0 comments on commit 8eb3560

Please sign in to comment.