barometer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +266 -0
- data/VERSION.yml +4 -0
- data/bin/barometer +63 -0
- data/lib/barometer.rb +52 -0
- data/lib/barometer/base.rb +52 -0
- data/lib/barometer/data.rb +15 -0
- data/lib/barometer/data/current.rb +93 -0
- data/lib/barometer/data/distance.rb +131 -0
- data/lib/barometer/data/forecast.rb +66 -0
- data/lib/barometer/data/geo.rb +98 -0
- data/lib/barometer/data/location.rb +20 -0
- data/lib/barometer/data/measurement.rb +161 -0
- data/lib/barometer/data/pressure.rb +133 -0
- data/lib/barometer/data/speed.rb +147 -0
- data/lib/barometer/data/sun.rb +35 -0
- data/lib/barometer/data/temperature.rb +164 -0
- data/lib/barometer/data/units.rb +55 -0
- data/lib/barometer/data/zone.rb +124 -0
- data/lib/barometer/extensions/graticule.rb +50 -0
- data/lib/barometer/extensions/httparty.rb +21 -0
- data/lib/barometer/query.rb +228 -0
- data/lib/barometer/services.rb +6 -0
- data/lib/barometer/services/google.rb +146 -0
- data/lib/barometer/services/noaa.rb +6 -0
- data/lib/barometer/services/service.rb +324 -0
- data/lib/barometer/services/weather_bug.rb +6 -0
- data/lib/barometer/services/weather_dot_com.rb +6 -0
- data/lib/barometer/services/wunderground.rb +285 -0
- data/lib/barometer/services/yahoo.rb +274 -0
- data/lib/barometer/weather.rb +187 -0
- data/spec/barometer_spec.rb +162 -0
- data/spec/data_current_spec.rb +225 -0
- data/spec/data_distance_spec.rb +336 -0
- data/spec/data_forecast_spec.rb +150 -0
- data/spec/data_geo_spec.rb +90 -0
- data/spec/data_location_spec.rb +59 -0
- data/spec/data_measurement_spec.rb +411 -0
- data/spec/data_pressure_spec.rb +336 -0
- data/spec/data_speed_spec.rb +374 -0
- data/spec/data_sun_spec.rb +76 -0
- data/spec/data_temperature_spec.rb +396 -0
- data/spec/data_zone_spec.rb +133 -0
- data/spec/fixtures/current_calgary_ab.xml +1 -0
- data/spec/fixtures/forecast_calgary_ab.xml +1 -0
- data/spec/fixtures/geocode_40_73.xml +1 -0
- data/spec/fixtures/geocode_90210.xml +1 -0
- data/spec/fixtures/geocode_T5B4M9.xml +1 -0
- data/spec/fixtures/geocode_calgary_ab.xml +1 -0
- data/spec/fixtures/geocode_newyork_ny.xml +1 -0
- data/spec/fixtures/google_calgary_ab.xml +1 -0
- data/spec/fixtures/yahoo_90210.xml +1 -0
- data/spec/query_spec.rb +469 -0
- data/spec/service_google_spec.rb +144 -0
- data/spec/service_wunderground_spec.rb +330 -0
- data/spec/service_yahoo_spec.rb +299 -0
- data/spec/services_spec.rb +1106 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/units_spec.rb +101 -0
- data/spec/weather_spec.rb +265 -0
- metadata +119 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
# measurements and units
|
4
|
+
require 'data/measurement'
|
5
|
+
require 'data/current'
|
6
|
+
require 'data/forecast'
|
7
|
+
require 'data/zone'
|
8
|
+
require 'data/sun'
|
9
|
+
require 'data/geo'
|
10
|
+
require 'data/location'
|
11
|
+
require 'data/units'
|
12
|
+
require 'data/temperature'
|
13
|
+
require 'data/distance'
|
14
|
+
require 'data/speed'
|
15
|
+
require 'data/pressure'
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Barometer
|
2
|
+
#
|
3
|
+
# Current Measurement
|
4
|
+
# a data class for current weather conditions
|
5
|
+
#
|
6
|
+
# This is basically a data holding class for the current weather
|
7
|
+
# conditions.
|
8
|
+
#
|
9
|
+
class CurrentMeasurement
|
10
|
+
|
11
|
+
attr_accessor :time, :local_time
|
12
|
+
attr_reader :humidity, :icon, :condition
|
13
|
+
attr_reader :temperature, :dew_point, :heat_index, :wind_chill
|
14
|
+
attr_reader :wind, :pressure, :visibility, :sun
|
15
|
+
|
16
|
+
def time=(time)
|
17
|
+
#raise ArgumentError unless time.is_a?(Time)
|
18
|
+
@time = time
|
19
|
+
end
|
20
|
+
|
21
|
+
def humidity=(humidity)
|
22
|
+
raise ArgumentError unless
|
23
|
+
(humidity.is_a?(Fixnum) || humidity.is_a?(Float))
|
24
|
+
@humidity = humidity
|
25
|
+
end
|
26
|
+
|
27
|
+
def icon=(icon)
|
28
|
+
raise ArgumentError unless icon.is_a?(String)
|
29
|
+
@icon = icon
|
30
|
+
end
|
31
|
+
|
32
|
+
def condition=(condition)
|
33
|
+
raise ArgumentError unless condition.is_a?(String)
|
34
|
+
@condition = condition
|
35
|
+
end
|
36
|
+
|
37
|
+
def temperature=(temperature)
|
38
|
+
raise ArgumentError unless temperature.is_a?(Barometer::Temperature)
|
39
|
+
@temperature = temperature
|
40
|
+
end
|
41
|
+
|
42
|
+
def dew_point=(dew_point)
|
43
|
+
raise ArgumentError unless dew_point.is_a?(Barometer::Temperature)
|
44
|
+
@dew_point = dew_point
|
45
|
+
end
|
46
|
+
|
47
|
+
def heat_index=(heat_index)
|
48
|
+
raise ArgumentError unless heat_index.is_a?(Barometer::Temperature)
|
49
|
+
@heat_index = heat_index
|
50
|
+
end
|
51
|
+
|
52
|
+
def wind_chill=(wind_chill)
|
53
|
+
raise ArgumentError unless wind_chill.is_a?(Barometer::Temperature)
|
54
|
+
@wind_chill = wind_chill
|
55
|
+
end
|
56
|
+
|
57
|
+
def wind=(wind)
|
58
|
+
raise ArgumentError unless wind.is_a?(Barometer::Speed)
|
59
|
+
@wind = wind
|
60
|
+
end
|
61
|
+
|
62
|
+
def pressure=(pressure)
|
63
|
+
raise ArgumentError unless pressure.is_a?(Barometer::Pressure)
|
64
|
+
@pressure = pressure
|
65
|
+
end
|
66
|
+
|
67
|
+
def visibility=(visibility)
|
68
|
+
raise ArgumentError unless visibility.is_a?(Barometer::Distance)
|
69
|
+
@visibility = visibility
|
70
|
+
end
|
71
|
+
|
72
|
+
def sun=(sun)
|
73
|
+
raise ArgumentError unless sun.is_a?(Barometer::Sun)
|
74
|
+
@sun = sun
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# helpers
|
79
|
+
#
|
80
|
+
|
81
|
+
# creates "?" helpers for all attributes (which maps to nil?)
|
82
|
+
def method_missing(method,*args)
|
83
|
+
# if the method ends in ?, then strip it off and see if we
|
84
|
+
# respond to the method without the ?
|
85
|
+
if (call_method = method.to_s.chomp!("?")) && respond_to?(call_method)
|
86
|
+
return send(call_method).nil? ? false : true
|
87
|
+
else
|
88
|
+
super(method,*args)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module Barometer
|
2
|
+
#
|
3
|
+
# A simple Distance class
|
4
|
+
#
|
5
|
+
# Think of this like the Integer class. Enhancement
|
6
|
+
# is that you can create a number (in a certain unit), then
|
7
|
+
# get that number back already converted to another unit.
|
8
|
+
#
|
9
|
+
# All comparison operations will be done in metric
|
10
|
+
#
|
11
|
+
# NOTE: this currently only supports the scale of
|
12
|
+
# kilometers (km) and miles (m). There is currently
|
13
|
+
# no way to scale to smaller units (eg km -> m -> mm)
|
14
|
+
class Distance < Barometer::Units
|
15
|
+
|
16
|
+
METRIC_UNITS = "km"
|
17
|
+
IMPERIAL_UNITS = "m"
|
18
|
+
|
19
|
+
attr_accessor :kilometers, :miles
|
20
|
+
|
21
|
+
def initialize(metric=true)
|
22
|
+
@kilometers = nil
|
23
|
+
@miles = nil
|
24
|
+
super(metric)
|
25
|
+
end
|
26
|
+
|
27
|
+
def metric_default=(value); self.km = value; end
|
28
|
+
def imperial_default=(value); self.m = value; end
|
29
|
+
|
30
|
+
#
|
31
|
+
# CONVERTERS
|
32
|
+
#
|
33
|
+
|
34
|
+
def self.km_to_m(km)
|
35
|
+
return nil unless km && (km.is_a?(Integer) || km.is_a?(Float))
|
36
|
+
km.to_f * 0.622
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.m_to_km(m)
|
40
|
+
return nil unless m && (m.is_a?(Integer) || m.is_a?(Float))
|
41
|
+
m.to_f * 1.609
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# ACCESSORS
|
46
|
+
#
|
47
|
+
|
48
|
+
# store kilometers
|
49
|
+
def km=(km)
|
50
|
+
return if !km || !(km.is_a?(Integer) || km.is_a?(Float))
|
51
|
+
@kilometers = km.to_f
|
52
|
+
self.update_miles(km.to_f)
|
53
|
+
end
|
54
|
+
|
55
|
+
# store miles
|
56
|
+
def m=(m)
|
57
|
+
return if !m || !(m.is_a?(Integer) || m.is_a?(Float))
|
58
|
+
@miles = m.to_f
|
59
|
+
self.update_kilometers(m.to_f)
|
60
|
+
end
|
61
|
+
|
62
|
+
# return the stored kilometers or convert from miles
|
63
|
+
def km(as_integer=true)
|
64
|
+
km = (@kilometers || Distance.m_to_km(@miles))
|
65
|
+
km ? (as_integer ? km.to_i : (100*km).round/100.0) : nil
|
66
|
+
end
|
67
|
+
|
68
|
+
# return the stored miles or convert from kilometers
|
69
|
+
def m(as_integer=true)
|
70
|
+
m = (@miles || Distance.km_to_m(@kilometers))
|
71
|
+
m ? (as_integer ? m.to_i : (100*m).round/100.0) : nil
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# OPERATORS
|
76
|
+
#
|
77
|
+
|
78
|
+
def <=>(other)
|
79
|
+
self.km <=> other.km
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# HELPERS
|
84
|
+
#
|
85
|
+
|
86
|
+
# will just return the value (no units)
|
87
|
+
def to_i(metric=nil)
|
88
|
+
(metric || (metric.nil? && self.metric?)) ? self.km : self.m
|
89
|
+
end
|
90
|
+
|
91
|
+
# will just return the value (no units) with more precision
|
92
|
+
def to_f(metric=nil)
|
93
|
+
(metric || (metric.nil? && self.metric?)) ? self.km(false) : self.m(false)
|
94
|
+
end
|
95
|
+
|
96
|
+
# will return the value with units
|
97
|
+
def to_s(metric=nil)
|
98
|
+
(metric || (metric.nil? && self.metric?)) ? "#{self.km} #{METRIC_UNITS}" : "#{self.m} #{IMPERIAL_UNITS}"
|
99
|
+
end
|
100
|
+
|
101
|
+
# will just return the units (no value)
|
102
|
+
def units(metric=nil)
|
103
|
+
(metric || (metric.nil? && self.metric?)) ? METRIC_UNITS : IMPERIAL_UNITS
|
104
|
+
end
|
105
|
+
|
106
|
+
# when we set miles, it is possible the a non-equivalent value of
|
107
|
+
# kilometers remains. if so, clear it.
|
108
|
+
def update_kilometers(m)
|
109
|
+
return unless @kilometers
|
110
|
+
difference = Distance.m_to_km(m.to_f) - @kilometers
|
111
|
+
# only clear kilometers if the stored kilometers is off be more then 1 unit
|
112
|
+
# then the conversion of miles
|
113
|
+
@kilometers = nil unless difference.abs <= 1.0
|
114
|
+
end
|
115
|
+
|
116
|
+
# when we set kilometers, it is possible the a non-equivalent value of
|
117
|
+
# miles remains. if so, clear it.
|
118
|
+
def update_miles(km)
|
119
|
+
return unless @miles
|
120
|
+
difference = Distance.km_to_m(km.to_f) - @miles
|
121
|
+
# only clear miles if the stored miles is off be more then 1 unit
|
122
|
+
# then the conversion of kilometers
|
123
|
+
@miles = nil unless difference.abs <= 1.0
|
124
|
+
end
|
125
|
+
|
126
|
+
def nil?
|
127
|
+
(@kilometers || @miles) ? false : true
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'date'
|
2
|
+
module Barometer
|
3
|
+
#
|
4
|
+
# Forecast Measurement
|
5
|
+
# a data class for forecasted weather conditions
|
6
|
+
#
|
7
|
+
# This is basically a data holding class for the forecasted weather
|
8
|
+
# conditions.
|
9
|
+
#
|
10
|
+
class ForecastMeasurement
|
11
|
+
|
12
|
+
attr_reader :date, :icon, :condition
|
13
|
+
attr_reader :low, :high, :pop, :sun
|
14
|
+
|
15
|
+
def date=(date)
|
16
|
+
raise ArgumentError unless date.is_a?(Date)
|
17
|
+
@date = date
|
18
|
+
end
|
19
|
+
|
20
|
+
def icon=(icon)
|
21
|
+
raise ArgumentError unless icon.is_a?(String)
|
22
|
+
@icon = icon
|
23
|
+
end
|
24
|
+
|
25
|
+
def condition=(condition)
|
26
|
+
raise ArgumentError unless condition.is_a?(String)
|
27
|
+
@condition = condition
|
28
|
+
end
|
29
|
+
|
30
|
+
def high=(high)
|
31
|
+
raise ArgumentError unless high.is_a?(Barometer::Temperature)
|
32
|
+
@high = high
|
33
|
+
end
|
34
|
+
|
35
|
+
def low=(low)
|
36
|
+
raise ArgumentError unless low.is_a?(Barometer::Temperature)
|
37
|
+
@low = low
|
38
|
+
end
|
39
|
+
|
40
|
+
def pop=(pop)
|
41
|
+
raise ArgumentError unless pop.is_a?(Fixnum)
|
42
|
+
@pop = pop
|
43
|
+
end
|
44
|
+
|
45
|
+
def sun=(sun)
|
46
|
+
raise ArgumentError unless sun.is_a?(Barometer::Sun)
|
47
|
+
@sun = sun
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# helpers
|
52
|
+
#
|
53
|
+
|
54
|
+
# creates "?" helpers for all attributes (which maps to nil?)
|
55
|
+
def method_missing(method,*args)
|
56
|
+
# if the method ends in ?, then strip it off and see if we
|
57
|
+
# respond to the method without the ?
|
58
|
+
if (call_method = method.to_s.chomp!("?")) && respond_to?(call_method)
|
59
|
+
return send(call_method).nil? ? false : true
|
60
|
+
else
|
61
|
+
super(method,*args)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Barometer
|
2
|
+
#
|
3
|
+
# A simple Geo class
|
4
|
+
#
|
5
|
+
# Used to store location data from Graticule or HTTParty and convert
|
6
|
+
# into just the data needed for geocoding
|
7
|
+
#
|
8
|
+
class Geo
|
9
|
+
|
10
|
+
attr_accessor :latitude, :longitude
|
11
|
+
attr_accessor :locality, :region, :country, :country_code
|
12
|
+
|
13
|
+
#
|
14
|
+
# this will take a Location object (either generated by Graticule
|
15
|
+
# or HTTParty), and fill in the applicable data
|
16
|
+
#
|
17
|
+
def initialize(location=nil)
|
18
|
+
return unless location
|
19
|
+
|
20
|
+
has_graticule = false
|
21
|
+
begin
|
22
|
+
Graticule
|
23
|
+
has_graticule = true
|
24
|
+
rescue
|
25
|
+
# do nothing, Graticule not available
|
26
|
+
end
|
27
|
+
|
28
|
+
if has_graticule
|
29
|
+
raise ArgumentError unless
|
30
|
+
(location.is_a?(Graticule::Location) || location.is_a?(Hash))
|
31
|
+
else
|
32
|
+
raise ArgumentError unless location.is_a?(Hash)
|
33
|
+
end
|
34
|
+
|
35
|
+
if has_graticule && location.class == Graticule::Location
|
36
|
+
self.build_from_graticule(location)
|
37
|
+
elsif location.class == Hash
|
38
|
+
self.build_from_httparty(location)
|
39
|
+
end
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_from_graticule(location=nil)
|
44
|
+
return nil unless location
|
45
|
+
|
46
|
+
begin
|
47
|
+
require 'rubygems'
|
48
|
+
require 'graticule'
|
49
|
+
$:.unshift(File.dirname(__FILE__))
|
50
|
+
# load some changes to Graticule
|
51
|
+
# TODO: attempt to get changes into Graticule gem
|
52
|
+
require 'extensions/graticule'
|
53
|
+
end
|
54
|
+
|
55
|
+
raise ArgumentError unless location.is_a?(Graticule::Location)
|
56
|
+
|
57
|
+
@latitude = location.latitude
|
58
|
+
@longitude = location.longitude
|
59
|
+
@locality = location.locality
|
60
|
+
@region = location.region
|
61
|
+
@country = location.country
|
62
|
+
@country_code = location.country_code
|
63
|
+
end
|
64
|
+
|
65
|
+
def build_from_httparty(location=nil)
|
66
|
+
return nil unless location
|
67
|
+
raise ArgumentError unless location.is_a?(Hash)
|
68
|
+
|
69
|
+
placemark = location["Placemark"]
|
70
|
+
placemark = placemark.first if placemark.is_a?(Array)
|
71
|
+
|
72
|
+
if placemark && placemark["Point"] && placemark["Point"]["coordinates"]
|
73
|
+
@latitude = placemark["Point"]["coordinates"].split(',')[1].to_f
|
74
|
+
@longitude = placemark["Point"]["coordinates"].split(',')[0].to_f
|
75
|
+
end
|
76
|
+
if placemark && placemark["AddressDetails"] && placemark["AddressDetails"]["Country"]
|
77
|
+
if placemark["AddressDetails"]["Country"]["AdministrativeArea"]
|
78
|
+
if placemark["AddressDetails"]["Country"]["AdministrativeArea"]["SubAdministrativeArea"]
|
79
|
+
locality = placemark["AddressDetails"]["Country"]["AdministrativeArea"]["SubAdministrativeArea"]["Locality"]
|
80
|
+
else
|
81
|
+
locality = placemark["AddressDetails"]["Country"]["AdministrativeArea"]["Locality"]
|
82
|
+
end
|
83
|
+
if locality
|
84
|
+
@locality = locality["LocalityName"]
|
85
|
+
end
|
86
|
+
@region = placemark["AddressDetails"]["Country"]["AdministrativeArea"]["AdministrativeAreaName"]
|
87
|
+
end
|
88
|
+
@country = placemark["AddressDetails"]["Country"]["CountryName"]
|
89
|
+
@country_code = placemark["AddressDetails"]["Country"]["CountryNameCode"]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def coordinates
|
94
|
+
[@latitude, @longitude].join(',')
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Barometer
|
2
|
+
#
|
3
|
+
# A simple Location class
|
4
|
+
#
|
5
|
+
# Used to store location information about the station that
|
6
|
+
# gave the measurement data for a weather query, or the location
|
7
|
+
# that was queried
|
8
|
+
#
|
9
|
+
class Location
|
10
|
+
|
11
|
+
attr_accessor :id, :name, :city
|
12
|
+
attr_accessor :state_name, :state_code, :country, :country_code, :zip_code
|
13
|
+
attr_accessor :latitude, :longitude
|
14
|
+
|
15
|
+
def coordinates
|
16
|
+
[@latitude, @longitude].join(',')
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module Barometer
|
2
|
+
#
|
3
|
+
# Measurement
|
4
|
+
# a class that holds the response from a weather service
|
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
|
8
|
+
#
|
9
|
+
# this response includes
|
10
|
+
# - current weather data (using the CurrentMeasurement class)
|
11
|
+
# - forecasted weather data (an Array of instances of the ForecastMeasurement class)
|
12
|
+
# - time_zone information (for the location in question)
|
13
|
+
# - weather station information (for the station that gave collected the data)
|
14
|
+
#
|
15
|
+
class Measurement
|
16
|
+
|
17
|
+
# the weather service source
|
18
|
+
attr_reader :source
|
19
|
+
# current and forecasted data
|
20
|
+
attr_reader :current, :forecast
|
21
|
+
attr_reader :timezone, :station, :location
|
22
|
+
attr_reader :success, :time
|
23
|
+
attr_accessor :metric
|
24
|
+
|
25
|
+
def initialize(source=nil, metric=true)
|
26
|
+
@source = source
|
27
|
+
@metric = metric
|
28
|
+
@success = false
|
29
|
+
end
|
30
|
+
|
31
|
+
def success!
|
32
|
+
if current && current.temperature && !current.temperature.c.nil?
|
33
|
+
@success = true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def stamp!; @time = Time.now.utc; end
|
38
|
+
def success?; @success; end
|
39
|
+
def metric?; @metric; end
|
40
|
+
def metric!; @metric=true; end
|
41
|
+
def imperial!; @metric=false; end
|
42
|
+
|
43
|
+
# this will tell us if the measurement is still current ... if it is still
|
44
|
+
# current this means that the CurrentMeasurement can still used as now
|
45
|
+
#
|
46
|
+
# what it also means is that if you took a measurement right now (time = now)
|
47
|
+
# and then asked if current?(time_in_future) that current? would be true for
|
48
|
+
# any time_in_future within 4 hours of now
|
49
|
+
#
|
50
|
+
# where is this useful? lets say you take the measurement now (time = now),
|
51
|
+
# and then you want to know if self.windy?(5_hours_in_future) ... we could
|
52
|
+
# not use the current data for this answser as the time 5_hours_in_future
|
53
|
+
# is not current
|
54
|
+
def current?(utc_time=Time.now.utc)
|
55
|
+
return false unless @time
|
56
|
+
raise ArgumentError unless utc_time.is_a?(Time)
|
57
|
+
hours_still_current = 4
|
58
|
+
difference = (@time - utc_time).to_i
|
59
|
+
difference = (difference*(-1)).to_i if difference < 0
|
60
|
+
difference <= (60*60*hours_still_current).to_i
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Returns a forecast for a day given by a Date, DateTime,
|
65
|
+
# Time, or a string that can be parsed to a date
|
66
|
+
#
|
67
|
+
# credit: http://github.com/jdpace/weatherman/
|
68
|
+
#
|
69
|
+
def for(date=nil)
|
70
|
+
date = @timezone.today unless date || !@timezone
|
71
|
+
date ||= Date.today
|
72
|
+
return nil unless (@forecast && @forecast.size > 0)
|
73
|
+
|
74
|
+
# Format date into a Date class
|
75
|
+
date = case date.class.name
|
76
|
+
when 'String'
|
77
|
+
Date.parse(date)
|
78
|
+
when 'Date'
|
79
|
+
date
|
80
|
+
when 'DateTime'
|
81
|
+
Date.new(date.year, date.month, date.day)
|
82
|
+
when 'Time'
|
83
|
+
Date.new(date.year, date.month, date.day)
|
84
|
+
end
|
85
|
+
|
86
|
+
day = nil
|
87
|
+
@forecast.each do |f|
|
88
|
+
day = f if date == f.date
|
89
|
+
end
|
90
|
+
return day
|
91
|
+
end
|
92
|
+
|
93
|
+
def source=(source)
|
94
|
+
raise ArgumentError unless source.is_a?(Symbol)
|
95
|
+
@source = source
|
96
|
+
end
|
97
|
+
|
98
|
+
def time=(time=Time.now.utc)
|
99
|
+
raise ArgumentError unless time.is_a?(Time)
|
100
|
+
@time = time
|
101
|
+
end
|
102
|
+
|
103
|
+
def current=(current)
|
104
|
+
raise ArgumentError unless current.is_a?(Barometer::CurrentMeasurement)
|
105
|
+
@current = current
|
106
|
+
self.stamp!
|
107
|
+
# self-determine success
|
108
|
+
self.success!
|
109
|
+
end
|
110
|
+
|
111
|
+
def forecast=(forecast)
|
112
|
+
raise ArgumentError unless forecast.is_a?(Array)
|
113
|
+
@forecast = forecast
|
114
|
+
end
|
115
|
+
|
116
|
+
def timezone=(timezone)
|
117
|
+
#raise ArgumentError unless timezone.is_a?(String)
|
118
|
+
raise ArgumentError unless timezone.is_a?(Barometer::Zone)
|
119
|
+
@timezone = timezone
|
120
|
+
end
|
121
|
+
|
122
|
+
def station=(station)
|
123
|
+
raise ArgumentError unless station.is_a?(Barometer::Location)
|
124
|
+
@station = station
|
125
|
+
end
|
126
|
+
|
127
|
+
def location=(location)
|
128
|
+
raise ArgumentError unless location.is_a?(Barometer::Location)
|
129
|
+
@location = location
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# simple questions
|
134
|
+
# pass questions to the source
|
135
|
+
#
|
136
|
+
|
137
|
+
def windy?(threshold=10, utc_time=Time.now.utc)
|
138
|
+
raise ArgumentError unless (threshold.is_a?(Fixnum) || threshold.is_a?(Float))
|
139
|
+
raise ArgumentError unless utc_time.is_a?(Time)
|
140
|
+
Barometer::Service.source(@source).windy?(self, threshold, utc_time)
|
141
|
+
end
|
142
|
+
|
143
|
+
def wet?(threshold=50, utc_time=Time.now.utc)
|
144
|
+
raise ArgumentError unless (threshold.is_a?(Fixnum) || threshold.is_a?(Float))
|
145
|
+
raise ArgumentError unless utc_time.is_a?(Time)
|
146
|
+
Barometer::Service.source(@source).wet?(self, threshold, utc_time)
|
147
|
+
end
|
148
|
+
|
149
|
+
def day?(utc_time=Time.now.utc)
|
150
|
+
raise ArgumentError unless utc_time.is_a?(Time)
|
151
|
+
Barometer::Service.source(@source).day?(self, utc_time)
|
152
|
+
end
|
153
|
+
|
154
|
+
def sunny?(utc_time=Time.now.utc)
|
155
|
+
raise ArgumentError unless utc_time.is_a?(Time)
|
156
|
+
return false if self.day?(utc_time) == false
|
157
|
+
Barometer::Service.source(@source).sunny?(self, utc_time)
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|