barometer 0.3.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. data/README.rdoc +78 -70
  2. data/VERSION.yml +2 -2
  3. data/bin/barometer +100 -37
  4. data/lib/barometer.rb +12 -8
  5. data/lib/barometer/base.rb +48 -20
  6. data/lib/barometer/data.rb +5 -1
  7. data/lib/barometer/data/current.rb +23 -15
  8. data/lib/barometer/data/distance.rb +15 -5
  9. data/lib/barometer/data/forecast.rb +23 -5
  10. data/lib/barometer/data/geo.rb +16 -54
  11. data/lib/barometer/data/local_datetime.rb +137 -0
  12. data/lib/barometer/data/local_time.rb +134 -0
  13. data/lib/barometer/data/location.rb +6 -1
  14. data/lib/barometer/data/measurement.rb +71 -42
  15. data/lib/barometer/data/night.rb +69 -0
  16. data/lib/barometer/data/pressure.rb +15 -5
  17. data/lib/barometer/data/speed.rb +16 -5
  18. data/lib/barometer/data/sun.rb +8 -20
  19. data/lib/barometer/data/temperature.rb +22 -9
  20. data/lib/barometer/data/units.rb +10 -19
  21. data/lib/barometer/data/zone.rb +135 -9
  22. data/lib/barometer/formats.rb +12 -0
  23. data/lib/barometer/formats/coordinates.rb +42 -0
  24. data/lib/barometer/formats/format.rb +46 -0
  25. data/lib/barometer/formats/geocode.rb +51 -0
  26. data/lib/barometer/formats/icao.rb +37 -0
  27. data/lib/barometer/formats/postalcode.rb +22 -0
  28. data/lib/barometer/formats/short_zipcode.rb +17 -0
  29. data/lib/barometer/formats/weather_id.rb +107 -0
  30. data/lib/barometer/formats/zipcode.rb +31 -0
  31. data/lib/barometer/query.rb +61 -232
  32. data/lib/barometer/services.rb +14 -4
  33. data/lib/barometer/translations/icao_country_codes.yml +9 -0
  34. data/lib/barometer/translations/weather_country_codes.yml +17 -0
  35. data/lib/barometer/weather.rb +51 -30
  36. data/lib/barometer/{services → weather_services}/google.rb +23 -26
  37. data/lib/barometer/weather_services/noaa.rb +6 -0
  38. data/lib/barometer/{services → weather_services}/service.rb +101 -92
  39. data/lib/barometer/weather_services/weather_bug.rb +6 -0
  40. data/lib/barometer/weather_services/weather_dot_com.rb +261 -0
  41. data/lib/barometer/{services → weather_services}/wunderground.rb +58 -76
  42. data/lib/barometer/{services → weather_services}/yahoo.rb +91 -121
  43. data/lib/barometer/web_services/geocode.rb +33 -0
  44. data/lib/barometer/web_services/weather_id.rb +37 -0
  45. data/lib/barometer/web_services/web_service.rb +32 -0
  46. data/lib/demometer/demometer.rb +31 -4
  47. data/lib/demometer/views/forecast.erb +20 -0
  48. data/lib/demometer/views/index.erb +10 -3
  49. data/lib/demometer/views/measurement.erb +8 -3
  50. data/lib/demometer/views/readme.erb +63 -24
  51. data/spec/barometer_spec.rb +18 -36
  52. data/spec/{data_current_spec.rb → data/current_spec.rb} +73 -49
  53. data/spec/{data_distance_spec.rb → data/distance_spec.rb} +30 -30
  54. data/spec/{data_forecast_spec.rb → data/forecast_spec.rb} +57 -15
  55. data/spec/data/geo_spec.rb +91 -0
  56. data/spec/data/local_datetime_spec.rb +269 -0
  57. data/spec/data/local_time_spec.rb +239 -0
  58. data/spec/{data_location_spec.rb → data/location_spec.rb} +12 -1
  59. data/spec/{data_measurement_spec.rb → data/measurement_spec.rb} +135 -66
  60. data/spec/data/night_measurement_spec.rb +136 -0
  61. data/spec/{data_pressure_spec.rb → data/pressure_spec.rb} +29 -29
  62. data/spec/{data_speed_spec.rb → data/speed_spec.rb} +30 -30
  63. data/spec/data/sun_spec.rb +49 -0
  64. data/spec/{data_temperature_spec.rb → data/temperature_spec.rb} +44 -44
  65. data/spec/{units_spec.rb → data/units_spec.rb} +6 -6
  66. data/spec/{data_zone_spec.rb → data/zone_spec.rb} +15 -15
  67. data/spec/fixtures/formats/weather_id/90210.xml +1 -0
  68. data/spec/fixtures/formats/weather_id/atlanta.xml +1 -0
  69. data/spec/fixtures/formats/weather_id/from_USGA0028.xml +1 -0
  70. data/spec/fixtures/formats/weather_id/new_york.xml +1 -0
  71. data/spec/fixtures/{geocode_40_73.xml → geocode/40_73.xml} +0 -0
  72. data/spec/fixtures/{geocode_90210.xml → geocode/90210.xml} +0 -0
  73. data/spec/fixtures/{geocode_T5B4M9.xml → geocode/T5B4M9.xml} +0 -0
  74. data/spec/fixtures/geocode/atlanta.xml +1 -0
  75. data/spec/fixtures/{geocode_calgary_ab.xml → geocode/calgary_ab.xml} +0 -0
  76. data/spec/fixtures/{geocode_ksfo.xml → geocode/ksfo.xml} +0 -0
  77. data/spec/fixtures/{geocode_newyork_ny.xml → geocode/newyork_ny.xml} +0 -0
  78. data/spec/fixtures/{google_calgary_ab.xml → services/google/calgary_ab.xml} +0 -0
  79. data/spec/fixtures/services/weather_dot_com/90210.xml +1 -0
  80. data/spec/fixtures/{current_calgary_ab.xml → services/wunderground/current_calgary_ab.xml} +0 -0
  81. data/spec/fixtures/{forecast_calgary_ab.xml → services/wunderground/forecast_calgary_ab.xml} +0 -0
  82. data/spec/fixtures/{yahoo_90210.xml → services/yahoo/90210.xml} +0 -0
  83. data/spec/formats/coordinates_spec.rb +158 -0
  84. data/spec/formats/format_spec.rb +73 -0
  85. data/spec/formats/geocode_spec.rb +179 -0
  86. data/spec/formats/icao_spec.rb +61 -0
  87. data/spec/formats/postalcode_spec.rb +59 -0
  88. data/spec/formats/short_zipcode_spec.rb +53 -0
  89. data/spec/formats/weather_id_spec.rb +191 -0
  90. data/spec/formats/zipcode_spec.rb +111 -0
  91. data/spec/query_spec.rb +261 -288
  92. data/spec/spec_helper.rb +128 -4
  93. data/spec/{service_google_spec.rb → weather_services/google_spec.rb} +46 -46
  94. data/spec/weather_services/services_spec.rb +1118 -0
  95. data/spec/weather_services/weather_dot_com_spec.rb +327 -0
  96. data/spec/weather_services/wunderground_spec.rb +332 -0
  97. data/spec/{service_yahoo_spec.rb → weather_services/yahoo_spec.rb} +65 -81
  98. data/spec/weather_spec.rb +73 -61
  99. data/spec/web_services/geocode_spec.rb +45 -0
  100. data/spec/web_services/web_services_spec.rb +26 -0
  101. metadata +88 -36
  102. data/lib/barometer/services/noaa.rb +0 -6
  103. data/lib/barometer/services/weather_bug.rb +0 -6
  104. data/lib/barometer/services/weather_dot_com.rb +0 -6
  105. data/spec/data_geo_spec.rb +0 -94
  106. data/spec/data_sun_spec.rb +0 -76
  107. data/spec/service_wunderground_spec.rb +0 -330
  108. data/spec/services_spec.rb +0 -1106
@@ -0,0 +1,6 @@
1
+ module Barometer
2
+
3
+ class WeatherService::WeatherBug < WeatherService
4
+ end
5
+
6
+ end
@@ -0,0 +1,261 @@
1
+ module Barometer
2
+ #
3
+ # = Weather.com
4
+ # www.weather.com
5
+ #
6
+ # - key required: YES (partnerid & licensekey)
7
+ # - registration required: YES
8
+ # - supported countries: US (by zipcode), International (by Weather Location ID)
9
+ #
10
+ # === performs geo coding
11
+ # - city: PARTIAL (just a name)
12
+ # - coordinates: YES
13
+ #
14
+ # === time info
15
+ # - sun rise/set: YES
16
+ # - provides timezone: NO, but provides a utc offset
17
+ # - requires TZInfo: NO
18
+ #
19
+ # == resources
20
+ # - API: ?
21
+ #
22
+ # === Possible queries:
23
+ # - http://xoap.weather.com/weather/local/30339?cc=*&dayf=5&link=xoap&prod=xoap&par=[PartnerID]&key=[LicenseKey]
24
+ #
25
+ # where query can be:
26
+ # - zipcode (US) [5 digits only]
27
+ # - Weather Location ID (International)
28
+ #
29
+ # = Weather.com terms of use
30
+ # ???
31
+ #
32
+ # == notes
33
+ # - the Weather Location ID is a propreitary number (possibly shared with yahoo.com)
34
+ #
35
+ # == TODO
36
+ # - improve "forecasted_wet_by_icon?" to determine if day or night and use right code
37
+ # - improve "forecasted_sunny_by_icon?" to determine if day or night and use right code
38
+ # - improve "forcasted_wet_by_humidity?" to use forecasted values
39
+ # - improve "forcasted_windy?" to use forecasted values
40
+ #
41
+ class WeatherService::WeatherDotCom < WeatherService
42
+
43
+ @@partner_key = nil
44
+ @@license_key = nil
45
+
46
+ def self.source_name; :weather_dot_com; end
47
+ def self.accepted_formats; [:short_zipcode, :weather_id]; end
48
+
49
+ def self.keys=(keys)
50
+ raise ArgumentError unless keys.is_a?(Hash)
51
+ keys.each do |key, value|
52
+ @@partner_key = value.to_s if key.to_s.downcase == "partner"
53
+ @@license_key = value.to_s if key.to_s.downcase == "license"
54
+ end
55
+ end
56
+
57
+ def self.has_keys?; !@@partner_key.nil? && !@@license_key.nil?; end
58
+ def self.requires_keys?; true; end
59
+
60
+ def self.wet_icon_codes
61
+ codes = (0..18).to_a + [35] + (37..43).to_a + (45..47).to_a
62
+ codes.collect {|c| c.to_s}
63
+ end
64
+ def self.sunny_icon_codes
65
+ codes = [19, 22, 28, 30, 32, 34, 36]
66
+ codes.collect {|c| c.to_s}
67
+ end
68
+
69
+ def self._measure(measurement, query, metric=true)
70
+ raise ArgumentError unless measurement.is_a?(Data::Measurement)
71
+ raise ArgumentError unless query.is_a?(Barometer::Query)
72
+ measurement.source = self.source_name
73
+
74
+ begin
75
+ result = self.fetch(query.q, metric)
76
+ rescue Timeout::Error => e
77
+ return measurement
78
+ end
79
+
80
+ measurement.current = self.build_current(result, metric)
81
+ measurement.forecast = self.build_forecast(result, metric)
82
+ measurement.location = self.build_location(result, query.geo)
83
+ measurement.current.sun = self.build_sun(result)
84
+
85
+ # add links
86
+ if result && result['lnks'] && result['lnks']['link']
87
+ result['lnks']['link'].each do |link_hash|
88
+ measurement.links[link_hash['t']] = link_hash['l']
89
+ end
90
+ end
91
+
92
+ # set local time of measurement
93
+ local_time = self.build_local_time(result)
94
+ measurement.measured_at = local_time
95
+ measurement.current.current_at = local_time
96
+
97
+ measurement
98
+ end
99
+
100
+ # WARNING
101
+ # this is a best guess method. the data provided for time conversions
102
+ # leaves a lot to be desired. some time zones, offsets, local times are
103
+ # out to lunch. eg. Tahiti (all times seem to be 30 min off), but there
104
+ # is no way to determine this
105
+ #
106
+ # regardless of the above, this method will trust the data given to it
107
+ #
108
+ def self.build_local_time(data)
109
+ (data && data['loc']) ? Data::LocalTime.parse(data['loc']['tm']) : nil
110
+ end
111
+
112
+ def self.build_current(data, metric=true)
113
+ raise ArgumentError unless data.is_a?(Hash)
114
+ current = Data::CurrentMeasurement.new
115
+ if data
116
+ if data['cc']
117
+ current.updated_at = Data::LocalDateTime.parse(data['cc']['lsup'])
118
+ current.icon = data['cc']['icon']
119
+ current.condition = data['cc']['t']
120
+ current.humidity = data['cc']['hmid'].to_i
121
+ current.temperature = Data::Temperature.new(metric)
122
+ current.temperature << data['cc']['tmp']
123
+ current.dew_point = Data::Temperature.new(metric)
124
+ current.dew_point << data['cc']['dewp']
125
+ current.wind_chill = Data::Temperature.new(metric)
126
+ current.wind_chill << data['cc']['flik']
127
+ current.visibility = Data::Distance.new(metric)
128
+ current.visibility << data['cc']['vis']
129
+ if data['cc']['wind']
130
+ current.wind = Data::Speed.new(metric)
131
+ current.wind << data['cc']['wind']['s']
132
+ current.wind.degrees = data['cc']['wind']['d'].to_f
133
+ current.wind.direction = data['cc']['wind']['t']
134
+ end
135
+ if data['cc']['bar']
136
+ current.pressure = Data::Pressure.new(metric)
137
+ current.pressure << data['cc']['bar']['r']
138
+ end
139
+ end
140
+ end
141
+ current
142
+ end
143
+
144
+ def self.build_forecast(data, metric=true)
145
+ raise ArgumentError unless data.is_a?(Hash)
146
+ forecasts = []
147
+
148
+ if data && data['dayf'] && data['dayf']['day']
149
+ local_date = data['dayf']['lsup']
150
+ data['dayf']['day'].each do |forecast|
151
+ forecast_measurement = Data::ForecastMeasurement.new
152
+ forecast_measurement.date = Date.parse(forecast['dt'])
153
+
154
+ forecast_measurement.high = Data::Temperature.new(metric)
155
+ forecast_measurement.high << forecast['hi']
156
+ forecast_measurement.low = Data::Temperature.new(metric)
157
+ forecast_measurement.low << forecast['low']
158
+
159
+ # build sun
160
+ rise_local_time = Data::LocalTime.parse(forecast['sunr'])
161
+ set_local_time = Data::LocalTime.parse(forecast['suns'])
162
+ sun = Data::Sun.new(rise_local_time, set_local_time)
163
+ forecast_measurement.sun = sun
164
+
165
+ if forecast['part']
166
+ forecast['part'].each do |part|
167
+ if part['p'] == 'd'
168
+ # add this to the ForecastMeasurement
169
+ forecast_measurement.condition = part['t']
170
+ forecast_measurement.icon = part['icon']
171
+ forecast_measurement.pop = part['ppcp'].to_i
172
+ forecast_measurement.humidity = part['hmid'].to_i
173
+
174
+ if part['wind']
175
+ forecast_measurement.wind = Data::Speed.new(metric)
176
+ forecast_measurement.wind << part['wind']['s']
177
+ forecast_measurement.wind.degrees = part['wind']['d'].to_i
178
+ forecast_measurement.wind.direction = part['wind']['t']
179
+ end
180
+
181
+ elsif part['p'] == 'n'
182
+ # add this to the NightMeasurement
183
+ forecast_measurement.night = Data::NightMeasurement.new
184
+ forecast_measurement.night.condition = part['t']
185
+ forecast_measurement.night.icon = part['icon']
186
+ forecast_measurement.night.pop = part['ppcp'].to_i
187
+ forecast_measurement.night.humidity = part['hmid'].to_i
188
+
189
+ if part['wind']
190
+ forecast_measurement.night.wind = Data::Speed.new(metric)
191
+ forecast_measurement.night.wind << part['wind']['s']
192
+ forecast_measurement.night.wind.degrees = part['wind']['d'].to_i
193
+ forecast_measurement.night.wind.direction = part['wind']['t']
194
+ end
195
+
196
+ end
197
+ end
198
+ end
199
+ forecasts << forecast_measurement
200
+ end
201
+ end
202
+ forecasts
203
+ end
204
+
205
+ def self.build_location(data, geo=nil)
206
+ raise ArgumentError unless data.is_a?(Hash)
207
+ raise ArgumentError unless (geo.nil? || geo.is_a?(Data::Geo))
208
+ location = Data::Location.new
209
+ # use the geocoded data if available, otherwise get data from result
210
+ if geo
211
+ location.city = geo.locality
212
+ location.state_code = geo.region
213
+ location.country = geo.country
214
+ location.country_code = geo.country_code
215
+ location.latitude = geo.latitude
216
+ location.longitude = geo.longitude
217
+ else
218
+ if data && data['loc']
219
+ location.name = data['loc']['dnam']
220
+ location.latitude = data['loc']['lat']
221
+ location.longitude = data['loc']['lon']
222
+ end
223
+ end
224
+ location
225
+ end
226
+
227
+ def self.build_sun(data)
228
+ raise ArgumentError unless data.is_a?(Hash)
229
+ sun = nil
230
+ if data
231
+ if data['loc']
232
+ rise_local_time = Data::LocalTime.parse(data['loc']['sunr'])
233
+ set_local_time = Data::LocalTime.parse(data['loc']['suns'])
234
+ end
235
+ sun = Data::Sun.new(rise_local_time, set_local_time)
236
+ end
237
+ sun || Data::Sun.new
238
+ end
239
+
240
+ # use HTTParty to get the current weather
241
+ #
242
+ def self.fetch(query, metric=true)
243
+ self.get(
244
+ "http://xoap.weather.com/weather/local/#{query}",
245
+ :query => { :par => @@partner_key, :key => @@license_key,
246
+ :prod => "xoap", :link => "xoap", :cc => "*",
247
+ :dayf => "5", :unit => (metric ? 'm' : 's')
248
+ },
249
+ :format => :xml,
250
+ :timeout => Barometer.timeout
251
+ )['weather']
252
+ end
253
+
254
+ end
255
+ end
256
+
257
+ # FUTURE DATA TO SUPPORT?
258
+ # "cc"=>
259
+ # {"obst"=>"Santa Monica, CA",
260
+ # "uv"=>{"i"=>"0", "t"=>"Low"},
261
+ # "moon"=>{"icon"=>"9", "t"=>"Waxing Gibbous"}
@@ -1,4 +1,6 @@
1
1
  module Barometer
2
+ #
3
+ # [DEFAULT PROVIDER]
2
4
  #
3
5
  # = Weather Underground
4
6
  # www.wunderground.com
@@ -16,6 +18,9 @@ module Barometer
16
18
  # - sun rise/set: YES (today only)
17
19
  # - provides timezone: YES
18
20
  # - requires TZInfo: YES
21
+ # *NOTE: If accuarcy of times and converting, this service is the top choice.
22
+ # They provide the full timezone name that is needed for the most
23
+ # accurate time conversions.
19
24
  #
20
25
  # == resources
21
26
  # - API: http://wiki.wunderground.com/index.php/API_-_XML
@@ -36,19 +41,19 @@ module Barometer
36
41
  # - airport code (3-letter or 4-letter)
37
42
  # - lat,lon
38
43
  #
39
- class Wunderground < Service
44
+ # = Wunderground terms of use
45
+ # Unable to locate.
46
+ #
47
+ class WeatherService::Wunderground < WeatherService
40
48
 
49
+ def self.source_name; :wunderground; end
41
50
  def self.accepted_formats
42
51
  [:zipcode, :postalcode, :icao, :coordinates, :geocode]
43
52
  end
44
53
 
45
- def self.source_name
46
- :wunderground
47
- end
48
-
49
54
  # these are the icon codes that indicate "wet", used by wet? function
50
55
  def self.wet_icon_codes
51
- %w(flurries rain sleet snow tstorms nt_flurries nt_rain nt_sleet nt_snow nt_tstorms chancerain)
56
+ %w(flurries rain sleet snow tstorms nt_flurries nt_rain nt_sleet nt_snow nt_tstorms chancerain chancetstorms)
52
57
  end
53
58
  # these are the icon codes that indicate "sun", used by sunny? function
54
59
  def self.sunny_icon_codes
@@ -56,21 +61,19 @@ module Barometer
56
61
  end
57
62
 
58
63
  def self._measure(measurement, query, metric=true)
59
- raise ArgumentError unless measurement.is_a?(Barometer::Measurement)
64
+ raise ArgumentError unless measurement.is_a?(Data::Measurement)
60
65
  raise ArgumentError unless query.is_a?(Barometer::Query)
61
66
  measurement.source = self.source_name
62
67
 
63
- # get current measurement
64
68
  begin
65
- current_result = self.get_current(query.preferred)
69
+ current_result = self.get_current(query.q)
66
70
  measurement.current = self.build_current(current_result, metric)
67
71
  rescue Timeout::Error => e
68
72
  return measurement
69
73
  end
70
74
 
71
- # get forecast measurement
72
75
  begin
73
- forecast_result = self.get_forecast(query.preferred)
76
+ forecast_result = self.get_forecast(query.q)
74
77
  measurement.forecast = self.build_forecast(forecast_result, metric)
75
78
  rescue Timeout::Error => e
76
79
  return measurement
@@ -80,7 +83,10 @@ module Barometer
80
83
  measurement.station = self.build_station(current_result)
81
84
  measurement.timezone = self.build_timezone(forecast_result)
82
85
 
83
- # add sun data to current
86
+ if current_result["credit"] && current_result["credit_URL"]
87
+ measurement.links[current_result["credit"]] = current_result["credit_URL"]
88
+ end
89
+
84
90
  sun = nil
85
91
  if measurement.current
86
92
  sun = self.build_sun(forecast_result, measurement.timezone)
@@ -88,48 +94,49 @@ module Barometer
88
94
  end
89
95
  # use todays sun data for all future days
90
96
  if measurement.forecast && sun
91
- start_date = Date.parse(measurement.current.time)
92
97
  measurement.forecast.each do |forecast|
93
- days_in_future = forecast.date - start_date
94
- forecast.sun = Barometer::Sun.add_days!(sun,days_in_future.to_i)
98
+ forecast.sun = sun
95
99
  end
96
100
  end
97
101
 
102
+ local_time = measurement.timezone ? Data::LocalTime.parse(
103
+ measurement.timezone.utc_to_local(Time.now.utc)
104
+ ) : nil
105
+ measurement.measured_at = local_time
106
+ measurement.current.current_at = local_time
107
+
98
108
  measurement
99
109
  end
100
-
110
+
101
111
  def self.build_current(data, metric=true)
102
112
  raise ArgumentError unless data.is_a?(Hash)
103
113
 
104
- current = CurrentMeasurement.new
105
- #current.time = Time.parse(current_result['observation_time_rfc822']) unless
106
- # current_result['observation_time_rfc822'].blank?
107
- current.time = data['observation_time_rfc822']
108
- current.local_time = data['observation_time']
114
+ current = Data::CurrentMeasurement.new
115
+ current.updated_at = Data::LocalDateTime.parse(data['observation_time']) if data['observation_time']
109
116
  current.humidity = data['relative_humidity'].to_i
110
117
  current.icon = data['icon'] if data['icon']
111
118
 
112
- current.temperature = Temperature.new(metric)
119
+ current.temperature = Data::Temperature.new(metric)
113
120
  current.temperature << [data['temp_c'], data['temp_f']]
114
121
 
115
- current.wind = Speed.new(metric)
122
+ current.wind = Data::Speed.new(metric)
116
123
  current.wind.mph = data['wind_mph'].to_f
117
124
  current.wind.degrees = data['wind_degrees'].to_i
118
125
  current.wind.direction = data['wind_dir']
119
126
 
120
- current.pressure = Pressure.new(metric)
127
+ current.pressure = Data::Pressure.new(metric)
121
128
  current.pressure << [data['pressure_mb'], data['pressure_in']]
122
129
 
123
- current.dew_point = Temperature.new(metric)
130
+ current.dew_point = Data::Temperature.new(metric)
124
131
  current.dew_point << [data['dewpoint_c'], data['dewpoint_f']]
125
132
 
126
- current.heat_index = Temperature.new(metric)
133
+ current.heat_index = Data::Temperature.new(metric)
127
134
  current.heat_index << [data['heat_index_c'], data['heat_index_f']]
128
135
 
129
- current.wind_chill = Temperature.new(metric)
136
+ current.wind_chill = Data::Temperature.new(metric)
130
137
  current.wind_chill << [data['windchill_c'], data['windchill_f']]
131
138
 
132
- current.visibility = Distance.new(metric)
139
+ current.visibility = Data::Distance.new(metric)
133
140
  current.visibility << [data['visibility_km'], data['visibility_mi']]
134
141
 
135
142
  current
@@ -143,15 +150,15 @@ module Barometer
143
150
  data['simpleforecast']['forecastday']
144
151
 
145
152
  data['simpleforecast']['forecastday'].each do |forecast|
146
- forecast_measurement = ForecastMeasurement.new
153
+ forecast_measurement = Data::ForecastMeasurement.new
147
154
  forecast_measurement.icon = forecast['icon']
148
155
  forecast_measurement.date = Date.parse(forecast['date']['pretty'])
149
156
  forecast_measurement.pop = forecast['pop'].to_i
150
157
 
151
- forecast_measurement.high = Temperature.new(metric)
158
+ forecast_measurement.high = Data::Temperature.new(metric)
152
159
  forecast_measurement.high << [forecast['high']['celsius'],forecast['high']['fahrenheit']]
153
160
 
154
- forecast_measurement.low = Temperature.new(metric)
161
+ forecast_measurement.low = Data::Temperature.new(metric)
155
162
  forecast_measurement.low << [forecast['low']['celsius'],forecast['low']['fahrenheit']]
156
163
 
157
164
  forecasts << forecast_measurement
@@ -162,7 +169,7 @@ module Barometer
162
169
 
163
170
  def self.build_location(data)
164
171
  raise ArgumentError unless data.is_a?(Hash)
165
- location = Location.new
172
+ location = Data::Location.new
166
173
  if data['display_location']
167
174
  location.name = data['display_location']['full']
168
175
  location.city = data['display_location']['city']
@@ -178,7 +185,7 @@ module Barometer
178
185
 
179
186
  def self.build_station(data)
180
187
  raise ArgumentError unless data.is_a?(Hash)
181
- station = Location.new
188
+ station = Data::Location.new
182
189
  station.id = data['station_id']
183
190
  if data['observation_location']
184
191
  station.name = data['observation_location']['full']
@@ -193,15 +200,6 @@ module Barometer
193
200
  station
194
201
  end
195
202
 
196
- # <forecastday>
197
- # <date>
198
- # <pretty_short>9:00 PM CST</pretty_short>
199
- # <pretty>9:00 PM CST on January 15, 2008</pretty>
200
- # <isdst>0</isdst>
201
- # <tz_short>CST</tz_short>
202
- # <tz_long>America/Chicago</tz_long>
203
- # </date>
204
- # </forecastday>
205
203
  def self.build_timezone(data)
206
204
  raise ArgumentError unless data.is_a?(Hash)
207
205
  timezone = nil
@@ -209,62 +207,45 @@ module Barometer
209
207
  data['simpleforecast']['forecastday'] &&
210
208
  data['simpleforecast']['forecastday'].first &&
211
209
  data['simpleforecast']['forecastday'].first['date']
212
- timezone = Barometer::Zone.new(
210
+ timezone = Data::Zone.new(
213
211
  data['simpleforecast']['forecastday'].first['date']['tz_long']
214
212
  )
215
213
  end
216
- timezone
214
+ timezone || Data::Zone.new(nil)
217
215
  end
218
216
 
219
217
  def self.build_sun(data, timezone)
220
218
  raise ArgumentError unless data.is_a?(Hash)
221
- raise ArgumentError unless timezone.is_a?(Barometer::Zone)
222
-
219
+ raise ArgumentError unless timezone.is_a?(Data::Zone)
223
220
  sun = nil
224
221
  if data
225
- time = nil
226
- if data['simpleforecast'] &&
227
- data['simpleforecast']['forecastday'] &&
228
- data['simpleforecast']['forecastday'].first &&
229
- data['simpleforecast']['forecastday'].first['date']
230
-
231
- # construct current date
232
- date_data = data['simpleforecast']['forecastday'].first['date']
233
- time = Time.local(
234
- date_data['year'], date_data['month'], date_data['day'],
235
- date_data['hour'], date_data['min'], date_data['sec']
236
- )
237
- end
238
- if time && data['moon_phase']
239
- # get the sun rise and set times (ie "6:32 am")
222
+ if data['moon_phase']
240
223
  if data['moon_phase']['sunrise']
241
- rise = Time.local(
242
- time.year, time.month, time.day,
243
- data['moon_phase']['sunrise']['hour'],
244
- data['moon_phase']['sunrise']['minute']
224
+ rise = Data::LocalTime.new(
225
+ data['moon_phase']['sunrise']['hour'].to_i,
226
+ data['moon_phase']['sunrise']['minute'].to_i
245
227
  )
246
228
  end
247
229
  if data['moon_phase']['sunset']
248
- set = Time.local(
249
- time.year, time.month, time.day,
250
- data['moon_phase']['sunset']['hour'],
251
- data['moon_phase']['sunset']['minute']
230
+ set = Data::LocalTime.new(
231
+ data['moon_phase']['sunset']['hour'].to_i,
232
+ data['moon_phase']['sunset']['minute'].to_i
252
233
  )
253
234
  end
254
235
 
255
- sun = Sun.new(
256
- timezone.tz.local_to_utc(rise),
257
- timezone.tz.local_to_utc(set)
236
+ sun = Data::Sun.new(
237
+ rise,
238
+ set
258
239
  )
259
240
  end
260
241
  end
261
-
262
- sun || Sun.new
242
+ sun || Data::Sun.new
263
243
  end
264
244
 
265
245
  # use HTTParty to get the current weather
266
246
  def self.get_current(query)
267
- Barometer::Wunderground.get(
247
+ return unless query
248
+ self.get(
268
249
  "http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml",
269
250
  :query => {:query => query},
270
251
  :format => :xml,
@@ -274,7 +255,8 @@ module Barometer
274
255
 
275
256
  # use HTTParty to get the forecasted weather
276
257
  def self.get_forecast(query)
277
- Barometer::Wunderground.get(
258
+ return unless query
259
+ self.get(
278
260
  "http://api.wunderground.com/auto/wui/geo/ForecastXML/index.xml",
279
261
  :query => {:query => query},
280
262
  :format => :xml,