jdpace-weatherman 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +93 -0
- data/lib/weather_man.rb +113 -0
- data/lib/weather_man_response.rb +200 -0
- data/spec/cc_only_response.xml +72 -0
- data/spec/default_response.xml +234 -0
- data/spec/forecast_only_response.xml +142 -0
- data/spec/weather_man_response_spec.rb +190 -0
- data/spec/weather_man_spec.rb +120 -0
- metadata +69 -0
data/README.rdoc
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
= WeatherMan
|
2
|
+
|
3
|
+
A Ruby Gem that wraps the Weather Channel, inc. XML data feed
|
4
|
+
written by Jared Pace, Codeword: Studios (http://codewordstudios.com),
|
5
|
+
based on the rweather[http://github.com/ckozus/rweather] gem by Carlos Kozuszko - http://www.ckozus.com.ar/blog/.
|
6
|
+
|
7
|
+
== Dependencies
|
8
|
+
|
9
|
+
1. XmlSimple
|
10
|
+
`gem install xml-simple`
|
11
|
+
|
12
|
+
---------------------------
|
13
|
+
|
14
|
+
== Installation
|
15
|
+
|
16
|
+
|
17
|
+
% sudo gem sources -a http://gems.github.com # (if you haven't already)
|
18
|
+
% sudo gem install jdpace-weatherman
|
19
|
+
|
20
|
+
---------------------------
|
21
|
+
|
22
|
+
== Usage
|
23
|
+
|
24
|
+
|
25
|
+
Find or load a location:
|
26
|
+
|
27
|
+
require 'weather_man'
|
28
|
+
WeatherMan.partner_id = '0123456789'
|
29
|
+
WeatherMan.license_key = '0123456789abcdef'
|
30
|
+
|
31
|
+
# Search for a location
|
32
|
+
# Returns an array of WeatherMan objects
|
33
|
+
locations = WeatherMan.search('New York')
|
34
|
+
|
35
|
+
# or if you know the location id or just want to use a US Zip code
|
36
|
+
ny = WeatherMan.new('USNY0996')
|
37
|
+
|
38
|
+
Fetch the weather:
|
39
|
+
|
40
|
+
# Fetch the current conditions and 5 day forecast in 'standard' units
|
41
|
+
weather = ny.fetch
|
42
|
+
|
43
|
+
# Fetch only current conditions in metric units
|
44
|
+
weather = ny.fetch(:days => 0, :unit => 'm')
|
45
|
+
|
46
|
+
# Fetch a 3 day forecast only
|
47
|
+
weather = ny.fetch(:days => 3, :current_conditions => false)
|
48
|
+
|
49
|
+
Look at the Current Conditions:
|
50
|
+
|
51
|
+
# current temperature
|
52
|
+
temp = weather.current_conditions.temperature
|
53
|
+
|
54
|
+
feels_like = weather.current_conditions.feels_like
|
55
|
+
|
56
|
+
wind_speed = weather.current_conditions.wind.speed
|
57
|
+
wind_direction = weather.current_conditions.wind.direction
|
58
|
+
|
59
|
+
Look at the forecast:
|
60
|
+
|
61
|
+
# how many days?
|
62
|
+
weather.forecast.size
|
63
|
+
|
64
|
+
# Some different forecasts
|
65
|
+
weather.forecast.today
|
66
|
+
weather.forecast.tomorrow
|
67
|
+
weather.forecast.monday
|
68
|
+
weather.forecast.for(Date.today)
|
69
|
+
weather.forecast.for(3.days.from_now) # Note: using rails core extensions
|
70
|
+
weather.forecast.for('Sep 1')
|
71
|
+
|
72
|
+
# data for a forecast
|
73
|
+
friday = weather.forecast.friday
|
74
|
+
|
75
|
+
high_temp = friday.high
|
76
|
+
low_temp = friday.low
|
77
|
+
|
78
|
+
# forecasts are split into 2 parts day/night
|
79
|
+
friday.day.description # Partly Cloudy, Sunny...
|
80
|
+
friday.day.chance_percipitation # 0..100
|
81
|
+
|
82
|
+
night_wind_speed = friday.night.wind.speed
|
83
|
+
|
84
|
+
The Weather Channel requires that you 4 promotional links for them if you use their service. Here's how to access those links:
|
85
|
+
|
86
|
+
# The array of pr links
|
87
|
+
weather.links
|
88
|
+
|
89
|
+
# Getting the first links text and url
|
90
|
+
weather.links.first.text
|
91
|
+
weather.links.first.url
|
92
|
+
|
93
|
+
<b>TODO:</b> Document all attributes
|
data/lib/weather_man.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'xmlsimple'
|
3
|
+
require 'weather_man_response'
|
4
|
+
|
5
|
+
# Raised if partner_id and license_key are not provided
|
6
|
+
class WeatherManNotConfiguredError < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
# Raised when the API returns an error
|
10
|
+
class WeatherManApiError < StandardError
|
11
|
+
end
|
12
|
+
|
13
|
+
# Raised when a location is not found by id
|
14
|
+
class WeatherManLocationNotFoundError < StandardError
|
15
|
+
end
|
16
|
+
|
17
|
+
class WeatherMan
|
18
|
+
VALID_UNITS = ['s', 'm']
|
19
|
+
DEFAULT_UNIT = 's' #standard
|
20
|
+
|
21
|
+
# partner id and license key can be obtained
|
22
|
+
# when you sign up for the weather.com xml api at
|
23
|
+
# http://www.weather.com/services/xmloap.html
|
24
|
+
@@partner_id = nil
|
25
|
+
@@license_key = nil
|
26
|
+
def self.partner_id; @@partner_id; end;
|
27
|
+
def self.partner_id=(pid); @@partner_id = pid; end;
|
28
|
+
def self.license_key; @@license_key; end;
|
29
|
+
def self.license_key=(lk); @@license_key = lk; end;
|
30
|
+
|
31
|
+
# id is the location id for the WeatherMan instance ie. 'USNY0996'
|
32
|
+
# name is the human readable name of the location ie. 'New York, NY'
|
33
|
+
attr_reader :id, :name
|
34
|
+
|
35
|
+
def initialize(location_id, location_name = 'n/a')
|
36
|
+
@id = location_id
|
37
|
+
@name = location_name
|
38
|
+
|
39
|
+
self.class.check_authentication
|
40
|
+
end
|
41
|
+
|
42
|
+
def fetch(opts = {})
|
43
|
+
options = default_forecast_options.merge(opts)
|
44
|
+
api_url = weather_url(options)
|
45
|
+
|
46
|
+
WeatherManResponse.new(self.class.fetch_response(api_url), api_url)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return an array of matching locations
|
50
|
+
def self.search(where)
|
51
|
+
# Make sure the partner id and license key have been provided
|
52
|
+
check_authentication
|
53
|
+
|
54
|
+
if response = fetch_response(search_url(:where => where))
|
55
|
+
response['loc'] ? response['loc'].map {|location| new(location['id'], location['content'])} : []
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
# API url for accssing weather
|
61
|
+
def weather_url(options = {})
|
62
|
+
options = encode_options(options)
|
63
|
+
options[:unit] = options[:unit].to_s.downcase[0..0] if options[:unit] # Allows for :metric, 'metric', 'Metric', or standard 'm'
|
64
|
+
|
65
|
+
url = "http://xoap.weather.com/weather/local/#{self.id}"
|
66
|
+
url << "?par=#{@@partner_id}"
|
67
|
+
url << "&key=#{@@license_key}"
|
68
|
+
url << "&prod=xoap"
|
69
|
+
url << "&link=xoap"
|
70
|
+
url << "&cc=*" if options[:current_conditions]
|
71
|
+
url << "&dayf=#{options[:days]}" if options[:days] && (1..5).include?(options[:days].to_i)
|
72
|
+
url << "&unit=#{options[:unit]}" if options[:unit] = (VALID_UNITS.include?(options[:unit]) ? options[:unit] : DEFAULT_UNIT)
|
73
|
+
url
|
74
|
+
end
|
75
|
+
|
76
|
+
def default_forecast_options
|
77
|
+
{
|
78
|
+
:current_conditions => true,
|
79
|
+
:days => 5, # 0 - 5
|
80
|
+
:unit => DEFAULT_UNIT
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
# Encode a hash of options to be used as request parameters
|
85
|
+
def encode_options(options)
|
86
|
+
options.each do |key,value|
|
87
|
+
options[key] = URI.encode(value.to_s) unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Fetch Response from the api
|
92
|
+
def self.fetch_response(api_url)
|
93
|
+
xml_data = Net::HTTP.get_response(URI.parse(api_url)).body
|
94
|
+
response = XmlSimple.xml_in(xml_data)
|
95
|
+
|
96
|
+
# Check if a response was returned at all
|
97
|
+
raise(WeatherManNoResponseError, "WeatherMan Error: No Response.") unless response
|
98
|
+
|
99
|
+
# Check if API call threw an error
|
100
|
+
raise(WeatherManApiError, "WeatherMan Error #{response['err'][0]['type']}: #{response['err'][0]['content']}") if response['err']
|
101
|
+
|
102
|
+
response
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.check_authentication
|
106
|
+
raise(WeatherManNotConfiguredError, 'A partner id and a license key must be provided before acessing the API') unless @@partner_id && @@license_key
|
107
|
+
end
|
108
|
+
|
109
|
+
# API url for searching for locations
|
110
|
+
def self.search_url(options = {})
|
111
|
+
"http://xoap.weather.com/search/search?where=#{URI.encode(options[:where])}"
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
class WeatherManResponse
|
4
|
+
attr_reader :current_conditions, :forecast, :api_url, :unit_temperature, :unit_distance, :unit_speed, :unit_pressure, :links
|
5
|
+
|
6
|
+
def initialize(simple_xml, url = nil)
|
7
|
+
@current_conditions = simple_xml['cc'] ? build_current_conditions(simple_xml['cc'][0]) : nil
|
8
|
+
@forecast = simple_xml['dayf'] ? build_forecast(simple_xml['dayf'][0]['day']) : nil
|
9
|
+
|
10
|
+
# Promotional links required by Weather Channel, Inc.
|
11
|
+
@links = simple_xml['lnks'] ? build_links(simple_xml['lnks'][0]['link']) : nil
|
12
|
+
|
13
|
+
# Capture the units
|
14
|
+
@unit_temperature = simple_xml['head'][0]['ut'][0]
|
15
|
+
@unit_distance = simple_xml['head'][0]['ud'][0]
|
16
|
+
@unit_speed = simple_xml['head'][0]['us'][0]
|
17
|
+
@unit_pressure = simple_xml['head'][0]['up'][0]
|
18
|
+
|
19
|
+
# The api url that was called to generate this response
|
20
|
+
@api_url = url
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
def build_current_conditions(response = {})
|
25
|
+
return nil if response.nil? || response.empty?
|
26
|
+
|
27
|
+
cc = WeatherManCurrentConditions.new
|
28
|
+
|
29
|
+
# Parse out Current Conditions
|
30
|
+
cc.temperature = response['tmp'][0]
|
31
|
+
cc.feels_like = response['flik'][0]
|
32
|
+
cc.description = response['t'][0]
|
33
|
+
cc.icon_code = response['icon'][0]
|
34
|
+
cc.humidity = response['hmid'][0]
|
35
|
+
cc.visibility = response['vis'][0]
|
36
|
+
cc.dew_point = response['dewp'][0]
|
37
|
+
cc.barometric_pressure = WeatherManBarometer.new({
|
38
|
+
:reading => response['bar'][0]['r'][0],
|
39
|
+
:description => response['bar'][0]['d'][0]
|
40
|
+
})
|
41
|
+
cc.wind = WeatherManWind.new({
|
42
|
+
:speed => response['wind'][0]['s'][0],
|
43
|
+
:gust => response['wind'][0]['gust'][0],
|
44
|
+
:degrees => response['wind'][0]['d'][0],
|
45
|
+
:direction => response['wind'][0]['t'][0]
|
46
|
+
})
|
47
|
+
cc.uv = WeatherManUV.new({
|
48
|
+
:index => response['uv'][0]['i'][0],
|
49
|
+
:description => response['uv'][0]['t'][0]
|
50
|
+
})
|
51
|
+
cc.moon = WeatherManMoon.new({
|
52
|
+
:icon_code => response['moon'][0]['icon'][0],
|
53
|
+
:description => response['moon'][0]['t'][0]
|
54
|
+
})
|
55
|
+
cc
|
56
|
+
end
|
57
|
+
|
58
|
+
def build_forecast(days = [])
|
59
|
+
return nil if days.nil? || days.empty?
|
60
|
+
|
61
|
+
f = WeatherManForecast.new
|
62
|
+
days.each do |day|
|
63
|
+
f << WeatherManForecastDay.build(day)
|
64
|
+
end
|
65
|
+
f
|
66
|
+
end
|
67
|
+
|
68
|
+
def build_links(links = [])
|
69
|
+
return nil if links.nil? || links.empty?
|
70
|
+
|
71
|
+
links.map {|link| WeatherManPromotionalLink.new({
|
72
|
+
:text => link['t'][0],
|
73
|
+
:url => link['l'][0]
|
74
|
+
})}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class WeatherManCurrentConditions
|
79
|
+
attr_accessor :temperature,
|
80
|
+
:feels_like,
|
81
|
+
:description,
|
82
|
+
:icon_code,
|
83
|
+
:humidity,
|
84
|
+
:visibility,
|
85
|
+
:dew_point,
|
86
|
+
:barometric_pressure,
|
87
|
+
:wind,
|
88
|
+
:uv,
|
89
|
+
:moon
|
90
|
+
end
|
91
|
+
|
92
|
+
class WeatherManForecast < Array
|
93
|
+
WEEK_DAYS = %w(sunday monday tuesday wednesday thursday friday saturday)
|
94
|
+
WEEK_DAYS.each {|day| attr_reader day.to_sym}
|
95
|
+
|
96
|
+
# Assign a forecast day to a week day accessor as it gets added
|
97
|
+
# allows for accessors like forecast.monday -> <WeatherManForecastDay>
|
98
|
+
def <<(day)
|
99
|
+
super
|
100
|
+
eval("@#{day.week_day.downcase} = day")
|
101
|
+
end
|
102
|
+
|
103
|
+
def today
|
104
|
+
self[0]
|
105
|
+
end
|
106
|
+
|
107
|
+
def tomorrow
|
108
|
+
self[1]
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns a forecast for a day given by a Date, DateTime,
|
112
|
+
# Time, or a string that can be parsed to a date
|
113
|
+
def for(date = Date.today)
|
114
|
+
# Format date into a Date class
|
115
|
+
date = case date.class.name
|
116
|
+
when 'String'
|
117
|
+
Date.parse(date)
|
118
|
+
when 'Date'
|
119
|
+
date
|
120
|
+
when 'DateTime'
|
121
|
+
Date.new(date.year, date.month, date.day)
|
122
|
+
when 'Time'
|
123
|
+
Date.new(date.year, date.month, date.day)
|
124
|
+
end
|
125
|
+
|
126
|
+
day = nil
|
127
|
+
# find the matching forecast day, if any
|
128
|
+
self.each do |fd|
|
129
|
+
day = fd if date == fd.date
|
130
|
+
end
|
131
|
+
return day
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class WeatherManForecastDay
|
136
|
+
attr_accessor :week_day,
|
137
|
+
:date,
|
138
|
+
:high,
|
139
|
+
:low,
|
140
|
+
:sunrise,
|
141
|
+
:sunset,
|
142
|
+
:day,
|
143
|
+
:night
|
144
|
+
|
145
|
+
# Build a new WeatherManForecastDay based on
|
146
|
+
# A response from the Weather Channel
|
147
|
+
def self.build(response = {})
|
148
|
+
fd = new
|
149
|
+
fd.week_day = response['t']
|
150
|
+
fd.date = Date.parse(response['dt'])
|
151
|
+
fd.high = response['hi'][0]
|
152
|
+
fd.low = response['low'][0]
|
153
|
+
fd.sunrise = response['sunr'][0]
|
154
|
+
fd.sunset = response['suns'][0]
|
155
|
+
fd.day = build_part(response['part'].first)
|
156
|
+
fd.night = build_part(response['part'].last)
|
157
|
+
fd
|
158
|
+
end
|
159
|
+
|
160
|
+
protected
|
161
|
+
# Build a part day
|
162
|
+
def self.build_part(part)
|
163
|
+
WeatherManForecastPart.new({
|
164
|
+
:icon_code => part['icon'][0],
|
165
|
+
:description => part['t'][0],
|
166
|
+
:chance_percipitation => part['ppcp'][0],
|
167
|
+
:humidity => part['hmid'][0],
|
168
|
+
:wind => WeatherManWind.new({
|
169
|
+
:speed => part['wind'][0]['s'][0],
|
170
|
+
:gust => part['wind'][0]['gust'][0],
|
171
|
+
:degrees => part['wind'][0]['d'][0],
|
172
|
+
:direction => part['wind'][0]['t'][0]
|
173
|
+
})
|
174
|
+
})
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# =================================
|
179
|
+
# WeatherMan Response classes
|
180
|
+
# used for tracking groups of data
|
181
|
+
# ie. Forecast parts, Barometer,
|
182
|
+
# UV, Moon, and Wind
|
183
|
+
# =================================
|
184
|
+
class WeatherManForecastPart < OpenStruct
|
185
|
+
end
|
186
|
+
|
187
|
+
class WeatherManBarometer < OpenStruct
|
188
|
+
end
|
189
|
+
|
190
|
+
class WeatherManUV < OpenStruct
|
191
|
+
end
|
192
|
+
|
193
|
+
class WeatherManMoon < OpenStruct
|
194
|
+
end
|
195
|
+
|
196
|
+
class WeatherManWind < OpenStruct
|
197
|
+
end
|
198
|
+
|
199
|
+
class WeatherManPromotionalLink < OpenStruct
|
200
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
<!-- This document is intended only for use by authorized licensees of The -->
|
3
|
+
<!-- Weather Channel. Unauthorized use is prohibited. Copyright 1995-2008, -->
|
4
|
+
<!-- The Weather Channel Interactive, Inc. All Rights Reserved. -->
|
5
|
+
<weather ver="2.0">
|
6
|
+
<head>
|
7
|
+
<locale>en_US</locale>
|
8
|
+
<form>MEDIUM</form>
|
9
|
+
<ut>F</ut>
|
10
|
+
<ud>mi</ud>
|
11
|
+
<us>mph</us>
|
12
|
+
<up>in</up>
|
13
|
+
<ur>in</ur>
|
14
|
+
</head>
|
15
|
+
<loc id="28277">
|
16
|
+
<dnam>Charlotte, NC (28277)</dnam>
|
17
|
+
<tm>8:34 PM</tm>
|
18
|
+
<lat>35.06</lat>
|
19
|
+
<lon>-80.81</lon>
|
20
|
+
<sunr>7:18 AM</sunr>
|
21
|
+
<suns>7:08 PM</suns>
|
22
|
+
<zone>-4</zone>
|
23
|
+
</loc>
|
24
|
+
|
25
|
+
<lnks type="prmo">
|
26
|
+
<link pos="1">
|
27
|
+
<l>http://www.weather.com/allergies?par=xoap&site=textlink&cm_ven=XOAP&cm_cat=TextLink&cm_pla=Link1&cm_ite=Allergies</l>
|
28
|
+
<t>Local Pollen Reports</t>
|
29
|
+
</link>
|
30
|
+
<link pos="2">
|
31
|
+
<l>http://www.weather.com/flights?par=xoap&site=textlink&cm_ven=XOAP&cm_cat=TextLink&cm_pla=Link2&cm_ite=BusinessTraveler</l>
|
32
|
+
<t>Airport Conditions</t>
|
33
|
+
</link>
|
34
|
+
<link pos="3">
|
35
|
+
<l>http://www.weather.com/garden?par=xoap&site=textlink&cm_ven=XOAP&cm_cat=TextLink&cm_pla=Link3&cm_ite=Garden</l>
|
36
|
+
<t>Lawn and Garden Weather</t>
|
37
|
+
</link>
|
38
|
+
<link pos="4">
|
39
|
+
<l>http://www.weather.com/traffic?par=xoap&site=textlink&cm_ven=XOAP&cm_cat=TextLink&cm_pla=Link4&cm_ite=Traffic</l>
|
40
|
+
<t>Rush Hour Traffic</t>
|
41
|
+
</link>
|
42
|
+
</lnks>
|
43
|
+
<cc>
|
44
|
+
<lsup>9/29/08 8:25 PM EDT</lsup>
|
45
|
+
<obst>Matthews, NC</obst>
|
46
|
+
<tmp>69</tmp>
|
47
|
+
<flik>69</flik>
|
48
|
+
<t>Clear</t>
|
49
|
+
<icon>31</icon>
|
50
|
+
<bar>
|
51
|
+
<r>29.97</r>
|
52
|
+
<d>steady</d>
|
53
|
+
</bar>
|
54
|
+
<wind>
|
55
|
+
<s>1</s>
|
56
|
+
<gust>N/A</gust>
|
57
|
+
<d>30</d>
|
58
|
+
<t>NNE</t>
|
59
|
+
</wind>
|
60
|
+
<hmid>78</hmid>
|
61
|
+
<vis>9.0</vis>
|
62
|
+
<uv>
|
63
|
+
<i>0</i>
|
64
|
+
<t>Low</t>
|
65
|
+
</uv>
|
66
|
+
<dewp>62</dewp>
|
67
|
+
<moon>
|
68
|
+
<icon>0</icon>
|
69
|
+
<t>New</t>
|
70
|
+
</moon>
|
71
|
+
</cc>
|
72
|
+
</weather>
|
@@ -0,0 +1,234 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
<!-- This document is intended only for use by authorized licensees of The -->
|
3
|
+
<!-- Weather Channel. Unauthorized use is prohibited. Copyright 1995-2008, -->
|
4
|
+
<!-- The Weather Channel Interactive, Inc. All Rights Reserved. -->
|
5
|
+
<weather ver="2.0">
|
6
|
+
<head>
|
7
|
+
<locale>en_US</locale>
|
8
|
+
<form>MEDIUM</form>
|
9
|
+
<ut>F</ut>
|
10
|
+
<ud>mi</ud>
|
11
|
+
<us>mph</us>
|
12
|
+
<up>in</up>
|
13
|
+
<ur>in</ur>
|
14
|
+
</head>
|
15
|
+
<loc id="28277">
|
16
|
+
<dnam>Charlotte, NC (28277)</dnam>
|
17
|
+
<tm>7:17 PM</tm>
|
18
|
+
<lat>35.06</lat>
|
19
|
+
<lon>-80.81</lon>
|
20
|
+
<sunr>7:17 AM</sunr>
|
21
|
+
<suns>7:09 PM</suns>
|
22
|
+
<zone>-4</zone>
|
23
|
+
</loc>
|
24
|
+
<lnks type="prmo">
|
25
|
+
<link pos="1">
|
26
|
+
<l>http://www.weather.com/allergies?par=xoap&site=textlink&cm_ven=XOAP&cm_cat=TextLink&cm_pla=Link1&cm_ite=Allergies</l>
|
27
|
+
<t>Local Pollen Reports</t>
|
28
|
+
</link>
|
29
|
+
<link pos="2">
|
30
|
+
<l>http://www.weather.com/flights?par=xoap&site=textlink&cm_ven=XOAP&cm_cat=TextLink&cm_pla=Link2&cm_ite=BusinessTraveler</l>
|
31
|
+
<t>Airport Conditions</t>
|
32
|
+
</link>
|
33
|
+
<link pos="3">
|
34
|
+
<l>http://www.weather.com/garden?par=xoap&site=textlink&cm_ven=XOAP&cm_cat=TextLink&cm_pla=Link3&cm_ite=Garden</l>
|
35
|
+
<t>Lawn and Garden Weather</t>
|
36
|
+
</link>
|
37
|
+
<link pos="4">
|
38
|
+
<l>http://www.weather.com/traffic?par=xoap&site=textlink&cm_ven=XOAP&cm_cat=TextLink&cm_pla=Link4&cm_ite=Traffic</l>
|
39
|
+
<t>Rush Hour Traffic</t>
|
40
|
+
</link>
|
41
|
+
</lnks>
|
42
|
+
<cc>
|
43
|
+
<lsup>9/29/08 7:05 PM EDT</lsup>
|
44
|
+
<obst>Matthews, NC</obst>
|
45
|
+
<tmp>73</tmp>
|
46
|
+
<flik>73</flik>
|
47
|
+
<t>Sunny</t>
|
48
|
+
<icon>32</icon>
|
49
|
+
<bar>
|
50
|
+
<r>29.97</r>
|
51
|
+
<d>steady</d>
|
52
|
+
</bar>
|
53
|
+
<wind>
|
54
|
+
<s>calm</s>
|
55
|
+
<gust>N/A</gust>
|
56
|
+
<d>0</d>
|
57
|
+
<t>CALM</t>
|
58
|
+
</wind>
|
59
|
+
<hmid>66</hmid>
|
60
|
+
<vis>10.0</vis>
|
61
|
+
<uv>
|
62
|
+
<i>0</i>
|
63
|
+
<t>Low</t>
|
64
|
+
</uv>
|
65
|
+
<dewp>61</dewp>
|
66
|
+
<moon>
|
67
|
+
<icon>0</icon>
|
68
|
+
<t>New</t>
|
69
|
+
</moon>
|
70
|
+
</cc>
|
71
|
+
<dayf>
|
72
|
+
<lsup>9/29/08 6:16 PM EDT</lsup>
|
73
|
+
<day d="0" t="Monday" dt="Sep 29">
|
74
|
+
<hi>N/A</hi>
|
75
|
+
<low>59</low>
|
76
|
+
<sunr>7:17 AM</sunr>
|
77
|
+
<suns>7:09 PM</suns>
|
78
|
+
<part p="d">
|
79
|
+
<icon>44</icon>
|
80
|
+
<t>N/A</t>
|
81
|
+
<wind>
|
82
|
+
<s>N/A</s>
|
83
|
+
<gust>N/A</gust>
|
84
|
+
<d>N/A</d>
|
85
|
+
<t>N/A</t>
|
86
|
+
</wind>
|
87
|
+
<bt>N/A</bt>
|
88
|
+
<ppcp>10</ppcp>
|
89
|
+
<hmid>N/A</hmid>
|
90
|
+
</part>
|
91
|
+
<part p="n">
|
92
|
+
<icon>29</icon>
|
93
|
+
<t>Partly Cloudy</t>
|
94
|
+
<wind>
|
95
|
+
<s>3</s>
|
96
|
+
<gust>N/A</gust>
|
97
|
+
<d>56</d>
|
98
|
+
<t>NE</t>
|
99
|
+
</wind>
|
100
|
+
<bt>P Cloudy</bt>
|
101
|
+
<ppcp>10</ppcp>
|
102
|
+
<hmid>86</hmid>
|
103
|
+
</part>
|
104
|
+
</day>
|
105
|
+
<day d="1" t="Tuesday" dt="Sep 30">
|
106
|
+
<hi>82</hi>
|
107
|
+
<low>59</low>
|
108
|
+
<sunr>7:18 AM</sunr>
|
109
|
+
<suns>7:08 PM</suns>
|
110
|
+
<part p="d">
|
111
|
+
<icon>34</icon>
|
112
|
+
<t>Mostly Sunny</t>
|
113
|
+
<wind>
|
114
|
+
<s>7</s>
|
115
|
+
<gust>N/A</gust>
|
116
|
+
<d>267</d>
|
117
|
+
<t>W</t>
|
118
|
+
</wind>
|
119
|
+
<bt>M Sunny</bt>
|
120
|
+
<ppcp>20</ppcp>
|
121
|
+
<hmid>69</hmid>
|
122
|
+
</part>
|
123
|
+
<part p="n">
|
124
|
+
<icon>47</icon>
|
125
|
+
<t>Isolated T-Storms</t>
|
126
|
+
<wind>
|
127
|
+
<s>5</s>
|
128
|
+
<gust>N/A</gust>
|
129
|
+
<d>223</d>
|
130
|
+
<t>SW</t>
|
131
|
+
</wind>
|
132
|
+
<bt>Iso T-Storms</bt>
|
133
|
+
<ppcp>30</ppcp>
|
134
|
+
<hmid>83</hmid>
|
135
|
+
</part>
|
136
|
+
</day>
|
137
|
+
<day d="2" t="Wednesday" dt="Oct 1">
|
138
|
+
<hi>74</hi>
|
139
|
+
<low>51</low>
|
140
|
+
<sunr>7:19 AM</sunr>
|
141
|
+
<suns>7:07 PM</suns>
|
142
|
+
<part p="d">
|
143
|
+
<icon>34</icon>
|
144
|
+
<t>Mostly Sunny</t>
|
145
|
+
<wind>
|
146
|
+
<s>9</s>
|
147
|
+
<gust>N/A</gust>
|
148
|
+
<d>309</d>
|
149
|
+
<t>NW</t>
|
150
|
+
</wind>
|
151
|
+
<bt>M Sunny</bt>
|
152
|
+
<ppcp>20</ppcp>
|
153
|
+
<hmid>63</hmid>
|
154
|
+
</part>
|
155
|
+
<part p="n">
|
156
|
+
<icon>31</icon>
|
157
|
+
<t>Clear</t>
|
158
|
+
<wind>
|
159
|
+
<s>5</s>
|
160
|
+
<gust>N/A</gust>
|
161
|
+
<d>328</d>
|
162
|
+
<t>NNW</t>
|
163
|
+
</wind>
|
164
|
+
<bt>Clear</bt>
|
165
|
+
<ppcp>20</ppcp>
|
166
|
+
<hmid>71</hmid>
|
167
|
+
</part>
|
168
|
+
</day>
|
169
|
+
<day d="3" t="Thursday" dt="Oct 2">
|
170
|
+
<hi>69</hi>
|
171
|
+
<low>47</low>
|
172
|
+
<sunr>7:19 AM</sunr>
|
173
|
+
<suns>7:05 PM</suns>
|
174
|
+
<part p="d">
|
175
|
+
<icon>32</icon>
|
176
|
+
<t>Sunny</t>
|
177
|
+
<wind>
|
178
|
+
<s>4</s>
|
179
|
+
<gust>N/A</gust>
|
180
|
+
<d>316</d>
|
181
|
+
<t>NW</t>
|
182
|
+
</wind>
|
183
|
+
<bt>Sunny</bt>
|
184
|
+
<ppcp>20</ppcp>
|
185
|
+
<hmid>63</hmid>
|
186
|
+
</part>
|
187
|
+
<part p="n">
|
188
|
+
<icon>31</icon>
|
189
|
+
<t>Clear</t>
|
190
|
+
<wind>
|
191
|
+
<s>1</s>
|
192
|
+
<gust>N/A</gust>
|
193
|
+
<d>338</d>
|
194
|
+
<t>NNW</t>
|
195
|
+
</wind>
|
196
|
+
<bt>Clear</bt>
|
197
|
+
<ppcp>20</ppcp>
|
198
|
+
<hmid>76</hmid>
|
199
|
+
</part>
|
200
|
+
</day>
|
201
|
+
<day d="4" t="Friday" dt="Oct 3">
|
202
|
+
<hi>70</hi>
|
203
|
+
<low>50</low>
|
204
|
+
<sunr>7:20 AM</sunr>
|
205
|
+
<suns>7:04 PM</suns>
|
206
|
+
<part p="d">
|
207
|
+
<icon>32</icon>
|
208
|
+
<t>Sunny</t>
|
209
|
+
<wind>
|
210
|
+
<s>3</s>
|
211
|
+
<gust>N/A</gust>
|
212
|
+
<d>303</d>
|
213
|
+
<t>WNW</t>
|
214
|
+
</wind>
|
215
|
+
<bt>Sunny</bt>
|
216
|
+
<ppcp>20</ppcp>
|
217
|
+
<hmid>66</hmid>
|
218
|
+
</part>
|
219
|
+
<part p="n">
|
220
|
+
<icon>31</icon>
|
221
|
+
<t>Clear</t>
|
222
|
+
<wind>
|
223
|
+
<s>1</s>
|
224
|
+
<gust>N/A</gust>
|
225
|
+
<d>310</d>
|
226
|
+
<t>NW</t>
|
227
|
+
</wind>
|
228
|
+
<bt>Clear</bt>
|
229
|
+
<ppcp>10</ppcp>
|
230
|
+
<hmid>79</hmid>
|
231
|
+
</part>
|
232
|
+
</day>
|
233
|
+
</dayf>
|
234
|
+
</weather>
|
@@ -0,0 +1,142 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
<!-- This document is intended only for use by authorized licensees of The -->
|
3
|
+
<!-- Weather Channel. Unauthorized use is prohibited. Copyright 1995-2008, -->
|
4
|
+
<!-- The Weather Channel Interactive, Inc. All Rights Reserved. -->
|
5
|
+
<weather ver="2.0">
|
6
|
+
<head>
|
7
|
+
<locale>en_US</locale>
|
8
|
+
<form>MEDIUM</form>
|
9
|
+
<ut>F</ut>
|
10
|
+
<ud>mi</ud>
|
11
|
+
<us>mph</us>
|
12
|
+
<up>in</up>
|
13
|
+
<ur>in</ur>
|
14
|
+
</head>
|
15
|
+
<loc id="28277">
|
16
|
+
<dnam>Charlotte, NC (28277)</dnam>
|
17
|
+
<tm>8:40 PM</tm>
|
18
|
+
<lat>35.06</lat>
|
19
|
+
<lon>-80.81</lon>
|
20
|
+
<sunr>7:18 AM</sunr>
|
21
|
+
<suns>7:08 PM</suns>
|
22
|
+
<zone>-4</zone>
|
23
|
+
</loc>
|
24
|
+
<lnks type="prmo">
|
25
|
+
<link pos="1">
|
26
|
+
<l>http://www.weather.com/allergies?par=xoap&site=textlink&cm_ven=XOAP&cm_cat=TextLink&cm_pla=Link1&cm_ite=Allergies</l>
|
27
|
+
<t>Local Pollen Reports</t>
|
28
|
+
</link>
|
29
|
+
<link pos="2">
|
30
|
+
<l>http://www.weather.com/flights?par=xoap&site=textlink&cm_ven=XOAP&cm_cat=TextLink&cm_pla=Link2&cm_ite=BusinessTraveler</l>
|
31
|
+
<t>Airport Conditions</t>
|
32
|
+
</link>
|
33
|
+
<link pos="3">
|
34
|
+
<l>http://www.weather.com/garden?par=xoap&site=textlink&cm_ven=XOAP&cm_cat=TextLink&cm_pla=Link3&cm_ite=Garden</l>
|
35
|
+
<t>Lawn and Garden Weather</t>
|
36
|
+
</link>
|
37
|
+
<link pos="4">
|
38
|
+
<l>http://www.weather.com/traffic?par=xoap&site=textlink&cm_ven=XOAP&cm_cat=TextLink&cm_pla=Link4&cm_ite=Traffic</l>
|
39
|
+
<t>Rush Hour Traffic</t>
|
40
|
+
</link>
|
41
|
+
</lnks>
|
42
|
+
<dayf>
|
43
|
+
<lsup>9/29/08 6:16 PM EDT</lsup>
|
44
|
+
<day d="0" t="Monday" dt="Sep 29">
|
45
|
+
<hi>N/A</hi>
|
46
|
+
<low>59</low>
|
47
|
+
<sunr>7:17 AM</sunr>
|
48
|
+
<suns>7:09 PM</suns>
|
49
|
+
<part p="d">
|
50
|
+
<icon>44</icon>
|
51
|
+
<t>N/A</t>
|
52
|
+
<wind>
|
53
|
+
<s>N/A</s>
|
54
|
+
<gust>N/A</gust>
|
55
|
+
<d>N/A</d>
|
56
|
+
<t>N/A</t>
|
57
|
+
</wind>
|
58
|
+
<bt>N/A</bt>
|
59
|
+
<ppcp>10</ppcp>
|
60
|
+
<hmid>N/A</hmid>
|
61
|
+
</part>
|
62
|
+
<part p="n">
|
63
|
+
<icon>29</icon>
|
64
|
+
<t>Partly Cloudy</t>
|
65
|
+
<wind>
|
66
|
+
<s>3</s>
|
67
|
+
<gust>N/A</gust>
|
68
|
+
<d>56</d>
|
69
|
+
<t>NE</t>
|
70
|
+
</wind>
|
71
|
+
<bt>P Cloudy</bt>
|
72
|
+
<ppcp>10</ppcp>
|
73
|
+
<hmid>86</hmid>
|
74
|
+
</part>
|
75
|
+
</day>
|
76
|
+
<day d="1" t="Tuesday" dt="Sep 30">
|
77
|
+
<hi>82</hi>
|
78
|
+
<low>59</low>
|
79
|
+
<sunr>7:18 AM</sunr>
|
80
|
+
<suns>7:08 PM</suns>
|
81
|
+
<part p="d">
|
82
|
+
<icon>34</icon>
|
83
|
+
<t>Mostly Sunny</t>
|
84
|
+
<wind>
|
85
|
+
<s>7</s>
|
86
|
+
<gust>N/A</gust>
|
87
|
+
<d>267</d>
|
88
|
+
<t>W</t>
|
89
|
+
</wind>
|
90
|
+
<bt>M Sunny</bt>
|
91
|
+
<ppcp>20</ppcp>
|
92
|
+
<hmid>69</hmid>
|
93
|
+
</part>
|
94
|
+
<part p="n">
|
95
|
+
<icon>47</icon>
|
96
|
+
<t>Isolated T-Storms</t>
|
97
|
+
<wind>
|
98
|
+
<s>5</s>
|
99
|
+
<gust>N/A</gust>
|
100
|
+
<d>223</d>
|
101
|
+
<t>SW</t>
|
102
|
+
</wind>
|
103
|
+
<bt>Iso T-Storms</bt>
|
104
|
+
<ppcp>30</ppcp>
|
105
|
+
<hmid>83</hmid>
|
106
|
+
</part>
|
107
|
+
</day>
|
108
|
+
<day d="2" t="Wednesday" dt="Oct 1">
|
109
|
+
<hi>74</hi>
|
110
|
+
<low>51</low>
|
111
|
+
<sunr>7:19 AM</sunr>
|
112
|
+
<suns>7:07 PM</suns>
|
113
|
+
<part p="d">
|
114
|
+
<icon>34</icon>
|
115
|
+
<t>Mostly Sunny</t>
|
116
|
+
<wind>
|
117
|
+
<s>9</s>
|
118
|
+
<gust>N/A</gust>
|
119
|
+
<d>309</d>
|
120
|
+
<t>NW</t>
|
121
|
+
</wind>
|
122
|
+
<bt>M Sunny</bt>
|
123
|
+
<ppcp>20</ppcp>
|
124
|
+
<hmid>63</hmid>
|
125
|
+
</part>
|
126
|
+
<part p="n">
|
127
|
+
<icon>31</icon>
|
128
|
+
<t>Clear</t>
|
129
|
+
<wind>
|
130
|
+
<s>5</s>
|
131
|
+
<gust>N/A</gust>
|
132
|
+
|
133
|
+
<d>328</d>
|
134
|
+
<t>NNW</t>
|
135
|
+
</wind>
|
136
|
+
<bt>Clear</bt>
|
137
|
+
<ppcp>20</ppcp>
|
138
|
+
<hmid>71</hmid>
|
139
|
+
</part>
|
140
|
+
</day>
|
141
|
+
</dayf>
|
142
|
+
</weather>
|
@@ -0,0 +1,190 @@
|
|
1
|
+
$: << '../lib'
|
2
|
+
require 'weather_man_response'
|
3
|
+
require 'xmlsimple'
|
4
|
+
|
5
|
+
describe WeatherManResponse, 'built from a default response' do
|
6
|
+
before :each do
|
7
|
+
default_response = XmlSimple.xml_in(File.read('default_response.xml'))
|
8
|
+
@weather = WeatherManResponse.new(default_response, 'test')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should grab all the units' do
|
12
|
+
@weather.unit_temperature.should eql('F')
|
13
|
+
@weather.unit_distance.should eql('mi')
|
14
|
+
@weather.unit_speed.should eql('mph')
|
15
|
+
@weather.unit_pressure.should eql('in')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should build a CurrentConditions object' do
|
19
|
+
@weather.current_conditions.should be_kind_of(WeatherManCurrentConditions)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should build the current conditions correctly' do
|
23
|
+
cc = @weather.current_conditions
|
24
|
+
cc.temperature.should eql('73')
|
25
|
+
cc.feels_like.should eql('73')
|
26
|
+
cc.description.should eql('Sunny')
|
27
|
+
cc.icon_code.should eql('32')
|
28
|
+
cc.humidity.should eql('66')
|
29
|
+
cc.visibility.should eql('10.0')
|
30
|
+
cc.dew_point.should eql('61')
|
31
|
+
|
32
|
+
# Barometric Pressure
|
33
|
+
cc.barometric_pressure.should be_kind_of(WeatherManBarometer)
|
34
|
+
cc.barometric_pressure.reading.should eql('29.97')
|
35
|
+
cc.barometric_pressure.description.should eql('steady')
|
36
|
+
|
37
|
+
# Wind
|
38
|
+
cc.wind.should be_kind_of(WeatherManWind)
|
39
|
+
cc.wind.speed.should eql('calm')
|
40
|
+
cc.wind.gust.should eql('N/A')
|
41
|
+
cc.wind.degrees.should eql('0')
|
42
|
+
cc.wind.direction.should eql('CALM')
|
43
|
+
|
44
|
+
# UV
|
45
|
+
cc.uv.should be_kind_of(WeatherManUV)
|
46
|
+
cc.uv.index.should eql('0')
|
47
|
+
cc.uv.description.should eql('Low')
|
48
|
+
|
49
|
+
# Moon
|
50
|
+
cc.moon.should be_kind_of(WeatherManMoon)
|
51
|
+
cc.moon.icon_code.should eql('0')
|
52
|
+
cc.moon.description.should eql('New')
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should build a forecast' do
|
56
|
+
@weather.forecast.should be_kind_of(WeatherManForecast)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should have a forecast for 5 days' do
|
60
|
+
@weather.forecast.size.should eql(5)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should build a correct forecast for today' do
|
64
|
+
today = @weather.forecast.today
|
65
|
+
today.should be_kind_of(WeatherManForecastDay)
|
66
|
+
|
67
|
+
today.week_day.should eql('Monday')
|
68
|
+
today.date.should eql(Date.parse('Sep 29'))
|
69
|
+
today.high.should eql('N/A')
|
70
|
+
today.low.should eql('59')
|
71
|
+
today.sunrise.should eql('7:17 AM')
|
72
|
+
today.sunset.should eql('7:09 PM')
|
73
|
+
|
74
|
+
# Day time part
|
75
|
+
today.day.should be_kind_of(WeatherManForecastPart)
|
76
|
+
today.day.icon_code.should eql('44')
|
77
|
+
today.day.description.should eql('N/A')
|
78
|
+
today.day.chance_percipitation.should eql('10')
|
79
|
+
today.day.humidity.should eql('N/A')
|
80
|
+
today.day.wind.should be_kind_of(WeatherManWind)
|
81
|
+
today.day.wind.speed.should eql('N/A')
|
82
|
+
today.day.wind.gust.should eql('N/A')
|
83
|
+
today.day.wind.degrees.should eql('N/A')
|
84
|
+
today.day.wind.direction.should eql('N/A')
|
85
|
+
|
86
|
+
# Nite time part
|
87
|
+
today.night.should be_kind_of(WeatherManForecastPart)
|
88
|
+
today.night.icon_code.should eql('29')
|
89
|
+
today.night.description.should eql('Partly Cloudy')
|
90
|
+
today.night.chance_percipitation.should eql('10')
|
91
|
+
today.night.humidity.should eql('86')
|
92
|
+
today.night.wind.should be_kind_of(WeatherManWind)
|
93
|
+
today.night.wind.speed.should eql('3')
|
94
|
+
today.night.wind.gust.should eql('N/A')
|
95
|
+
today.night.wind.degrees.should eql('56')
|
96
|
+
today.night.wind.direction.should eql('NE')
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should get a set of promotional links' do
|
100
|
+
@weather.links.should_not be_empty
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should get exactly 4 links' do
|
104
|
+
@weather.links.size.should eql(4)
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should have a set of links that are each objects' do
|
108
|
+
@weather.links.each do |link|
|
109
|
+
link.should be_kind_of(WeatherManPromotionalLink)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should build the promotional links correctly' do
|
114
|
+
link = @weather.links.first
|
115
|
+
link.text.should eql('Local Pollen Reports')
|
116
|
+
link.url.should eql('http://www.weather.com/allergies?par=xoap&site=textlink&cm_ven=XOAP&cm_cat=TextLink&cm_pla=Link1&cm_ite=Allergies')
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe WeatherManResponse, 'with only the current conditions' do
|
121
|
+
before :each do
|
122
|
+
cc_only_response = XmlSimple.xml_in(File.read('cc_only_response.xml'))
|
123
|
+
@weather = WeatherManResponse.new(cc_only_response, 'test')
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should have a current_conditions object' do
|
127
|
+
@weather.current_conditions.should be_kind_of(WeatherManCurrentConditions)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should not have a forecast' do
|
131
|
+
@weather.forecast.should be_nil
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe WeatherManResponse, 'with only a 3 day forecast' do
|
136
|
+
before :each do
|
137
|
+
forecast_only_response = XmlSimple.xml_in(File.read('forecast_only_response.xml'))
|
138
|
+
@weather = WeatherManResponse.new(forecast_only_response, 'test')
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'should not have a current_conditions object' do
|
142
|
+
@weather.current_conditions.should be_nil
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should have a forecast' do
|
146
|
+
@weather.forecast.should be_kind_of(WeatherManForecast)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should have a forecast for exactly 3 days' do
|
150
|
+
@weather.forecast.size.should eql(3)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe WeatherManForecast, 'generated from a default response' do
|
155
|
+
before :each do
|
156
|
+
default_response = XmlSimple.xml_in(File.read('default_response.xml'))
|
157
|
+
@forecast = WeatherManResponse.new(default_response, 'test').forecast
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'should have some week day accessors' do
|
161
|
+
@forecast.monday.should be_kind_of(WeatherManForecastDay)
|
162
|
+
@forecast.tuesday.should be_kind_of(WeatherManForecastDay)
|
163
|
+
@forecast.wednesday.should be_kind_of(WeatherManForecastDay)
|
164
|
+
@forecast.thursday.should be_kind_of(WeatherManForecastDay)
|
165
|
+
@forecast.friday.should be_kind_of(WeatherManForecastDay)
|
166
|
+
@forecast.saturday.should be_nil
|
167
|
+
@forecast.sunday.should be_nil
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'should have some relative date helpers' do
|
171
|
+
@forecast.today.should eql(@forecast.first)
|
172
|
+
@forecast.tomorrow.should eql(@forecast[1])
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'should be able to get the forecast given a date' do
|
176
|
+
@forecast.for(Date.parse('Sep 29')).should eql(@forecast.today)
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should be able to get the forecast given a time' do
|
180
|
+
@forecast.for(Time.at(1222750106)).should eql(@forecast.tomorrow)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'should be able to get the forecast given a string representing a date' do
|
184
|
+
@forecast.for('Oct 1').should eql(@forecast.wednesday)
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'should return nil when asked for the forecast of a date it doesnt have' do
|
188
|
+
@forecast.for(Date.new(2050,1,1)).should be_nil
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
$: << '../lib'
|
2
|
+
require 'weather_man'
|
3
|
+
|
4
|
+
describe WeatherMan, 'trying to access the api before being configured' do
|
5
|
+
it 'should throw an error when searching' do
|
6
|
+
lambda {
|
7
|
+
WeatherMan.search('test')
|
8
|
+
}.should raise_error(WeatherManNotConfiguredError)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should throw an error when initializing' do
|
12
|
+
lambda {
|
13
|
+
WeatherMan.new('28115')
|
14
|
+
}.should raise_error(WeatherManNotConfiguredError)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe WeatherMan, 'dealing with locations' do
|
19
|
+
before :each do
|
20
|
+
load_default_authentication
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should be able to search for locations' do
|
24
|
+
WeatherMan.search('Charlotte').should_not be_empty
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should return an array of instances when searching' do
|
28
|
+
WeatherMan.search('Charlotte').each do |location|
|
29
|
+
location.should be_kind_of(WeatherMan)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should be be able to able to initialize from a location id' do
|
34
|
+
WeatherMan.new('28115').should_not be_nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe WeatherMan, 'using a bad partner id / license key' do
|
39
|
+
before :each do
|
40
|
+
WeatherMan.partner_id = 'test'
|
41
|
+
WeatherMan.license_key = 'test'
|
42
|
+
@weatherman = WeatherMan.new('28115')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should raise an error when fetching the weather' do
|
46
|
+
lambda {
|
47
|
+
@weatherman.fetch
|
48
|
+
}.should raise_error(WeatherManApiError)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe WeatherMan, 'trying to use a bad location id' do
|
53
|
+
before :each do
|
54
|
+
load_default_authentication
|
55
|
+
@weatherman = WeatherMan.new('test')
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should raise an error when fetching the weather' do
|
59
|
+
lambda {
|
60
|
+
@weatherman.fetch
|
61
|
+
}.should raise_error(WeatherManApiError)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe WeatherMan, 'fetching the weather' do
|
66
|
+
before :each do
|
67
|
+
load_default_authentication
|
68
|
+
@weather = WeatherMan.new('28277').fetch
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should get the current conditions' do
|
72
|
+
@weather.current_conditions.should_not be_nil
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should get a forecast' do
|
76
|
+
@weather.forecast.should_not be_nil
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should get a forecast of 5 days by default' do
|
80
|
+
@weather.forecast.size.should eql(5)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should get the weather in standard units by default' do
|
84
|
+
@weather.unit_distance.should eql('mi')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe WeatherMan, 'asking for different kinds of weather' do
|
89
|
+
before :each do
|
90
|
+
load_default_authentication
|
91
|
+
@charlotte = WeatherMan.new('28277')
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should only not get the forecast when asking for 0 forecast days' do
|
95
|
+
weather = @charlotte.fetch(:days => 0)
|
96
|
+
weather.forecast.should be_nil
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should only get the amount of days you ask for' do
|
100
|
+
weather = @charlotte.fetch(:days => 3)
|
101
|
+
weather.forecast.size.should eql(3)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should not get the current conditions if you tell it not to' do
|
105
|
+
weather = @charlotte.fetch(:current_conditions => false)
|
106
|
+
weather.current_conditions.should be_nil
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should get the weather in metric when ask for it that way' do
|
110
|
+
weather = @charlotte.fetch(:unit => :metric)
|
111
|
+
weather.unit_distance.should eql('km')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def load_default_authentication
|
116
|
+
# Hey! Get your own.
|
117
|
+
# heres the link: http://www.weather.com/services/xmloap.html
|
118
|
+
WeatherMan.partner_id = '1075758518'
|
119
|
+
WeatherMan.license_key = '7c731d27fae916fb'
|
120
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jdpace-weatherman
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jared Pace
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-09-29 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: xml-simple
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.0.11
|
23
|
+
version:
|
24
|
+
description: A wrapper for the Weather Channel, inc (weather.com) XML api covers most features of the api. Current Conditions, Forecasting, and access to the promotional links that you are required to display as part of the API TOS.
|
25
|
+
email: jared@codewordstudios.com
|
26
|
+
executables: []
|
27
|
+
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files:
|
31
|
+
- README.rdoc
|
32
|
+
files:
|
33
|
+
- lib/weather_man.rb
|
34
|
+
- lib/weather_man_response.rb
|
35
|
+
- spec/cc_only_response.xml
|
36
|
+
- spec/default_response.xml
|
37
|
+
- spec/forecast_only_response.xml
|
38
|
+
- spec/weather_man_response_spec.rb
|
39
|
+
- spec/weather_man_spec.rb
|
40
|
+
- README.rdoc
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://github.com/jdpace/weatherman
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options:
|
45
|
+
- --main
|
46
|
+
- README.rdoc
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.2.0
|
65
|
+
signing_key:
|
66
|
+
specification_version: 2
|
67
|
+
summary: Ruby gem for accessing the Weather Channel XML API based on rweather.
|
68
|
+
test_files: []
|
69
|
+
|