jdpace-weatherman 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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&amp;site=textlink&amp;cm_ven=XOAP&amp;cm_cat=TextLink&amp;cm_pla=Link1&amp;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&amp;site=textlink&amp;cm_ven=XOAP&amp;cm_cat=TextLink&amp;cm_pla=Link2&amp;cm_ite=BusinessTraveler</l>
32
+ <t>Airport Conditions</t>
33
+ </link>
34
+ <link pos="3">
35
+ <l>http://www.weather.com/garden?par=xoap&amp;site=textlink&amp;cm_ven=XOAP&amp;cm_cat=TextLink&amp;cm_pla=Link3&amp;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&amp;site=textlink&amp;cm_ven=XOAP&amp;cm_cat=TextLink&amp;cm_pla=Link4&amp;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&amp;site=textlink&amp;cm_ven=XOAP&amp;cm_cat=TextLink&amp;cm_pla=Link1&amp;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&amp;site=textlink&amp;cm_ven=XOAP&amp;cm_cat=TextLink&amp;cm_pla=Link2&amp;cm_ite=BusinessTraveler</l>
31
+ <t>Airport Conditions</t>
32
+ </link>
33
+ <link pos="3">
34
+ <l>http://www.weather.com/garden?par=xoap&amp;site=textlink&amp;cm_ven=XOAP&amp;cm_cat=TextLink&amp;cm_pla=Link3&amp;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&amp;site=textlink&amp;cm_ven=XOAP&amp;cm_cat=TextLink&amp;cm_pla=Link4&amp;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&amp;site=textlink&amp;cm_ven=XOAP&amp;cm_cat=TextLink&amp;cm_pla=Link1&amp;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&amp;site=textlink&amp;cm_ven=XOAP&amp;cm_cat=TextLink&amp;cm_pla=Link2&amp;cm_ite=BusinessTraveler</l>
31
+ <t>Airport Conditions</t>
32
+ </link>
33
+ <link pos="3">
34
+ <l>http://www.weather.com/garden?par=xoap&amp;site=textlink&amp;cm_ven=XOAP&amp;cm_cat=TextLink&amp;cm_pla=Link3&amp;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&amp;site=textlink&amp;cm_ven=XOAP&amp;cm_cat=TextLink&amp;cm_pla=Link4&amp;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
+