barometer 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/README.rdoc +1 -1
  2. data/VERSION.yml +1 -1
  3. data/bin/barometer +4 -12
  4. data/lib/barometer/data.rb +2 -5
  5. data/lib/barometer/data/local_datetime.rb +2 -2
  6. data/lib/barometer/measurements/measurement.rb +6 -6
  7. data/lib/barometer/measurements/result.rb +207 -0
  8. data/lib/barometer/measurements/{forecast_array.rb → result_array.rb} +16 -13
  9. data/lib/barometer/translations/weather_country_codes.yml +3 -3
  10. data/lib/barometer/weather_services/google.rb +3 -3
  11. data/lib/barometer/weather_services/weather_bug.rb +3 -3
  12. data/lib/barometer/weather_services/weather_dot_com.rb +65 -38
  13. data/lib/barometer/weather_services/wunderground.rb +3 -3
  14. data/lib/barometer/weather_services/yahoo.rb +3 -3
  15. data/lib/demometer/demometer.rb +2 -2
  16. data/lib/demometer/public/css/master.css +4 -75
  17. data/lib/demometer/public/css/print.css +1 -2
  18. data/lib/demometer/public/css/syntax.css +1 -2
  19. data/lib/demometer/views/about.erb +1 -1
  20. data/lib/demometer/views/contributing.erb +4 -4
  21. data/lib/demometer/views/index.erb +8 -9
  22. data/lib/demometer/views/layout.erb +1 -2
  23. data/spec/measurements/measurement_spec.rb +29 -53
  24. data/spec/measurements/{forecast_array_spec.rb → result_array_spec.rb} +8 -8
  25. data/spec/measurements/result_spec.rb +660 -0
  26. data/spec/spec_helper.rb +1 -0
  27. data/spec/weather_services/google_spec.rb +4 -4
  28. data/spec/weather_services/services_spec.rb +1 -1
  29. data/spec/weather_services/weather_bug_spec.rb +3 -3
  30. data/spec/weather_services/weather_dot_com_spec.rb +19 -22
  31. data/spec/weather_services/wunderground_spec.rb +2 -2
  32. data/spec/weather_services/yahoo_spec.rb +2 -2
  33. data/spec/weather_spec.rb +14 -39
  34. metadata +6 -12
  35. data/lib/barometer/measurements/common.rb +0 -113
  36. data/lib/barometer/measurements/current.rb +0 -76
  37. data/lib/barometer/measurements/forecast.rb +0 -62
  38. data/lib/barometer/measurements/night.rb +0 -27
  39. data/spec/measurements/common_spec.rb +0 -352
  40. data/spec/measurements/current_spec.rb +0 -186
  41. data/spec/measurements/forecast_spec.rb +0 -135
  42. 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.0 is the current release of this gem.
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
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 1
2
+ :patch: 2
3
3
  :major: 0
4
4
  :minor: 6
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.0'
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
- "Date" => f.date, "Icon" => f.icon,
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
 
@@ -17,8 +17,5 @@ require 'data/local_datetime'
17
17
  # measurements
18
18
  #
19
19
  require 'measurements/measurement'
20
- require 'measurements/common'
21
- require 'measurements/current'
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 service
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
- current && current.temperature &&
35
- !current.temperature.c.nil? && @success = true
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::Current)
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::ForecastArray)
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
- # Forecast Array
5
- # an array that holds multiple forecasts
4
+ # Result Array
6
5
  #
7
- class Measurement::ForecastArray < Array
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::Forecast)
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 date
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(date)
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
- date = case date.class.name
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
- date.to_d
36
+ datetime
34
37
  when 'String'
35
- Date.parse(date)
38
+ Data::LocalDateTime.parse(datetime)
36
39
  when 'Time'
37
- Date.new(date.year, date.month, date.day)
40
+ Data::LocalDateTime.parse(datetime)
38
41
  when 'DateTime'
39
- Date.new(date.year, date.month, date.day)
42
+ Data::LocalDateTime.parse(datetime)
40
43
  end
41
44
 
42
45
  day = nil
43
46
  self.each do |f|
44
- day = f if date == f.date
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::Current.new
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::ForecastArray.new
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::Forecast.new
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::Current.new
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::ForecastArray.new
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::Forecast.new
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