yahoo-weather 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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