barometer 0.6.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +1 -1
- data/VERSION.yml +1 -1
- data/bin/barometer +4 -12
- data/lib/barometer/data.rb +2 -5
- data/lib/barometer/data/local_datetime.rb +2 -2
- data/lib/barometer/measurements/measurement.rb +6 -6
- data/lib/barometer/measurements/result.rb +207 -0
- data/lib/barometer/measurements/{forecast_array.rb → result_array.rb} +16 -13
- data/lib/barometer/translations/weather_country_codes.yml +3 -3
- data/lib/barometer/weather_services/google.rb +3 -3
- data/lib/barometer/weather_services/weather_bug.rb +3 -3
- data/lib/barometer/weather_services/weather_dot_com.rb +65 -38
- data/lib/barometer/weather_services/wunderground.rb +3 -3
- data/lib/barometer/weather_services/yahoo.rb +3 -3
- data/lib/demometer/demometer.rb +2 -2
- data/lib/demometer/public/css/master.css +4 -75
- data/lib/demometer/public/css/print.css +1 -2
- data/lib/demometer/public/css/syntax.css +1 -2
- data/lib/demometer/views/about.erb +1 -1
- data/lib/demometer/views/contributing.erb +4 -4
- data/lib/demometer/views/index.erb +8 -9
- data/lib/demometer/views/layout.erb +1 -2
- data/spec/measurements/measurement_spec.rb +29 -53
- data/spec/measurements/{forecast_array_spec.rb → result_array_spec.rb} +8 -8
- data/spec/measurements/result_spec.rb +660 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/weather_services/google_spec.rb +4 -4
- data/spec/weather_services/services_spec.rb +1 -1
- data/spec/weather_services/weather_bug_spec.rb +3 -3
- data/spec/weather_services/weather_dot_com_spec.rb +19 -22
- data/spec/weather_services/wunderground_spec.rb +2 -2
- data/spec/weather_services/yahoo_spec.rb +2 -2
- data/spec/weather_spec.rb +14 -39
- metadata +6 -12
- data/lib/barometer/measurements/common.rb +0 -113
- data/lib/barometer/measurements/current.rb +0 -76
- data/lib/barometer/measurements/forecast.rb +0 -62
- data/lib/barometer/measurements/night.rb +0 -27
- data/spec/measurements/common_spec.rb +0 -352
- data/spec/measurements/current_spec.rb +0 -186
- data/spec/measurements/forecast_spec.rb +0 -135
- data/spec/measurements/night_measurement_spec.rb +0 -49
data/README.rdoc
CHANGED
@@ -15,7 +15,7 @@ this.
|
|
15
15
|
|
16
16
|
== version
|
17
17
|
|
18
|
-
Version 0.6.
|
18
|
+
Version 0.6.2 is the current release of this gem.
|
19
19
|
The gem is available from github (attack-barometer) or rubyforge (barometer).
|
20
20
|
It is fully functional (for five weather service APIs).
|
21
21
|
|
data/VERSION.yml
CHANGED
data/bin/barometer
CHANGED
@@ -66,7 +66,7 @@ require 'yaml'
|
|
66
66
|
|
67
67
|
# file where API keys are stored
|
68
68
|
KEY_FILE = File.expand_path(File.join('~', '.barometer'))
|
69
|
-
BAROMETER_VERSION = '0.6.
|
69
|
+
BAROMETER_VERSION = '0.6.2'
|
70
70
|
|
71
71
|
class App
|
72
72
|
|
@@ -403,22 +403,14 @@ def pretty_forecast(f)
|
|
403
403
|
return unless f
|
404
404
|
section("FOR: #{f.date}", 3) do
|
405
405
|
pretty_hash({
|
406
|
-
"
|
406
|
+
"Valid From" => f.valid_start_date.to_s(true),
|
407
|
+
"Valid Until" => f.valid_end_date.to_s(true),
|
408
|
+
"Icon" => f.icon, "Description" => f.description,
|
407
409
|
"Condition" => f.condition, "High" => f.high,
|
408
410
|
"Low" => f.low, "POP" => f.pop, "Humidity" => f.humidity })
|
409
411
|
pretty_hash({ "Wind" => f.wind, "Wind Direction" => f.wind.direction,
|
410
412
|
"Wind Degrees" => f.wind.degrees }) if f.wind
|
411
413
|
pretty_hash({ "Sun Rise" => f.sun.rise, "Sun Set" => f.sun.set }) if f.sun
|
412
|
-
if f.night
|
413
|
-
puts
|
414
|
-
title("NIGHT", 4)
|
415
|
-
pretty_hash({
|
416
|
-
"Date" => f.night.date, "Icon" => f.night.icon,
|
417
|
-
"Condition" => f.night.condition, "POP" => f.night.pop,
|
418
|
-
"Humidity" => f.night.humidity })
|
419
|
-
pretty_hash({ "Wind" => f.night.wind, "Wind Direction" => f.night.wind.direction,
|
420
|
-
"Wind Degrees" => f.night.wind.degrees }) if f.night.wind
|
421
|
-
end
|
422
414
|
end
|
423
415
|
end
|
424
416
|
|
data/lib/barometer/data.rb
CHANGED
@@ -17,8 +17,5 @@ require 'data/local_datetime'
|
|
17
17
|
# measurements
|
18
18
|
#
|
19
19
|
require 'measurements/measurement'
|
20
|
-
require 'measurements/
|
21
|
-
require 'measurements/
|
22
|
-
require 'measurements/forecast_array'
|
23
|
-
require 'measurements/forecast'
|
24
|
-
require 'measurements/night'
|
20
|
+
require 'measurements/result'
|
21
|
+
require 'measurements/result_array'
|
@@ -101,8 +101,8 @@ module Barometer
|
|
101
101
|
end
|
102
102
|
raise ArgumentError unless the_other.is_a?(Data::LocalDateTime) || the_other.is_a?(Data::LocalTime)
|
103
103
|
|
104
|
-
if (other.is_a?(String) || other.is_a?(Time) || other.is_a?(DateTime)) &&
|
105
|
-
the_other.is_a?(Data::LocalDateTime)
|
104
|
+
if ((other.is_a?(String) || other.is_a?(Time) || other.is_a?(DateTime)) &&
|
105
|
+
the_other.is_a?(Data::LocalDateTime)) || other.is_a?(Data::LocalDateTime)
|
106
106
|
# we are counting days + seconds
|
107
107
|
if (_total_days <=> the_other._total_days) == 0
|
108
108
|
return total_seconds <=> the_other.total_seconds
|
@@ -3,8 +3,8 @@ module Barometer
|
|
3
3
|
# Measurement
|
4
4
|
# a class that holds the response from a weather service
|
5
5
|
#
|
6
|
-
# its main purpose is to hold all the data collected from a weather
|
7
|
-
# as it is passed to the weather object
|
6
|
+
# its main purpose is to hold all the data collected from a single weather
|
7
|
+
# service as it is passed to the weather object
|
8
8
|
#
|
9
9
|
# this response includes
|
10
10
|
# - current weather data (using the CurrentMeasurement class)
|
@@ -31,8 +31,8 @@ module Barometer
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def success!
|
34
|
-
|
35
|
-
|
34
|
+
current && current.temperature &&
|
35
|
+
!current.temperature.c.nil? && @success = true
|
36
36
|
end
|
37
37
|
|
38
38
|
def stamp!; @utc_time_stamp = Time.now.utc; end
|
@@ -95,14 +95,14 @@ module Barometer
|
|
95
95
|
end
|
96
96
|
|
97
97
|
def current=(current)
|
98
|
-
raise ArgumentError unless current.is_a?(Measurement::
|
98
|
+
raise ArgumentError unless current.is_a?(Measurement::Result)
|
99
99
|
@current = current
|
100
100
|
self.stamp!
|
101
101
|
self.success!
|
102
102
|
end
|
103
103
|
|
104
104
|
def forecast=(forecast)
|
105
|
-
raise ArgumentError unless forecast.is_a?(Measurement::
|
105
|
+
raise ArgumentError unless forecast.is_a?(Measurement::ResultArray)
|
106
106
|
@forecast = forecast
|
107
107
|
end
|
108
108
|
|
@@ -0,0 +1,207 @@
|
|
1
|
+
module Barometer
|
2
|
+
#
|
3
|
+
# Result Measurement
|
4
|
+
# a data class for resulting weather conditions
|
5
|
+
#
|
6
|
+
# This is basically a data holding class for the resulting weather
|
7
|
+
# conditions.
|
8
|
+
#
|
9
|
+
class Measurement::Result
|
10
|
+
|
11
|
+
attr_reader :current_at, :updated_at
|
12
|
+
attr_reader :valid_start_date, :valid_end_date, :date
|
13
|
+
attr_reader :humidity, :icon, :condition
|
14
|
+
attr_reader :temperature, :dew_point, :heat_index, :wind_chill
|
15
|
+
attr_reader :low, :high, :pop
|
16
|
+
attr_reader :wind, :sun, :pressure, :visibility
|
17
|
+
attr_accessor :metric, :description
|
18
|
+
|
19
|
+
def initialize(metric=true); @metric = metric; end
|
20
|
+
|
21
|
+
# accessors (with input checking)
|
22
|
+
#
|
23
|
+
def temperature=(temperature)
|
24
|
+
raise ArgumentError unless temperature.is_a?(Data::Temperature)
|
25
|
+
@temperature = temperature
|
26
|
+
end
|
27
|
+
|
28
|
+
def dew_point=(dew_point)
|
29
|
+
raise ArgumentError unless dew_point.is_a?(Data::Temperature)
|
30
|
+
@dew_point = dew_point
|
31
|
+
end
|
32
|
+
|
33
|
+
def heat_index=(heat_index)
|
34
|
+
raise ArgumentError unless heat_index.is_a?(Data::Temperature)
|
35
|
+
@heat_index = heat_index
|
36
|
+
end
|
37
|
+
|
38
|
+
def wind_chill=(wind_chill)
|
39
|
+
raise ArgumentError unless wind_chill.is_a?(Data::Temperature)
|
40
|
+
@wind_chill = wind_chill
|
41
|
+
end
|
42
|
+
|
43
|
+
def pressure=(pressure)
|
44
|
+
raise ArgumentError unless pressure.is_a?(Data::Pressure)
|
45
|
+
@pressure = pressure
|
46
|
+
end
|
47
|
+
|
48
|
+
def visibility=(visibility)
|
49
|
+
raise ArgumentError unless visibility.is_a?(Data::Distance)
|
50
|
+
@visibility = visibility
|
51
|
+
end
|
52
|
+
|
53
|
+
def current_at=(current_at)
|
54
|
+
raise ArgumentError unless (current_at.is_a?(Data::LocalTime) || current_at.is_a?(Data::LocalDateTime))
|
55
|
+
@current_at = current_at
|
56
|
+
end
|
57
|
+
|
58
|
+
def updated_at=(updated_at)
|
59
|
+
raise ArgumentError unless (updated_at.is_a?(Data::LocalTime) || updated_at.is_a?(Data::LocalDateTime))
|
60
|
+
@updated_at = updated_at
|
61
|
+
end
|
62
|
+
|
63
|
+
def date=(date)
|
64
|
+
raise ArgumentError unless date.is_a?(Date)
|
65
|
+
@date = date
|
66
|
+
@valid_start_date = Data::LocalDateTime.new(date.year,date.month,date.day,0,0,0)
|
67
|
+
@valid_end_date = Data::LocalDateTime.new(date.year,date.month,date.day,23,59,59)
|
68
|
+
end
|
69
|
+
|
70
|
+
def valid_start_date=(date)
|
71
|
+
raise ArgumentError unless date.is_a?(Data::LocalDateTime)
|
72
|
+
@valid_start_date = date
|
73
|
+
end
|
74
|
+
|
75
|
+
def valid_end_date=(date)
|
76
|
+
raise ArgumentError unless date.is_a?(Data::LocalDateTime)
|
77
|
+
@valid_end_date = date
|
78
|
+
end
|
79
|
+
|
80
|
+
def high=(high)
|
81
|
+
raise ArgumentError unless high.is_a?(Data::Temperature)
|
82
|
+
@high = high
|
83
|
+
end
|
84
|
+
|
85
|
+
def low=(low)
|
86
|
+
raise ArgumentError unless low.is_a?(Data::Temperature)
|
87
|
+
@low = low
|
88
|
+
end
|
89
|
+
|
90
|
+
def pop=(pop)
|
91
|
+
raise ArgumentError unless pop.is_a?(Fixnum)
|
92
|
+
@pop = pop
|
93
|
+
end
|
94
|
+
|
95
|
+
def humidity=(humidity)
|
96
|
+
raise ArgumentError unless
|
97
|
+
(humidity.is_a?(Fixnum) || humidity.is_a?(Float))
|
98
|
+
@humidity = humidity
|
99
|
+
end
|
100
|
+
|
101
|
+
def icon=(icon)
|
102
|
+
raise ArgumentError unless icon.is_a?(String)
|
103
|
+
@icon = icon
|
104
|
+
end
|
105
|
+
|
106
|
+
def condition=(condition)
|
107
|
+
raise ArgumentError unless condition.is_a?(String)
|
108
|
+
@condition = condition
|
109
|
+
end
|
110
|
+
|
111
|
+
def wind=(wind)
|
112
|
+
raise ArgumentError unless wind.is_a?(Data::Speed)
|
113
|
+
@wind = wind
|
114
|
+
end
|
115
|
+
|
116
|
+
def sun=(sun)
|
117
|
+
raise ArgumentError unless (sun.is_a?(Data::Sun) || sun.nil?)
|
118
|
+
@sun = sun
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
# answer simple questions
|
123
|
+
#
|
124
|
+
|
125
|
+
def windy?(threshold=10)
|
126
|
+
raise ArgumentError unless (threshold.is_a?(Fixnum) || threshold.is_a?(Float))
|
127
|
+
return nil unless wind?
|
128
|
+
wind.to_f(metric?) >= threshold.to_f
|
129
|
+
end
|
130
|
+
|
131
|
+
def day?(time)
|
132
|
+
return nil unless time && sun?
|
133
|
+
sun.after_rise?(time) && sun.before_set?(time)
|
134
|
+
end
|
135
|
+
|
136
|
+
def sunny?(time, sunny_icons=nil)
|
137
|
+
return nil unless time
|
138
|
+
is_day = day?(time)
|
139
|
+
return nil if is_day.nil?
|
140
|
+
is_day && _sunny_by_icon?(sunny_icons)
|
141
|
+
end
|
142
|
+
|
143
|
+
def wet?(wet_icons=nil, pop_threshold=50, humidity_threshold=99)
|
144
|
+
result = nil
|
145
|
+
result ||= _wet_by_pop?(pop_threshold) if pop?
|
146
|
+
result ||= _wet_by_icon?(wet_icons) if icon?
|
147
|
+
result ||= _wet_by_humidity?(humidity_threshold) if humidity?
|
148
|
+
result ||= _wet_by_dewpoint? if (dew_point? && temperature?)
|
149
|
+
result
|
150
|
+
end
|
151
|
+
|
152
|
+
#
|
153
|
+
# helpers
|
154
|
+
#
|
155
|
+
|
156
|
+
def metric?; metric; end
|
157
|
+
|
158
|
+
def for_datetime?(datetime)
|
159
|
+
raise ArgumentError unless datetime.is_a?(Data::LocalDateTime)
|
160
|
+
datetime >= @valid_start_date && datetime <= @valid_end_date
|
161
|
+
end
|
162
|
+
|
163
|
+
# creates "?" helpers for all attributes (which maps to nil?)
|
164
|
+
#
|
165
|
+
def method_missing(method,*args)
|
166
|
+
# if the method ends in ?, then strip it off and see if we
|
167
|
+
# respond to the method without the ?
|
168
|
+
if (call_method = method.to_s.chomp!("?")) && respond_to?(call_method)
|
169
|
+
return send(call_method).nil? ? false : true
|
170
|
+
else
|
171
|
+
super(method,*args)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def _wet_by_dewpoint?
|
178
|
+
return nil unless dew_point? && temperature?
|
179
|
+
temperature.to_f(metric?) <= dew_point.to_f(metric?)
|
180
|
+
end
|
181
|
+
|
182
|
+
def _wet_by_pop?(threshold=50)
|
183
|
+
raise ArgumentError unless (threshold.is_a?(Fixnum) || threshold.is_a?(Float))
|
184
|
+
return nil unless pop?
|
185
|
+
pop.to_f >= threshold.to_f
|
186
|
+
end
|
187
|
+
|
188
|
+
def _wet_by_humidity?(threshold=99)
|
189
|
+
raise ArgumentError unless (threshold.is_a?(Fixnum) || threshold.is_a?(Float))
|
190
|
+
return nil unless humidity?
|
191
|
+
humidity.to_f >= threshold.to_f
|
192
|
+
end
|
193
|
+
|
194
|
+
def _wet_by_icon?(wet_icons=nil)
|
195
|
+
raise ArgumentError unless (wet_icons.nil? || wet_icons.is_a?(Array))
|
196
|
+
return nil unless (icon? && wet_icons)
|
197
|
+
wet_icons.include?(icon.to_s.downcase)
|
198
|
+
end
|
199
|
+
|
200
|
+
def _sunny_by_icon?(sunny_icons=nil)
|
201
|
+
raise ArgumentError unless (sunny_icons.nil? || sunny_icons.is_a?(Array))
|
202
|
+
return nil unless (icon? && sunny_icons)
|
203
|
+
sunny_icons.include?(icon.to_s.downcase)
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
require 'date'
|
2
2
|
module Barometer
|
3
3
|
#
|
4
|
-
#
|
5
|
-
# an array that holds multiple forecasts
|
4
|
+
# Result Array
|
6
5
|
#
|
7
|
-
|
6
|
+
# an array that holds multiple results,
|
7
|
+
# with methods for insertion and searching
|
8
|
+
#
|
9
|
+
class Measurement::ResultArray < Array
|
8
10
|
|
9
11
|
def <<(forecast)
|
10
|
-
raise ArgumentError unless forecast.is_a?(Measurement::
|
12
|
+
raise ArgumentError unless forecast.is_a?(Measurement::Result)
|
11
13
|
super(forecast)
|
12
14
|
end
|
13
15
|
|
@@ -17,31 +19,32 @@ module Barometer
|
|
17
19
|
|
18
20
|
#
|
19
21
|
# Returns a forecast for a day given by a Date, DateTime,
|
20
|
-
# Time, or a string that can be parsed to a
|
22
|
+
# Time, or a string that can be parsed to a Data::LocalDateTime
|
21
23
|
#
|
22
24
|
# credit: http://github.com/jdpace/weatherman/
|
23
25
|
#
|
24
|
-
def for(
|
26
|
+
def for(datetime)
|
25
27
|
|
26
28
|
return nil unless self.size > 0
|
27
29
|
|
28
30
|
# Format date into a Date class
|
29
|
-
|
31
|
+
datetime = case datetime.class.name
|
30
32
|
when 'Date'
|
31
|
-
date
|
33
|
+
# if just given a date, assume a time that will be mid-day
|
34
|
+
Data::LocalDateTime.new(datetime.year,datetime.month,datetime.day,12,0,0)
|
32
35
|
when 'Data::LocalDateTime'
|
33
|
-
|
36
|
+
datetime
|
34
37
|
when 'String'
|
35
|
-
|
38
|
+
Data::LocalDateTime.parse(datetime)
|
36
39
|
when 'Time'
|
37
|
-
|
40
|
+
Data::LocalDateTime.parse(datetime)
|
38
41
|
when 'DateTime'
|
39
|
-
|
42
|
+
Data::LocalDateTime.parse(datetime)
|
40
43
|
end
|
41
44
|
|
42
45
|
day = nil
|
43
46
|
self.each do |f|
|
44
|
-
day = f if
|
47
|
+
day = f if f.for_datetime?(datetime)
|
45
48
|
end
|
46
49
|
return day
|
47
50
|
end
|
@@ -102,7 +102,7 @@ MG : MN
|
|
102
102
|
|
103
103
|
WA : NA
|
104
104
|
NT : AN
|
105
|
-
NU: NI
|
105
|
+
NU : NI
|
106
106
|
NG : NE
|
107
107
|
NI : NG
|
108
108
|
|
@@ -127,7 +127,7 @@ LO : SK
|
|
127
127
|
BP : SB
|
128
128
|
SF : ZA
|
129
129
|
SP : ES
|
130
|
-
CE: LK
|
130
|
+
CE : LK
|
131
131
|
SU : SD
|
132
132
|
NS : SR
|
133
133
|
WZ : SZ
|
@@ -147,7 +147,7 @@ UP : UA
|
|
147
147
|
UK : GB
|
148
148
|
|
149
149
|
NH : VU
|
150
|
-
VM: VN
|
150
|
+
VM : VN
|
151
151
|
|
152
152
|
WI : EH
|
153
153
|
|
@@ -52,7 +52,7 @@ module Barometer
|
|
52
52
|
|
53
53
|
def self._build_current(data, metric=true)
|
54
54
|
raise ArgumentError unless data.is_a?(Hash)
|
55
|
-
current = Measurement::
|
55
|
+
current = Measurement::Result.new
|
56
56
|
|
57
57
|
if data['current_conditions']
|
58
58
|
data = data['current_conditions']
|
@@ -81,7 +81,7 @@ module Barometer
|
|
81
81
|
def self._build_forecast(data, metric=true)
|
82
82
|
raise ArgumentError unless data.is_a?(Hash)
|
83
83
|
|
84
|
-
forecasts = Measurement::
|
84
|
+
forecasts = Measurement::ResultArray.new
|
85
85
|
return forecasts unless data && data['forecast_information'] &&
|
86
86
|
data['forecast_information']['forecast_date']
|
87
87
|
start_date = Date.parse(data['forecast_information']['forecast_date']['data'])
|
@@ -90,7 +90,7 @@ module Barometer
|
|
90
90
|
# go through each forecast and create an instance
|
91
91
|
d = 0
|
92
92
|
data.each do |forecast|
|
93
|
-
forecast_measurement = Measurement::
|
93
|
+
forecast_measurement = Measurement::Result.new
|
94
94
|
if forecast['icon']
|
95
95
|
icon_match = forecast['icon']['data'].match(/.*\/([A-Za-z_]*)\.png/)
|
96
96
|
forecast_measurement.icon = icon_match[1] if icon_match
|
@@ -97,7 +97,7 @@ module Barometer
|
|
97
97
|
def self._build_current(data, metric=true)
|
98
98
|
raise ArgumentError unless data.is_a?(Hash)
|
99
99
|
|
100
|
-
current = Measurement::
|
100
|
+
current = Measurement::Result.new
|
101
101
|
# current.updated_at = Data::LocalDateTime.parse(data['observation_time']) if data['observation_time']
|
102
102
|
current.humidity = data['aws:humidity'].to_i
|
103
103
|
current.condition = data['aws:current_condition'] if data['aws:current_condition']
|
@@ -124,13 +124,13 @@ module Barometer
|
|
124
124
|
|
125
125
|
def self._build_forecast(data, metric=true)
|
126
126
|
raise ArgumentError unless data.is_a?(Hash)
|
127
|
-
forecasts = Measurement::
|
127
|
+
forecasts = Measurement::ResultArray.new
|
128
128
|
# go through each forecast and create an instance
|
129
129
|
if data && data["aws:forecast"]
|
130
130
|
start_date = Date.parse(data['date'])
|
131
131
|
i = 0
|
132
132
|
data["aws:forecast"].each do |forecast|
|
133
|
-
forecast_measurement = Measurement::
|
133
|
+
forecast_measurement = Measurement::Result.new
|
134
134
|
icon_match = forecast['aws:image'].match(/cond(\d*)\.gif$/)
|
135
135
|
forecast_measurement.icon = icon_match[1].to_i.to_s if icon_match
|
136
136
|
forecast_measurement.date = start_date + i
|