yahoo-weather 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ # Describes astronomy information for a particular location.
2
+ class YahooWeather::Astronomy
3
+ # a Time object detailing the sunrise time for a location.
4
+ attr_reader :sunrise
5
+
6
+ # a Time object detailing the sunset time for a location.
7
+ attr_reader :sunset
8
+
9
+ def initialize (payload)
10
+ @sunrise = YahooWeather._parse_time(payload['sunrise'])
11
+ @sunset = YahooWeather._parse_time(payload['sunset'])
12
+ end
13
+ end
@@ -0,0 +1,40 @@
1
+ # Describes the specific atmospheric conditions at a location.
2
+ class YahooWeather::Atmosphere
3
+ # Constants representing the state of the barometric pressure.
4
+ #
5
+ class Barometer
6
+ STEADY = 'steady'
7
+ RISING = 'rising'
8
+ FALLING = 'falling'
9
+
10
+ # lists all possible barometer constants
11
+ ALL = [ STEADY, RISING, FALLING ]
12
+ end
13
+
14
+ # the humidity of the surroundings.
15
+ attr_reader :humidity
16
+
17
+ # the visibility level of the surroundings
18
+ attr_reader :visibility
19
+
20
+ # the pressure level of the surroundings.
21
+ attr_reader :pressure
22
+
23
+ # the state of the barometer, defined as one of the
24
+ # YahooWeather::Atmosphere::Barometer constants.
25
+ attr_reader :barometer
26
+
27
+ def initialize (payload)
28
+ @humidity = payload['humidity'].to_i
29
+ @visibility = payload['visibility'].to_i
30
+ @pressure = payload['pressure'].to_f
31
+
32
+ # map barometric pressure direction to appropriate constant
33
+ @barometer = nil
34
+ case payload['rising'].to_i
35
+ when 0: @barometer = Barometer::STEADY
36
+ when 1: @barometer = Barometer::RISING
37
+ when 2: @barometer = Barometer::FALLING
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,70 @@
1
+ # The main client object through which the Yahoo! Weather service may be accessed.
2
+ class YahooWeather::Client
3
+ # the url with which we obtain weather information from yahoo
4
+ @@API_URL = "http://weather.yahooapis.com/forecastrss"
5
+
6
+ def initialize (api_url = @@API_URL)
7
+ @api_url = api_url
8
+ end
9
+
10
+ # Returns a YahooWeather::Response object detailing the current weather
11
+ # information for the specified location.
12
+ #
13
+ # The lookup requires the unique WOEID for the location whose
14
+ # weather is sought.. To find your WOEID, browse or search for your
15
+ # city from the Weather (http://weather.yahoo.com/) home page. The
16
+ # WOEID is in the URL for the forecast page for that city. You can
17
+ # also get the WOEID by entering your zip code on the home page. For
18
+ # example, if you search for Los Angeles on the Weather home page,
19
+ # the forecast page for that city is
20
+ # http://weather.yahoo.com/united-states/california/los-angeles-2442047/. The
21
+ # WOEID is 2442047.
22
+ #
23
+ # +units+ allows specifying whether to retrieve information in
24
+ # +Fahrenheit+ as YahooWeather::Units::FAHRENHEIT, or +Celsius+ as
25
+ # YahooWeather::Units::CELSIUS, and defaults to fahrenheit.
26
+ #
27
+ def lookup_by_woeid (woeid, units = 'f')
28
+ url = @api_url + '?w=' + CGI.escape(woeid.to_s) + '&u=' + CGI.escape(units)
29
+ _lookup(woeid, url)
30
+ end
31
+
32
+ # Returns a YahooWeather::Response object detailing the current weather
33
+ # information for the specified location.
34
+ #
35
+ # NOTE: This method is deprecated as Yahoo has deprecated this
36
+ # non-WOEID-based lookup function. Please use the new
37
+ # +lookup_by_woeid+ method instead.
38
+ #
39
+ # +location+ can be either a US zip code or a location code. Location
40
+ # codes can be looked up at http://weather.yahoo.com, where it will appear
41
+ # in the URL that results from searching on the city or zip code. For
42
+ # instance, searching on 'Seattle, WA' results in a URL ending in
43
+ # 'USWA0395.html', so the location code for Seattle is 'USWA0395'.
44
+ #
45
+ # +units+ allows specifying whether to retrieve information in
46
+ # +Fahrenheit+ as YahooWeather::Units::FAHRENHEIT, or +Celsius+ as
47
+ # YahooWeather::Units::CELSIUS, and defaults to fahrenheit.
48
+ #
49
+ def lookup_location (location, units = 'f')
50
+ url = @api_url + '?p=' + CGI.escape(location) + '&u=' + CGI.escape(units)
51
+ _lookup(location, url)
52
+ end
53
+
54
+ private
55
+
56
+ def _lookup (src, url)
57
+ begin
58
+ response = Net::HTTP.get_response(URI.parse(url)).body.to_s
59
+
60
+ rescue => e
61
+ raise RuntimeError.new("failed to get weather [src=#{src}, " +
62
+ "url=#{url}, e=#{e}].")
63
+ end
64
+
65
+ # create the response object
66
+ doc = Nokogiri::XML.parse(response)
67
+ YahooWeather::Response.new(src, url, doc)
68
+ end
69
+
70
+ end
@@ -0,0 +1,20 @@
1
+ class YahooWeather::Condition
2
+ # the Yahoo! Weather condition code, as detailed at http://developer.yahoo.com/weather.
3
+ attr_reader :code
4
+
5
+ # the date and time associated with these conditions.
6
+ attr_reader :date
7
+
8
+ # the temperature of the location.
9
+ attr_reader :temp
10
+
11
+ # the brief prose text description of the weather conditions of the location.
12
+ attr_reader :text
13
+
14
+ def initialize (payload)
15
+ @code = payload['code'].to_i
16
+ @date = YahooWeather._parse_time(payload['date'])
17
+ @temp = payload['temp'].to_i
18
+ @text = payload['text']
19
+ end
20
+ end
@@ -0,0 +1,29 @@
1
+ # The forecasted weather conditions for a specific location.
2
+ class YahooWeather::Forecast
3
+ # the brief name of the day associated with the forecast.
4
+ attr_reader :day
5
+
6
+ # the date associated with the forecast.
7
+ attr_reader :date
8
+
9
+ # the low temperature forecasted.
10
+ attr_reader :low
11
+
12
+ # the high temperature forecasted.
13
+ attr_reader :high
14
+
15
+ # the brief prose text description of the forecasted weather conditions.
16
+ attr_reader :text
17
+
18
+ # the Yahoo! Weather code associated with the forecast, per http://developer.yahoo.com/weather.
19
+ attr_reader :code
20
+
21
+ def initialize (payload)
22
+ @day = payload['day']
23
+ @date = YahooWeather._parse_time(payload['date'])
24
+ @low = payload['low'].to_i
25
+ @high = payload['high'].to_i
26
+ @text = payload['text']
27
+ @code = payload['code'].to_i
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ class YahooWeather::Image
2
+ # the height of the image in pixels.
3
+ attr_reader :height
4
+
5
+ # the url intended to be used as a link wrapping the image, for
6
+ # instance, to send the user to the main Yahoo Weather home page.
7
+ attr_reader :link
8
+
9
+ # the title of hte image.
10
+ attr_reader :title
11
+
12
+ # the full url to the image.
13
+ attr_reader :url
14
+
15
+ # the width of the image in pixels.
16
+ attr_reader :width
17
+
18
+ def initialize (payload)
19
+ @title = payload.xpath('title').first.content
20
+ @link = payload.xpath('link').first.content
21
+ @url = payload.xpath('url').first.content
22
+ @height = payload.xpath('height').first.content.to_i
23
+ @width = payload.xpath('width').first.content.to_i
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ # Describes a geographical location.
2
+ class YahooWeather::Location
3
+ # the name of the city.
4
+ attr_reader :city
5
+
6
+ # the name of the country.
7
+ attr_reader :country
8
+
9
+ # the name of the region, such as a state.
10
+ attr_reader :region
11
+
12
+ def initialize (payload)
13
+ @city = payload['city']
14
+ @country = payload['country']
15
+ @region = payload['region']
16
+ end
17
+ end
@@ -0,0 +1,85 @@
1
+ # Describes the weather conditions for a particular requested location.
2
+ class YahooWeather::Response
3
+ # a YahooWeather::Astronomy object detailing the sunrise and sunset
4
+ # information for the requested location.
5
+ attr_reader :astronomy
6
+
7
+ # a YahooWeather::Location object detailing the precise geographical names
8
+ # to which the requested location was mapped.
9
+ attr_reader :location
10
+
11
+ # a YahooWeather::Units object detailing the units corresponding to the
12
+ # information detailed in the response.
13
+ attr_reader :units
14
+
15
+ # a YahooWeather::Wind object detailing the wind information at the
16
+ # requested location.
17
+ attr_reader :wind
18
+
19
+ # a YahooWeather::Atmosphere object detailing the atmosphere information
20
+ # of the requested location.
21
+ attr_reader :atmosphere
22
+
23
+ # a YahooWeather::Condition object detailing the current conditions of the
24
+ # requested location.
25
+ attr_reader :condition
26
+
27
+ # a list of YahooWeather::Forecast objects detailing the high-level
28
+ # forecasted weather conditions for upcoming days.
29
+ attr_reader :forecasts
30
+
31
+ # the raw HTML generated by the Yahoo! Weather service summarizing current
32
+ # weather conditions for the requested location.
33
+ attr_reader :description
34
+
35
+ # a YahooWeather::Image record describing an image icon
36
+ # representing the current weather.
37
+ attr_reader :image
38
+
39
+ # the latitude of the location for which weather is detailed.
40
+ attr_reader :latitude
41
+
42
+ # the longitude of the location for which weather is detailed.
43
+ attr_reader :longitude
44
+
45
+ # a link to the Yahoo! Weather page with full detailed information on the
46
+ # requested location's current weather conditions.
47
+ attr_reader :page_url
48
+
49
+ # the location string initially requested of the service.
50
+ attr_reader :request_location
51
+
52
+ # the url with which the Yahoo! Weather service was accessed to build the response.
53
+ attr_reader :request_url
54
+
55
+ # the prose descriptive title of the weather information.
56
+ attr_reader :title
57
+
58
+ def initialize (request_location, request_url, doc)
59
+ # save off the request params
60
+ @request_location = request_location
61
+ @request_url = request_url
62
+
63
+ # parse the nokogiri xml document to gather response data
64
+ root = doc.xpath('/rss/channel').first
65
+
66
+ @astronomy = YahooWeather::Astronomy.new(root.xpath('yweather:astronomy').first)
67
+ @location = YahooWeather::Location.new(root.xpath('yweather:location').first)
68
+ @units = YahooWeather::Units.new(root.xpath('yweather:units').first)
69
+ @wind = YahooWeather::Wind.new(root.xpath('yweather:wind').first)
70
+ @atmosphere = YahooWeather::Atmosphere.new(root.xpath('yweather:atmosphere').first)
71
+ @image = YahooWeather::Image.new(root.xpath('image').first)
72
+
73
+ item = root.xpath('item').first
74
+ @condition = YahooWeather::Condition.
75
+ new(item.xpath('yweather:condition').first)
76
+ @forecasts = []
77
+ item.xpath('yweather:forecast').each { |forecast|
78
+ @forecasts << YahooWeather::Forecast.new(forecast) }
79
+ @latitude = item.xpath('geo:lat').first.content.to_f
80
+ @longitude = item.xpath('geo:long').first.content.to_f
81
+ @page_url = item.xpath('link').first.content
82
+ @title = item.xpath('title').first.content
83
+ @description = item.xpath('description').first.content
84
+ end
85
+ end
@@ -0,0 +1,24 @@
1
+ # Describes the units of measure with which weather information is provided.
2
+ class YahooWeather::Units
3
+ FAHRENHEIT = 'f'
4
+ CELSIUS = 'c'
5
+
6
+ # the units in which temperature is measured, e.g. +F+ for +Fahrenheit+ or +C+ for +Celsius+.
7
+ attr_reader :temperature
8
+
9
+ # the units in which distance is measured, e.g. +mi+ for +miles+.
10
+ attr_reader :distance
11
+
12
+ # the units in which pressure is measured, e.g. +in+ for +inches+.
13
+ attr_reader :pressure
14
+
15
+ # the units in which speed is measured, e.g. +mph+ for <tt>miles per hour</tt>.
16
+ attr_reader :speed
17
+
18
+ def initialize (payload)
19
+ @temperature = payload['temperature']
20
+ @distance = payload['distance']
21
+ @pressure = payload['pressure']
22
+ @speed = payload['speed']
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ # Describes the specific wind conditions at a location.
2
+ class YahooWeather::Wind
3
+ # the temperature factoring in wind chill.
4
+ attr_reader :chill
5
+
6
+ # the direction of the wind in degrees.
7
+ attr_reader :direction
8
+
9
+ # the speed of the wind.
10
+ attr_reader :speed
11
+
12
+ def initialize (payload)
13
+ @chill = payload['chill'].to_i
14
+ @direction = payload['direction'].to_i
15
+ @speed = payload['speed'].to_i
16
+ end
17
+ end
@@ -11,7 +11,9 @@ class TestAPI < Test::Unit::TestCase
11
11
  # check a seattle, wa zipcode
12
12
  request_location = '98103'
13
13
  response = @client.lookup_location(request_location)
14
- _assert_valid_response(response, request_location, 'f', 'Seattle', 'WA', 'US')
14
+ _assert_valid_response(response, request_location,
15
+ YahooWeather::Units::FAHRENHEIT,
16
+ 'Seattle', 'WA', 'US')
15
17
  end
16
18
 
17
19
  def test_lookup_location
@@ -19,7 +21,9 @@ class TestAPI < Test::Unit::TestCase
19
21
  # yahoo weather developer page
20
22
  request_location = 'FRXX0076'
21
23
  response = @client.lookup_location(request_location)
22
- _assert_valid_response(response, request_location, 'f', 'Paris', '', 'FR')
24
+ _assert_valid_response(response, request_location,
25
+ YahooWeather::Units::FAHRENHEIT,
26
+ 'Paris', '', 'FR')
23
27
  end
24
28
 
25
29
  def test_units
@@ -29,100 +33,118 @@ class TestAPI < Test::Unit::TestCase
29
33
  country = 'US'
30
34
 
31
35
  # check explicitly specifying fahrenheit units
32
- response = @client.lookup_location(request_location, 'f')
33
- _assert_valid_response(response, request_location, 'f', city, region, country)
36
+ response = @client.lookup_location(request_location,
37
+ YahooWeather::Units::FAHRENHEIT)
38
+ _assert_valid_response(response, request_location,
39
+ YahooWeather::Units::FAHRENHEIT,
40
+ city, region, country)
34
41
 
35
42
  # check alternate units
36
- response = @client.lookup_location(request_location, 'c')
37
- _assert_valid_response(response, request_location, 'c', city, region, country)
43
+ response = @client.lookup_location(request_location,
44
+ YahooWeather::Units::CELSIUS)
45
+ _assert_valid_response(response, request_location,
46
+ YahooWeather::Units::CELSIUS,
47
+ city, region, country)
38
48
  end
39
-
49
+
50
+ def test_lookup_by_woeid
51
+ woeid_sf = '12797168'
52
+ response = @client.lookup_by_woeid(woeid_sf)
53
+ _assert_valid_response(response, woeid_sf,
54
+ YahooWeather::Units::FAHRENHEIT,
55
+ 'San Francisco', 'CA', 'United States')
56
+ end
57
+
40
58
  private
41
59
 
42
- def _assert_valid_response (response, request_location, units, city, region, country)
43
- assert_not_nil response
44
-
45
- # check the request location and url explicitly against what we know they should be
46
- assert_equal response.request_location, request_location
47
- assert_equal response.request_url, "http://xml.weather.yahoo.com/forecastrss?p=#{request_location}&u=#{units}"
48
-
49
- # check the astronomy info
50
- assert_instance_of YahooWeather::Astronomy, response.astronomy
51
- assert_instance_of Time, response.astronomy.sunrise
52
- assert_instance_of Time, response.astronomy.sunset
53
- assert(response.astronomy.sunrise < response.astronomy.sunset)
54
-
55
- # check the location
56
- assert_instance_of YahooWeather::Location, response.location
57
- assert_equal response.location.city, city
58
- assert_equal response.location.region, region
59
- assert_equal response.location.country, country
60
-
61
- # check the default units
62
- _assert_valid_units response.units, (units == 'f')
63
-
64
- # check the wind info
65
- assert_instance_of YahooWeather::Wind, response.wind
66
- assert(response.wind.chill <= response.condition.temp)
67
- assert_kind_of Numeric, response.wind.direction
68
- assert_kind_of Numeric, response.wind.speed
69
-
70
- # check the atmosphere info
71
- assert_instance_of YahooWeather::Atmosphere, response.atmosphere
72
- assert_kind_of Numeric, response.atmosphere.humidity
73
- assert_kind_of Numeric, response.atmosphere.visibility
74
- assert_kind_of Numeric, response.atmosphere.pressure
75
- assert(YahooWeather::Atmosphere::Barometer::ALL.include?(response.atmosphere.barometer))
76
-
77
- # check the condition info
78
- assert_instance_of YahooWeather::Condition, response.condition
79
- _assert_valid_weather_code response.condition.code
80
- assert_instance_of Time, response.condition.date
81
- assert_kind_of Numeric, response.condition.temp
82
- assert(response.condition.text && response.condition.text.length > 0)
83
-
84
- # check the forecast info
85
- assert_not_nil response.forecasts
86
- assert_kind_of Array, response.forecasts
87
- assert_equal response.forecasts.length, 2
88
- response.forecasts.each do |forecast|
89
- assert_instance_of YahooWeather::Forecast, forecast
90
- assert(forecast.day && forecast.day.length == 3)
91
- assert_instance_of Time, forecast.date
92
- assert_kind_of Numeric, forecast.low
93
- assert_kind_of Numeric, forecast.high
94
- assert(forecast.low <= forecast.high)
95
- assert(forecast.text && forecast.text.length > 0)
96
- _assert_valid_weather_code forecast.code
60
+ def _assert_valid_response (response, request_location, units,
61
+ city, region, country)
62
+ assert_not_nil response
63
+
64
+ # check the request location and url explicitly against what we
65
+ # know they should be
66
+ assert_equal response.request_location, request_location
67
+ assert_match(/http:\/\/weather\.yahooapis\.com\/forecastrss\?[pw]=#{request_location}&u=#{units}/, response.request_url)
68
+
69
+ # check the astronomy info
70
+ assert_instance_of YahooWeather::Astronomy, response.astronomy
71
+ assert_instance_of Time, response.astronomy.sunrise
72
+ assert_instance_of Time, response.astronomy.sunset
73
+ assert(response.astronomy.sunrise < response.astronomy.sunset)
74
+
75
+ # check the location
76
+ assert_instance_of YahooWeather::Location, response.location
77
+ assert_equal response.location.city, city
78
+ assert_equal response.location.region, region
79
+ assert_equal response.location.country, country
80
+
81
+ # check the default units
82
+ _assert_valid_units(response.units,
83
+ (units == YahooWeather::Units::FAHRENHEIT))
84
+
85
+ # check the wind info
86
+ assert_instance_of YahooWeather::Wind, response.wind
87
+ assert(response.wind.chill <= response.condition.temp)
88
+ assert_kind_of Numeric, response.wind.direction
89
+ assert_kind_of Numeric, response.wind.speed
90
+
91
+ # check the atmosphere info
92
+ assert_instance_of YahooWeather::Atmosphere, response.atmosphere
93
+ assert_kind_of Numeric, response.atmosphere.humidity
94
+ assert_kind_of Numeric, response.atmosphere.visibility
95
+ assert_kind_of Numeric, response.atmosphere.pressure
96
+ assert(YahooWeather::Atmosphere::Barometer::ALL.include?(response.atmosphere.barometer))
97
+
98
+ # check the condition info
99
+ assert_instance_of YahooWeather::Condition, response.condition
100
+ _assert_valid_weather_code response.condition.code
101
+ assert_instance_of Time, response.condition.date
102
+ assert_kind_of Numeric, response.condition.temp
103
+ assert(response.condition.text && response.condition.text.length > 0)
104
+
105
+ # check the forecast info
106
+ assert_not_nil response.forecasts
107
+ assert_kind_of Array, response.forecasts
108
+ assert_equal response.forecasts.length, 2
109
+ response.forecasts.each do |forecast|
110
+ assert_instance_of YahooWeather::Forecast, forecast
111
+ assert(forecast.day && forecast.day.length == 3)
112
+ assert_instance_of Time, forecast.date
113
+ assert_kind_of Numeric, forecast.low
114
+ assert_kind_of Numeric, forecast.high
115
+ assert(forecast.low <= forecast.high)
116
+ assert(forecast.text && forecast.text.length > 0)
117
+ _assert_valid_weather_code forecast.code
118
+ end
119
+
120
+ # check the basic attributes
121
+ assert(response.description && response.description.length > 0)
122
+ assert(response.image.url =~ /yimg\.com/)
123
+ assert_kind_of Numeric, response.latitude
124
+ assert_kind_of Numeric, response.longitude
125
+ assert(response.page_url =~ /\.html$/)
126
+ assert(response.title && response.title.length > 0)
127
+ assert_not_nil(response.title.index("#{response.location.city}, #{response.location.region}"))
97
128
  end
98
-
99
- # check the basic attributes
100
- assert(response.description && response.description.length > 0)
101
- assert(response.image_url =~ /yimg\.com/)
102
- assert_kind_of Numeric, response.latitude
103
- assert_kind_of Numeric, response.longitude
104
- assert(response.page_url =~ /\.html$/)
105
- assert(response.title && response.title.length > 0)
106
- assert_not_nil (response.title.index("#{response.location.city}, #{response.location.region}"))
107
- end
108
-
109
- def _assert_valid_weather_code (code)
110
- (((code >= 0) && (code <= 47)) || (code == 3200))
111
- end
112
-
113
- def _assert_valid_units (units, fahrenheit_based)
114
- assert_instance_of YahooWeather::Units, units
115
- if fahrenheit_based
116
- assert_equal units.temperature, 'F'
117
- assert_equal units.distance, 'mi'
118
- assert_equal units.pressure, 'in'
119
- assert_equal units.speed, 'mph'
120
-
121
- else
122
- assert_equal units.temperature, 'C'
123
- assert_equal units.distance, 'km'
124
- assert_equal units.pressure, 'mb'
125
- assert_equal units.speed, 'kph'
129
+
130
+ def _assert_valid_weather_code (code)
131
+ (((code >= 0) && (code <= 47)) || (code == 3200))
126
132
  end
127
- end
133
+
134
+ def _assert_valid_units (units, fahrenheit_based)
135
+ assert_instance_of YahooWeather::Units, units
136
+ if fahrenheit_based
137
+ assert_equal units.temperature, 'F'
138
+ assert_equal units.distance, 'mi'
139
+ assert_equal units.pressure, 'in'
140
+ assert_equal units.speed, 'mph'
141
+
142
+ else
143
+ assert_equal units.temperature, 'C'
144
+ assert_equal units.distance, 'km'
145
+ assert_equal units.pressure, 'mb'
146
+ assert_equal units.speed, 'km/h'
147
+ end
148
+ end
149
+
128
150
  end