weather_report 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +14 -0
- data/PostInstall.txt +2 -0
- data/README.rdoc +119 -0
- data/Rakefile +28 -0
- data/lib/weather_report.rb +221 -0
- data/script/console +10 -0
- data/script/console.cmd +1 -0
- data/script/destroy +14 -0
- data/script/destroy.cmd +1 -0
- data/script/generate +14 -0
- data/script/generate.cmd +1 -0
- data/test/test_helper.rb +3 -0
- data/test/test_weather_report.rb +115 -0
- metadata +91 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.rdoc
|
4
|
+
PostInstall.txt
|
5
|
+
Rakefile
|
6
|
+
lib/weather_report.rb
|
7
|
+
script/console
|
8
|
+
script/console.cmd
|
9
|
+
script/destroy
|
10
|
+
script/destroy.cmd
|
11
|
+
script/generate
|
12
|
+
script/generate.cmd
|
13
|
+
test/test_helper.rb
|
14
|
+
test/test_weather_report.rb
|
data/PostInstall.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
= weather_report
|
2
|
+
|
3
|
+
Homepage:: http://www.cordinc.com/projects/weather_report
|
4
|
+
Author:: Charles Cordingley
|
5
|
+
Copyright:: (c) 2009 Charles Cordingley
|
6
|
+
License:: MIT
|
7
|
+
|
8
|
+
== DESCRIPTION:
|
9
|
+
|
10
|
+
Connect to the BBC Backstage (http://backstage.bbc.co.uk) weather API and get weather observations and forecasts for thousands of cities worldwide. No
|
11
|
+
login is required to the BBC for use.
|
12
|
+
|
13
|
+
== TODO
|
14
|
+
|
15
|
+
* Allow weather to be shown in either metric or imperial units (currently only metric)
|
16
|
+
* Be able to seach or lookup location ids
|
17
|
+
* Add weather sources other than the BBC
|
18
|
+
|
19
|
+
== SYNOPSIS:
|
20
|
+
|
21
|
+
After requiring the library, create a WeatherReport object with a BBC weather location id:
|
22
|
+
|
23
|
+
require 'weather_report'
|
24
|
+
# 8 is the BBC Backstage weather code for London, UK
|
25
|
+
londonWeather = WeatherReport.new(8)
|
26
|
+
|
27
|
+
Then you can get the observations or forecasts for that location:
|
28
|
+
|
29
|
+
londonWeather.observation.temperature
|
30
|
+
londonWeather.forecast.for_tomorrow.max_temperature
|
31
|
+
|
32
|
+
Calls to the observation or forecast data after the first call for a location will just return return cached data. Use force_reload = true on the call to force
|
33
|
+
the data to be reloaded from the BBC.
|
34
|
+
|
35
|
+
# use the cached data
|
36
|
+
londonWeather.observation.reading_date
|
37
|
+
# reload the data
|
38
|
+
londonWeather.observation(force_reload = true).reading_date
|
39
|
+
|
40
|
+
Forecasts can be specified in a number of ways:
|
41
|
+
|
42
|
+
londonWeather.forecast.for_tomorrow.max_temperature
|
43
|
+
londonWeather.forecast.for_today.max_temperature
|
44
|
+
# also today
|
45
|
+
londonWeather.forecast.for(Date.today).max_temperature
|
46
|
+
# day after tomorrow
|
47
|
+
londonWeather.forecast.for(Date.today+2).max_temperature
|
48
|
+
# The date can also be specified as a date string
|
49
|
+
londonWeather.forecast.for("2009-05-06").max_temperature
|
50
|
+
# The underlying structure is an array, so you can just index into it. The below is the same as today
|
51
|
+
londonWeather.forecast[0].max_temperature
|
52
|
+
|
53
|
+
The full set of attributes is:
|
54
|
+
|
55
|
+
londonWeather.observation.reading_date
|
56
|
+
londonWeather.observation.name
|
57
|
+
londonWeather.observation.country
|
58
|
+
londonWeather.observation.latitude
|
59
|
+
londonWeather.observation.longtitude
|
60
|
+
londonWeather.observation.temperature
|
61
|
+
londonWeather.observation.wind_speed
|
62
|
+
londonWeather.observation.wind_direction
|
63
|
+
londonWeather.observation.humidity
|
64
|
+
londonWeather.observation.pressure
|
65
|
+
londonWeather.observation.pressure_state
|
66
|
+
londonWeather.observation.visibility
|
67
|
+
londonWeather.observation.humidity
|
68
|
+
|
69
|
+
londonWeather.forecast.reading_date
|
70
|
+
londonWeather.forecast.name
|
71
|
+
londonWeather.forecast.country
|
72
|
+
londonWeather.forecast.latitude
|
73
|
+
londonWeather.forecast.longtitude
|
74
|
+
londonWeather.forecast.for_today.date
|
75
|
+
londonWeather.forecast.for_today.max_temperature
|
76
|
+
londonWeather.forecast.for_today.min_temperature
|
77
|
+
londonWeather.forecast.for_today.wind_speed
|
78
|
+
londonWeather.forecast.for_today.wind_direction
|
79
|
+
londonWeather.forecast.for_today.humidity
|
80
|
+
londonWeather.forecast.for_today.pressure
|
81
|
+
londonWeather.forecast.for_today.visibility
|
82
|
+
londonWeather.forecast.for_today.humidity
|
83
|
+
|
84
|
+
== REQUIREMENTS:
|
85
|
+
|
86
|
+
The only external dependencies are REXML and net/http which are include as part of most Ruby installations.
|
87
|
+
|
88
|
+
== INSTALL:
|
89
|
+
|
90
|
+
The weather_report library is distributed itself as a RubyGem and is available immediately after installation.
|
91
|
+
|
92
|
+
sudo gem install weather_report
|
93
|
+
|
94
|
+
Alternately, download the gem and install manually.
|
95
|
+
|
96
|
+
== LICENSE:
|
97
|
+
|
98
|
+
(The MIT License)
|
99
|
+
|
100
|
+
Copyright (c) 2009 FIXME full name
|
101
|
+
|
102
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
103
|
+
a copy of this software and associated documentation files (the
|
104
|
+
'Software'), to deal in the Software without restriction, including
|
105
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
106
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
107
|
+
permit persons to whom the Software is furnished to do so, subject to
|
108
|
+
the following conditions:
|
109
|
+
|
110
|
+
The above copyright notice and this permission notice shall be
|
111
|
+
included in all copies or substantial portions of the Software.
|
112
|
+
|
113
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
114
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
115
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
116
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
117
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
118
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
119
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
2
|
+
require File.dirname(__FILE__) + '/lib/weather_report'
|
3
|
+
|
4
|
+
# Generate all the Rake tasks
|
5
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
|
+
$hoe = Hoe.new('weather_report', WeatherReport::VERSION) do |p|
|
7
|
+
p.developer('Charles Cordingley', 'inquiries at cordinc dot com')
|
8
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
9
|
+
p.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
10
|
+
p.rubyforge_name = 'weather-report'
|
11
|
+
# p.extra_deps = [
|
12
|
+
# ['activesupport','>= 2.0.2'],
|
13
|
+
# ]
|
14
|
+
p.extra_dev_deps = [
|
15
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
16
|
+
]
|
17
|
+
|
18
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
19
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
20
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
21
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
25
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
26
|
+
|
27
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
28
|
+
# task :default => [:spec, :features]
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require "rexml/document"
|
3
|
+
|
4
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
5
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
6
|
+
|
7
|
+
# Class containing weather observations and forecasts for the location specified by id in the constructor.
|
8
|
+
class WeatherReport
|
9
|
+
|
10
|
+
VERSION = '0.0.1'
|
11
|
+
|
12
|
+
# The id is the number (1..9999) the BBC uses to identify cities for weather reports
|
13
|
+
attr_reader :id
|
14
|
+
|
15
|
+
# Raised when there is no response from the server
|
16
|
+
class NoResponseError < StandardError
|
17
|
+
end
|
18
|
+
|
19
|
+
# Raised when the data returned from BBC is not in the expected format
|
20
|
+
class FormatError < StandardError
|
21
|
+
end
|
22
|
+
|
23
|
+
# Requires a valid BBC Backstage weather id (or any call to the API will return a FormatError)
|
24
|
+
def initialize(location_id)
|
25
|
+
@id = location_id
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the weather observation for the current id. If one is not currently loaded then this will go to the BBC Backstage API and download it.
|
29
|
+
# Alternatively, by specifying force_reload = true the observation will be loaded from the BBC regardless of whether one has already been downloaded.
|
30
|
+
def observation(force_reload = false)
|
31
|
+
@observation = WeatherReportObservation.new(fetch(observation_url())) if force_reload or @observation.nil?
|
32
|
+
@observation
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the weather forecast for the current id. If one is not currently loaded then this will go to the BBC Backstage API and download it.
|
36
|
+
# Alternatively, by specifying force_reload = true the observation will be loaded from the BBC regardless of whether one has already been downloaded.
|
37
|
+
def forecast(force_reload = false)
|
38
|
+
@forecast = WeatherReportForecasts.new(fetch(forecast_url())) if force_reload or @forecast.nil?
|
39
|
+
@forecast
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
# Fetch Response from the api
|
45
|
+
def fetch(url)
|
46
|
+
response = Net::HTTP.get_response(URI.parse(url)).body
|
47
|
+
|
48
|
+
# Check if a response was returned at all
|
49
|
+
raise(WeatherReport::NoResponseError, "WeatherReport Error: No Response.") unless response
|
50
|
+
|
51
|
+
response
|
52
|
+
end
|
53
|
+
|
54
|
+
# The url for getting current weather observations from BBC Backstage
|
55
|
+
def observation_url()
|
56
|
+
"http://news.bbc.co.uk/weather/forecast/#{@id}/ObservationsRSS.rss"
|
57
|
+
end
|
58
|
+
|
59
|
+
# The url for getting weather forecasts from BBC Backstage
|
60
|
+
def forecast_url()
|
61
|
+
"http://news.bbc.co.uk/weather/forecast/#{@id}/Next3DaysRSS.rss"
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
# Module to load and provide accessors for shared location specific
|
67
|
+
module Location
|
68
|
+
|
69
|
+
TITLE = /(.+) for ([\w -\.]*), ([\w -\.]*)/
|
70
|
+
|
71
|
+
attr_reader :name, :country, :latitude, :longtitude
|
72
|
+
|
73
|
+
# load the location based data (name, country, latitude, longtitude) from the backstage feed
|
74
|
+
def loadLocation(xmlDoc)
|
75
|
+
xmlDoc.elements.each("rss/channel/title") { |element|
|
76
|
+
md = TITLE.match(element.text)
|
77
|
+
@name = md[2] if md
|
78
|
+
@country = md[3] if md
|
79
|
+
}
|
80
|
+
|
81
|
+
xmlDoc.elements.each("rss/channel/item[1]/geo:lat") { |element| @latitude = element.text.to_f }
|
82
|
+
xmlDoc.elements.each("rss/channel/item[1]/geo:long") { |element| @longtitude = element.text.to_f }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# The current weather observations (at least at the time of reading - given by attribute reading_date)
|
87
|
+
class WeatherReportObservation
|
88
|
+
include Location
|
89
|
+
|
90
|
+
DESCRIPTION = /Temperature: ([-\d\.]+|N\/A|NA|\(none\))(.+)Wind Direction: ([\w -\/\(\)]*), Wind Speed: ([-\d\.]+|N\/A|NA|\(none\))mph, Relative Humidity: ([\d\.]+|N\/A|NA|\(none\))(.*), Pressure: ([\d\.]+|N\/A|NA|\(none\))mB, ([\w -\/]+), Visibility: ([\w -\/]+)/
|
91
|
+
SUMMARY = /(.+):(\W+)([\w -\/\(\)]+). (.+)/m
|
92
|
+
|
93
|
+
attr_reader :temperature, :wind_direction, :wind_speed, :visibility, :pressure, :pressure_state, :humidity, :reading_date
|
94
|
+
|
95
|
+
# Constructs the weather observation from an XML string or file containing the XML in BBC Backstage weather format
|
96
|
+
def initialize(document)
|
97
|
+
doc = REXML::Document.new document
|
98
|
+
|
99
|
+
loadLocation(doc)
|
100
|
+
@reading_date = DateTime.now
|
101
|
+
doc.elements.each("rss/channel/item[1]/title[1]") { |element|
|
102
|
+
md = SUMMARY.match(element.text)
|
103
|
+
@description = md[3]
|
104
|
+
}
|
105
|
+
|
106
|
+
doc.elements.each("rss/channel/item[1]/description[1]") { |element|
|
107
|
+
md = DESCRIPTION.match(element.text)
|
108
|
+
@temperature = md[1].to_f
|
109
|
+
@wind_direction = md[3]
|
110
|
+
@wind_speed = md[4].to_f * 1.61
|
111
|
+
@humidity = md[5].to_f
|
112
|
+
@pressure = md[7].to_f
|
113
|
+
@pressure_state = md[8]
|
114
|
+
@visibility = md[9]
|
115
|
+
}
|
116
|
+
|
117
|
+
rescue
|
118
|
+
raise WeatherReport::FormatError
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
# A collection of forecasts for a particular location
|
124
|
+
class WeatherReportForecasts < Array
|
125
|
+
include Location
|
126
|
+
|
127
|
+
attr_reader :reading_date
|
128
|
+
|
129
|
+
# Constructs the weather forecasts from an XML string or file containing the XML in BBC Backstage weather format
|
130
|
+
def initialize(document)
|
131
|
+
doc = REXML::Document.new document
|
132
|
+
@reading_date = DateTime.now
|
133
|
+
loadLocation(doc)
|
134
|
+
doc.elements.each("rss/channel/image/url") { |element| @image_url = element.text }
|
135
|
+
|
136
|
+
doc.elements.each("rss/channel/item/") { |element|
|
137
|
+
self << WeatherReportForecast.new(element)
|
138
|
+
}
|
139
|
+
|
140
|
+
rescue
|
141
|
+
raise WeatherReport::FormatError
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns the forecast for today, or nil is there is no such forecast.
|
145
|
+
def for_today
|
146
|
+
self.for(Date.today)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns the forecast for tomorrow, or nil is there is no such forecast.
|
150
|
+
def for_tomorrow
|
151
|
+
self.for(Date.today+1)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns a forecast for a day given by a Date, DateTime, Time, or a string that can be parsed to a date.
|
155
|
+
# If there is no forecast for the given date then nil is returned.
|
156
|
+
def for(date = Date.today)
|
157
|
+
date = case date.class.name
|
158
|
+
when 'String'
|
159
|
+
Date.parse(date)
|
160
|
+
when 'Date'
|
161
|
+
date
|
162
|
+
when 'DateTime'
|
163
|
+
Date.new(date.year, date.month, date.day)
|
164
|
+
when 'Time'
|
165
|
+
Date.new(date.year, date.month, date.day)
|
166
|
+
end
|
167
|
+
|
168
|
+
day = nil
|
169
|
+
self.each do |fd|
|
170
|
+
day = fd if date == fd.date
|
171
|
+
end
|
172
|
+
return day
|
173
|
+
end
|
174
|
+
|
175
|
+
# A forecast for a single day
|
176
|
+
class WeatherReportForecast
|
177
|
+
|
178
|
+
SUMMARY = /([\w -\/]+): ([\w -]+|N\/A|NA|\(none\)), Max Temp: (.*)/m
|
179
|
+
DESCRIPTION = /Max Temp: ([-\d\.]+|N\/A|NA|\(none\))(.+)Min Temp: ([-\d\.]+|N\/A|NA|\(none\))(.+)Wind Direction: ([\w -\/\(\)]*), Wind Speed: ([-\d\.]+|N\/A|NA|\(none\))mph, Visibility: ([\w -\/]+), Pressure: ([\d\.]+|N\/A|NA|\(none\))mB, Humidity: ([\d\.]+|N\/A|NA|\(none\))(.*), (.+)/m
|
180
|
+
|
181
|
+
attr_reader :max_temperature, :min_temperature, :wind_direction, :wind_speed, :visibility, :pressure, :humidity, :date
|
182
|
+
|
183
|
+
# Constructs the single day forecast from an REXML element containing the forecast in BBC Backstage weather format
|
184
|
+
def initialize(item)
|
185
|
+
item.elements.each("title[1]") { |element|
|
186
|
+
md = SUMMARY.match(element.text)
|
187
|
+
@description = md[2]
|
188
|
+
diff = day_diff(md[1])
|
189
|
+
raise(WeatherReport::FormatError, "WeatherReport Error: Day mismatch.") if diff.nil?
|
190
|
+
@date = Date.today+diff
|
191
|
+
}
|
192
|
+
|
193
|
+
item.elements.each("description[1]") { |element|
|
194
|
+
md = DESCRIPTION.match(element.text)
|
195
|
+
@max_temperature = md[1].to_f
|
196
|
+
@min_temperature = md[3].to_f
|
197
|
+
@wind_direction = md[5]
|
198
|
+
@wind_speed = md[6].to_f * 1.61
|
199
|
+
@visibility = md[7]
|
200
|
+
@pressure = md[8].to_f
|
201
|
+
@humidity = md[9].to_f
|
202
|
+
}
|
203
|
+
end
|
204
|
+
|
205
|
+
protected
|
206
|
+
|
207
|
+
# Calculate the number of days the given day name is from today's day name assuming it is no more than 5 days in the future or no further back than yesterday.
|
208
|
+
# Eg. If today is Wednesday, then if passed Tuesday this function will return -1, if passed Friday it will return 2, and if passed Wednesday it will return 0.
|
209
|
+
def day_diff(day_from)
|
210
|
+
start = Date::DAYNAMES.index(day_from.downcase.gsub!(/^[a-z]|\s+[a-z]/) { |a| a.upcase })
|
211
|
+
return if start.nil?
|
212
|
+
finish = Date.today.wday
|
213
|
+
result = start - finish
|
214
|
+
result = result+7 if result < -1
|
215
|
+
result = result-7 if result >5
|
216
|
+
result
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/weather_report.rb'}"
|
9
|
+
puts "Loading weather_report gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/console.cmd
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
@ruby script/console %*
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/destroy.cmd
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
@ruby script/destroy %*
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/script/generate.cmd
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
@ruby script/generate %*
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestWeatherReport < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_truth
|
9
|
+
w = WeatherReport.new(92)
|
10
|
+
assert_not_nil(w)
|
11
|
+
assert_equal(w.id, 92)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_fine_observation
|
15
|
+
obs = WeatherReportObservation.new(File.read(File.dirname(__FILE__) + '/BBC_ObservationsRSS.rss'))
|
16
|
+
assert_not_nil(obs)
|
17
|
+
assert_equal(obs.name, "Perth")
|
18
|
+
assert_equal(obs.country, "Australia")
|
19
|
+
assert_equal(obs.latitude, -31.93)
|
20
|
+
assert_equal(obs.longtitude, 115.97)
|
21
|
+
assert_equal(obs.temperature, 15)
|
22
|
+
assert_equal(obs.wind_direction, "SW")
|
23
|
+
assert_equal(obs.wind_speed, 1.61)
|
24
|
+
assert_equal(obs.visibility, "Very good")
|
25
|
+
assert_equal(obs.pressure, 1026)
|
26
|
+
assert_equal(obs.pressure_state, "rising")
|
27
|
+
assert_equal(obs.humidity, 52)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_no_wind_observation
|
31
|
+
obs = WeatherReportObservation.new(File.read(File.dirname(__FILE__) + '/BBC_no_wind_ObservationsRSS.rss'))
|
32
|
+
assert_not_nil(obs)
|
33
|
+
assert_equal(obs.name, "Perth")
|
34
|
+
assert_equal(obs.country, "Australia")
|
35
|
+
assert_equal(obs.latitude, -31.93)
|
36
|
+
assert_equal(obs.longtitude, 115.97)
|
37
|
+
assert_equal(obs.temperature, 10)
|
38
|
+
assert_equal(obs.wind_direction, "")
|
39
|
+
assert_equal(obs.wind_speed, 0)
|
40
|
+
assert_equal(obs.visibility, "Excellent")
|
41
|
+
assert_equal(obs.pressure, 1025)
|
42
|
+
assert_equal(obs.pressure_state, "falling")
|
43
|
+
assert_equal(obs.humidity, 63)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_error_observation
|
47
|
+
assert_raise(WeatherReport::FormatError) { WeatherReportObservation.new(File.read(File.dirname(__FILE__) + '/error.rss'))}
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_error_forecast
|
51
|
+
assert_raise(WeatherReport::FormatError) { WeatherReportForecasts.new(File.read(File.dirname(__FILE__) + '/error.rss'))}
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_current_forecast
|
55
|
+
doc = File.read(File.dirname(__FILE__) + '/BBC_Next3DaysRSS.rss')
|
56
|
+
doc = doc.gsub("<TODAY>", Date::DAYNAMES[Date.today.wday]).gsub("<TOMORROW>", Date::DAYNAMES[(Date.today+1).wday]).gsub("<DAY_AFTER_TOMORROW>", Date::DAYNAMES[(Date.today+2).wday])
|
57
|
+
f = WeatherReportForecasts.new(doc)
|
58
|
+
assert_equal(f.name, "Perth")
|
59
|
+
assert_equal(f.country, "Australia")
|
60
|
+
assert_equal(f.latitude, -31.93)
|
61
|
+
assert_equal(f.longtitude, 115.95)
|
62
|
+
|
63
|
+
assert_equal(f.for_today.max_temperature, 28)
|
64
|
+
assert_equal(f.for_today.min_temperature, 14)
|
65
|
+
assert_equal(f.for_today.wind_direction, "ENE")
|
66
|
+
assert_equal(f.for_today.wind_speed, 9.66)
|
67
|
+
assert_equal(f.for_today.visibility, "very good")
|
68
|
+
assert_equal(f.for_today.pressure, 1026)
|
69
|
+
assert_equal(f.for_today.humidity, 30)
|
70
|
+
|
71
|
+
assert_equal(f.for_tomorrow.max_temperature, 27)
|
72
|
+
assert_equal(f.for_tomorrow.min_temperature, 14)
|
73
|
+
assert_equal(f.for_tomorrow.wind_direction, "ENE")
|
74
|
+
assert((f.for_tomorrow.wind_speed - 11.27).abs < 0.01)
|
75
|
+
assert_equal(f.for_tomorrow.visibility, "very good")
|
76
|
+
assert_equal(f.for_tomorrow.pressure, 1028)
|
77
|
+
assert_equal(f.for_tomorrow.humidity, 33)
|
78
|
+
|
79
|
+
assert_equal(f.for(Date.today+2).max_temperature, 25)
|
80
|
+
assert_equal(f.for(Date.today+2).min_temperature, 14)
|
81
|
+
assert_equal(f.for(Date.today+2).wind_direction, "ENE")
|
82
|
+
assert_equal(f.for(Date.today+2).wind_speed, 14.49)
|
83
|
+
assert_equal(f.for(Date.today+2).visibility, "very good")
|
84
|
+
assert_equal(f.for(Date.today+2).pressure, 1028)
|
85
|
+
assert_equal(f.for(Date.today+2).humidity, 37)
|
86
|
+
|
87
|
+
assert_nil(f.for(Date.today+3))
|
88
|
+
assert_nil(f.for(Date.today-1))
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_shift_back_forecast
|
92
|
+
doc = File.read(File.dirname(__FILE__) + '/BBC_Next3DaysRSS.rss')
|
93
|
+
doc = doc.gsub("<TODAY>", Date::DAYNAMES[(Date.today-1).wday]).gsub("<TOMORROW>", Date::DAYNAMES[Date.today.wday]).gsub("<DAY_AFTER_TOMORROW>", Date::DAYNAMES[(Date.today+1).wday])
|
94
|
+
f = WeatherReportForecasts.new(doc)
|
95
|
+
|
96
|
+
assert_equal(f.for(Date.today-1).max_temperature, 28)
|
97
|
+
assert_equal(f.for(Date.today).max_temperature, 27)
|
98
|
+
assert_equal(f.for(Date.today+1).max_temperature, 25)
|
99
|
+
assert_nil(f.for(Date.today+2))
|
100
|
+
assert_nil(f.for(Date.today-2))
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_shift_forward_forecast
|
104
|
+
doc = File.read(File.dirname(__FILE__) + '/BBC_Next3DaysRSS.rss')
|
105
|
+
doc = doc.gsub("<TODAY>", Date::DAYNAMES[(Date.today+1).wday]).gsub("<TOMORROW>", Date::DAYNAMES[(Date.today+2).wday]).gsub("<DAY_AFTER_TOMORROW>", Date::DAYNAMES[(Date.today+3).wday])
|
106
|
+
f = WeatherReportForecasts.new(doc)
|
107
|
+
|
108
|
+
assert_equal(f.for_tomorrow.max_temperature, 28)
|
109
|
+
assert_equal(f.for(Date.today+2).max_temperature, 27)
|
110
|
+
assert_equal(f.for(Date.today+3).max_temperature, 25)
|
111
|
+
assert_nil(f.for_today)
|
112
|
+
assert_nil(f.for(Date.today+4))
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: weather_report
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Charles Cordingley
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-10 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: newgem
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.3.0
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: hoe
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.8.0
|
34
|
+
version:
|
35
|
+
description: Connect to the BBC Backstage (http://backstage.bbc.co.uk) weather API and get weather observations and forecasts for thousands of cities worldwide. No login is required to the BBC for use.
|
36
|
+
email:
|
37
|
+
- inquiries at cordinc dot com
|
38
|
+
executables: []
|
39
|
+
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files:
|
43
|
+
- History.txt
|
44
|
+
- Manifest.txt
|
45
|
+
- README.rdoc
|
46
|
+
- PostInstall.txt
|
47
|
+
files:
|
48
|
+
- History.txt
|
49
|
+
- Manifest.txt
|
50
|
+
- README.rdoc
|
51
|
+
- PostInstall.txt
|
52
|
+
- Rakefile
|
53
|
+
- lib/weather_report.rb
|
54
|
+
- script/console
|
55
|
+
- script/console.cmd
|
56
|
+
- script/destroy
|
57
|
+
- script/destroy.cmd
|
58
|
+
- script/generate
|
59
|
+
- script/generate.cmd
|
60
|
+
- test/test_helper.rb
|
61
|
+
- test/test_weather_report.rb
|
62
|
+
has_rdoc: true
|
63
|
+
homepage: "Homepage:: http://www.cordinc.com/projects/weather_report"
|
64
|
+
post_install_message: PostInstall.txt
|
65
|
+
rdoc_options:
|
66
|
+
- --main
|
67
|
+
- README.rdoc
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: "0"
|
81
|
+
version:
|
82
|
+
requirements: []
|
83
|
+
|
84
|
+
rubyforge_project: weather-report
|
85
|
+
rubygems_version: 1.3.1
|
86
|
+
signing_key:
|
87
|
+
specification_version: 2
|
88
|
+
summary: Connect to the BBC Backstage (http://backstage.bbc.co.uk) weather API and get weather observations and forecasts for thousands of cities worldwide
|
89
|
+
test_files:
|
90
|
+
- test/test_helper.rb
|
91
|
+
- test/test_weather_report.rb
|