attack-barometer 0.1.0 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +1 -1
- data/README.rdoc +53 -7
- data/VERSION.yml +2 -2
- data/bin/barometer +407 -53
- data/lib/barometer/data/geo.rb +3 -1
- data/lib/barometer/extensions/graticule.rb +3 -2
- data/lib/barometer/query.rb +57 -8
- data/lib/barometer/services/wunderground.rb +3 -2
- data/lib/barometer/weather.rb +45 -42
- data/spec/barometer_spec.rb +1 -1
- data/spec/data_geo_spec.rb +4 -0
- data/spec/fixtures/geocode_ksfo.xml +1 -0
- data/spec/query_spec.rb +49 -5
- data/spec/service_wunderground_spec.rb +1 -1
- data/spec/spec_helper.rb +25 -0
- data/spec/weather_spec.rb +168 -0
- metadata +3 -2
data/LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -8,28 +8,43 @@ information, or they can be used in a hierarchical configuration where lower
|
|
8
8
|
preferred weather services are only used if previous services are
|
9
9
|
unavailable.
|
10
10
|
|
11
|
+
== version
|
12
|
+
|
13
|
+
Version 0.1.0 is the current release of this gem.
|
14
|
+
The gem is available from github (attack-barometer) or rubyforge (barometer).
|
15
|
+
It is fully functional (for three weather service APIs).
|
16
|
+
|
11
17
|
== status
|
12
18
|
|
13
19
|
Currently this project is in development and will only work for a few weather
|
14
20
|
services (wunderground, google, yahoo).
|
15
21
|
|
16
|
-
Features to be added
|
17
|
-
- gem setup/config, apply to rubyforge
|
18
|
-
|
19
|
-
Features to be added in future releases:
|
22
|
+
Features to be added next:
|
20
23
|
- even more weather service drivers (noaa, weather.com, weatherbug)
|
21
|
-
- ability to query multiple services and combine/average the results
|
22
|
-
- support iaco as a query format
|
23
24
|
|
24
25
|
= dependencies
|
25
26
|
|
27
|
+
=== Google API key
|
28
|
+
|
29
|
+
In most cases you will need to have a free google geocode api key.
|
30
|
+
Get one here: http://code.google.com/apis/maps/signup.html
|
31
|
+
Then put it in the file '~/.barometer' by adding the line:
|
32
|
+
geocode_google: YOUR_KEY_HERE
|
33
|
+
|
34
|
+
You will need this for:
|
35
|
+
- using the Barometer gem (unless you use queries that are directly supported
|
36
|
+
by the weather source API, ie yahoo will take a zip code directly and doesn't
|
37
|
+
require any geocoding)
|
38
|
+
- running the Barometer binary
|
39
|
+
- running the Barometer Web Demo
|
40
|
+
|
26
41
|
=== HTTParty
|
27
42
|
|
28
43
|
Why? HTTParty was created and designed specifically for consuming web services.
|
29
44
|
I choose to use this over using the Net::HTTP library directly to allow for
|
30
45
|
faster development of this project.
|
31
46
|
|
32
|
-
HTTParty is also extended to include configurable
|
47
|
+
HTTParty is also extended to include configurable Timeout support.
|
33
48
|
|
34
49
|
=== tzinfo
|
35
50
|
|
@@ -96,6 +111,18 @@ You can use barometer from the command line.
|
|
96
111
|
# barometer berlin
|
97
112
|
|
98
113
|
This will output the weather information for the given query.
|
114
|
+
See the help for more command line information.
|
115
|
+
|
116
|
+
# barometer -h
|
117
|
+
|
118
|
+
=== web demo
|
119
|
+
|
120
|
+
There is a Sinatra application that demos the functionality of Barometer,
|
121
|
+
and provides Barometer information. Start this local demo with:
|
122
|
+
|
123
|
+
# barometer -w
|
124
|
+
|
125
|
+
NOTE: This requires the gems "sinatra" and "vegas".
|
99
126
|
|
100
127
|
=== fail
|
101
128
|
|
@@ -188,6 +215,25 @@ the data as shown in the above examples.
|
|
188
215
|
|
189
216
|
puts weather.source(:wunderground).for(time).low.f
|
190
217
|
|
218
|
+
== averages
|
219
|
+
|
220
|
+
If you consume more then one weather service, Barometer can provide averages
|
221
|
+
for the values (currently only for the 'current' values and not the forecasted
|
222
|
+
values).
|
223
|
+
|
224
|
+
require 'barometer'
|
225
|
+
|
226
|
+
Barometer.google_geocode_key = "THE_GOOGLE_API_KEY"
|
227
|
+
# use yahoo and wunderground
|
228
|
+
Barometer.selection = { 1 => [:yahoo, :wunderground] }
|
229
|
+
|
230
|
+
barometer = Barometer.new("90210")
|
231
|
+
weather = barometer.measure
|
232
|
+
|
233
|
+
puts weather.temperture
|
234
|
+
|
235
|
+
This will calculate the average temperature as given by :yahoo and :wunderground
|
236
|
+
|
191
237
|
== simple answers
|
192
238
|
|
193
239
|
After you have measured the data, Barometer provides several "simple answer"
|
data/VERSION.yml
CHANGED
data/bin/barometer
CHANGED
@@ -1,63 +1,417 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
|
4
|
-
#
|
5
|
-
#
|
6
|
-
|
7
|
-
#
|
8
|
-
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
2
|
+
|
3
|
+
# == Barometer
|
4
|
+
# This is the command line interface to the barometer gem.
|
5
|
+
#
|
6
|
+
# == Examples
|
7
|
+
# This command will measure the weather for the given query.
|
8
|
+
# barometer berlin
|
9
|
+
#
|
10
|
+
# Other examples:
|
11
|
+
# barometer --yahoo 90210
|
12
|
+
# barometer --verbose 'new york'
|
13
|
+
#
|
14
|
+
# == Local Web Demo
|
15
|
+
# You can easily interact directly with barometer with the command:
|
16
|
+
# barometer -w
|
17
|
+
#
|
18
|
+
# This demo has 2 gem requirements:
|
19
|
+
# - sinatra (tested with 0.9.1.1)
|
20
|
+
# - vegas (tested with 0.0.1)
|
21
|
+
#
|
22
|
+
# == Usage
|
23
|
+
# barometer [options] query
|
24
|
+
#
|
25
|
+
# For help use: barometer -h
|
26
|
+
#
|
27
|
+
# Options:
|
28
|
+
# -v, --version Display the version, then exit
|
29
|
+
# -V, --verbose Verbose output
|
30
|
+
# -t, --timeout seconds until service queries will timeout
|
31
|
+
# -g, --geocode Force Geocoding of query
|
32
|
+
# -m, --metric measure in metric
|
33
|
+
# -i, --imperial measure in imperial
|
34
|
+
# --no-wunderground DONT use default wunderground as source
|
35
|
+
# --yahoo use yahoo as source
|
36
|
+
# --google use google as source
|
37
|
+
# -p, --pop pop threshold used to determine wet?
|
38
|
+
# -s, --wind wind speed threshold used to determine windy?
|
39
|
+
# -a, --at time/date used to determine when to calculate summary
|
40
|
+
#
|
41
|
+
# Web Demo:
|
42
|
+
# -w, --web run web-app with barometer demo
|
43
|
+
# -k, --kill stop the web demo background process
|
44
|
+
# -S, --status show the web demo status
|
45
|
+
#
|
46
|
+
# == Author
|
47
|
+
# Mark G
|
48
|
+
# http://github.com/attack/barometer
|
49
|
+
#
|
50
|
+
# == Copyright
|
51
|
+
# Copyright (c) 2009 Mark G. Licensed under the MIT License:
|
52
|
+
# http://www.opensource.org/licenses/mit-license.php
|
53
|
+
|
54
|
+
require 'rubygems'
|
55
|
+
require 'barometer'
|
56
|
+
|
57
|
+
require 'optparse'
|
58
|
+
require 'rdoc/usage'
|
59
|
+
require 'ostruct'
|
60
|
+
require 'time'
|
61
|
+
require 'date'
|
62
|
+
require 'yaml'
|
63
|
+
|
64
|
+
# file where API keys are stored
|
65
|
+
KEY_FILE = File.expand_path(File.join('~', '.barometer'))
|
66
|
+
|
67
|
+
class App
|
68
|
+
VERSION = '0.2.3'
|
69
|
+
|
70
|
+
attr_reader :options
|
71
|
+
|
72
|
+
def initialize(arguments, stdin)
|
73
|
+
@arguments = arguments.dup
|
74
|
+
|
75
|
+
# Set defaults
|
76
|
+
@options = OpenStruct.new
|
77
|
+
@options.timeout = 15
|
78
|
+
@options.geocode = false
|
79
|
+
@options.skip_graticule = false
|
80
|
+
@options.metric = true
|
81
|
+
@options.sources = [:wunderground]
|
82
|
+
@options.verbode = false
|
83
|
+
@options.web = false
|
84
|
+
@options.at = Time.now.utc
|
85
|
+
|
86
|
+
# thresholds
|
87
|
+
@options.windy_m = 10
|
88
|
+
@options.windy_i = 7
|
89
|
+
@options.pop = 50
|
90
|
+
end
|
91
|
+
|
92
|
+
# Parse options, check arguments, then process the command
|
93
|
+
def run
|
94
|
+
if parsed_options? && arguments_valid?
|
95
|
+
puts "Start at #{DateTime.now}\n\n" if @options.verbose
|
96
|
+
output_options if @options.verbose # [Optional]
|
97
|
+
|
98
|
+
process_arguments
|
99
|
+
process_command
|
100
|
+
|
101
|
+
puts "\nFinished at #{DateTime.now}" if @options.verbose
|
102
|
+
else
|
103
|
+
output_usage
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
protected
|
108
|
+
|
109
|
+
# future options
|
110
|
+
#
|
111
|
+
# time: -a --at
|
112
|
+
#
|
113
|
+
def parsed_options?
|
114
|
+
# Specify options
|
115
|
+
opt = OptionParser.new
|
116
|
+
opt.on('-v', '--version') { output_version ; exit 0 }
|
117
|
+
opt.on('-h', '--help') { output_help }
|
118
|
+
opt.on('-V', '--verbose') { @options.verbose = true }
|
119
|
+
opt.on('-a n', '--at n') {|n| @options.at = Time.parse(n.to_s) }
|
120
|
+
opt.on('-t n', '--timeout n') {|n| @options.timeout = n }
|
121
|
+
opt.on('-w', '--web') { @options.web = true; ARGV.shift }
|
122
|
+
opt.on('-g', '--geocode') { @options.geocode = true }
|
123
|
+
opt.on('--skip') { @options.skip_graticule = true }
|
124
|
+
opt.on('-m', '--metric') { @options.metric = true }
|
125
|
+
opt.on('-i', '--imperial') { @options.metric = false }
|
126
|
+
opt.on('--no-wunderground') { @options.sources = @options.sources.delete_if{|s| s == :wunderground} }
|
127
|
+
opt.on('--yahoo') { @options.sources << :yahoo }
|
128
|
+
opt.on('--google') { @options.sources << :google }
|
129
|
+
opt.on('-p n', '--pop n') {|n| @options.pop = n.to_i || 50 }
|
130
|
+
opt.on('-s n', '--wind n') {|n| @options.metric ? @options.windy_m = n.to_f || 10 : @options.windy_i = n.to_f || 7 }
|
131
|
+
|
132
|
+
# pass these onto vegas
|
133
|
+
opt.on('-k', '--kill') { @options.web = true }
|
134
|
+
opt.on('-S', '--status') { @options.web = true }
|
135
|
+
|
136
|
+
opt.parse!(@arguments) rescue return false
|
137
|
+
|
138
|
+
process_options
|
139
|
+
true
|
140
|
+
end
|
141
|
+
|
142
|
+
# Performs post-parse processing on options
|
143
|
+
def process_options
|
144
|
+
Barometer.force_geocode = @options.geocode
|
145
|
+
Barometer.selection = { 1 => @options.sources.uniq }
|
146
|
+
Barometer.skip_graticule = @options.skip_graticule
|
147
|
+
Barometer.timeout = @options.timeout
|
148
|
+
end
|
149
|
+
|
150
|
+
def output_options
|
151
|
+
puts "Options:\n"
|
152
|
+
|
153
|
+
@options.marshal_dump.each do |name, val|
|
154
|
+
puts " #{name} = #{val}"
|
155
|
+
end
|
156
|
+
puts
|
157
|
+
end
|
158
|
+
|
159
|
+
# True if required arguments were provided
|
160
|
+
def arguments_valid?
|
161
|
+
true if (@arguments.length >= 1 || @options.web)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Setup the arguments
|
165
|
+
def process_arguments
|
166
|
+
#puts @arguments.inspect
|
167
|
+
end
|
168
|
+
|
169
|
+
def output_help
|
170
|
+
output_version
|
171
|
+
RDoc::usage() #exits app
|
172
|
+
end
|
173
|
+
|
174
|
+
def output_usage
|
175
|
+
RDoc::usage('usage') # gets usage from comments above
|
176
|
+
end
|
177
|
+
|
178
|
+
def output_version
|
179
|
+
puts "#{File.basename(__FILE__)} version #{VERSION}"
|
180
|
+
end
|
181
|
+
|
182
|
+
def process_command
|
183
|
+
if @options.web
|
184
|
+
run_web_mode(@arguments.join(" "))
|
185
|
+
else
|
186
|
+
barometer = Barometer.new(@arguments.join(" "))
|
187
|
+
begin
|
188
|
+
barometer.measure(@options.metric) if barometer
|
189
|
+
pretty_output(barometer) if barometer.weather
|
190
|
+
rescue Barometer::OutOfSources
|
191
|
+
puts
|
192
|
+
puts " SORRY: your query did not provide any results"
|
193
|
+
puts
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
35
197
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
198
|
+
|
199
|
+
#
|
200
|
+
# HELPERS
|
201
|
+
#
|
202
|
+
|
203
|
+
@level = 1
|
39
204
|
|
40
205
|
def y(value)
|
41
206
|
value ? "yes" : "no"
|
42
207
|
end
|
43
208
|
|
44
|
-
|
45
|
-
puts
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
puts " --
|
51
|
-
|
209
|
+
def div(char="#")
|
210
|
+
puts char*50
|
211
|
+
end
|
212
|
+
|
213
|
+
def title(title, level=1)
|
214
|
+
@level = level
|
215
|
+
puts "#{" " * @level}-- #{title} --"
|
216
|
+
end
|
217
|
+
|
218
|
+
def value(title, value)
|
219
|
+
puts "#{" " * @level}#{title}: #{value}" unless value.nil?
|
220
|
+
end
|
221
|
+
|
222
|
+
def blank
|
52
223
|
puts
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
224
|
+
end
|
225
|
+
|
226
|
+
def section(title, level=1, show_blank=true)
|
227
|
+
@level = level
|
228
|
+
title(title, level); yield; blank if show_blank
|
229
|
+
end
|
230
|
+
|
231
|
+
def pretty_hash(hash)
|
232
|
+
return unless hash.is_a?(Hash)
|
233
|
+
hash.each { |k,v| value(k,v) }
|
234
|
+
end
|
235
|
+
|
236
|
+
def pretty_summary(s)
|
237
|
+
return unless s
|
238
|
+
section("AVERAGES") do
|
239
|
+
pretty_hash({
|
240
|
+
"humidity" => s.humidity.to_i, "temperature" => s.temperature })
|
241
|
+
end
|
242
|
+
section("SUMMARY#{ " (@ #{@options.at})" if @options.at }") do
|
243
|
+
pretty_hash({
|
244
|
+
"day?" => s.day?(@options.at), "sunny?" => s.sunny?(@options.at),
|
245
|
+
"windy?" => s.windy?(@options.metric ? @options.windy_m : @options.windy_i, @options.at),
|
246
|
+
"wet?" => s.wet?(@options.pop,@options.at) })
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def pretty_query(q)
|
251
|
+
return unless q
|
252
|
+
section("QUERY", 2) do
|
253
|
+
pretty_hash({"Format" => q.format})
|
254
|
+
pretty_hash({
|
255
|
+
"Address" => q.geo.address,
|
256
|
+
"Locality" => q.geo.locality, "Region" => q.geo.region,
|
257
|
+
"Country" => q.geo.country, "Country Code" => q.geo.country_code,
|
258
|
+
"Latitude" => q.geo.latitude, "Longitude" => q.geo.longitude }) if q.geo
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def pretty_location(l)
|
263
|
+
return unless l
|
264
|
+
section("LOCATION", 2) do
|
265
|
+
pretty_hash({
|
266
|
+
"ID" => l.id, "Name" => l.name,
|
267
|
+
"City" => l.city, "State Name" => l.state_name,
|
268
|
+
"State Code" => l.state_code, "Country" => l.country,
|
269
|
+
"Country Code" => l.country_code, "Zip Code" => l.zip_code,
|
270
|
+
"Latitude" => l.latitude, "Longitude" => l.longitude })
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def pretty_station(s)
|
275
|
+
return unless s
|
276
|
+
section("STATION", 2) do
|
277
|
+
pretty_hash({
|
278
|
+
"ID" => s.id, "Name" => s.name,
|
279
|
+
"City" => s.city, "State Name" => s.state_name,
|
280
|
+
"State Code" => s.state_code, "Country" => s.country,
|
281
|
+
"Country Code" => s.country_code, "Zip Code" => s.zip_code,
|
282
|
+
"Latitude" => s.latitude, "Longitude" => s.longitude })
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def pretty_timezone(t)
|
287
|
+
return unless t
|
288
|
+
section("TIMEZONE", 2) do
|
289
|
+
pretty_hash({ "Long" => t.timezone, "Code" => t.code,"DST?" => t.dst? })
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def pretty_current(c)
|
294
|
+
return unless c
|
295
|
+
section("CURRENT", 2) do
|
296
|
+
pretty_hash({
|
297
|
+
"Time" => c.time, "Local Time" => c.local_time,
|
298
|
+
"Humidity" => c.humidity, "Icon" => c.icon,
|
299
|
+
"Condition" => c.condition, "Temperature" => c.temperature,
|
300
|
+
"Dew Point" => c.dew_point, "Heat Index" => c.heat_index,
|
301
|
+
"Pressure" => c.pressure, "Visibility" => c.visibility })
|
302
|
+
pretty_hash({ "Wind Chill" => c.wind_chill, "Wind" => c.wind,
|
303
|
+
"Wind Direction" => c.wind.direction, "Degrees" => c.wind.degrees }) if c.wind
|
304
|
+
pretty_hash({ "Sun Rise" => c.sun.rise, "Sun Set" => c.sun.set }) if c.sun
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def pretty_forecast(f)
|
309
|
+
return unless f
|
310
|
+
section("FOR: #{f.date}", 3) do
|
311
|
+
pretty_hash({
|
312
|
+
"Date" => f.date, "Icon" => f.icon,
|
313
|
+
"Condition" => f.condition, "High" => f.high,
|
314
|
+
"Low" => f.low, "POP" => f.pop })
|
315
|
+
pretty_hash({ "Sun Rise" => f.sun.rise, "Sun Set" => f.sun.set }) if f.sun
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def pretty_forecasts(forecasts)
|
320
|
+
return unless forecasts
|
321
|
+
section("FORECAST", 3, false) do
|
322
|
+
blank
|
323
|
+
forecasts.each do |forecast|
|
324
|
+
pretty_forecast(forecast)
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def pretty_measurement(m)
|
330
|
+
return unless m
|
331
|
+
section(m.source.to_s, 1) do
|
332
|
+
pretty_hash({
|
333
|
+
"Source" => m.source, "Time" => m.time,
|
334
|
+
"Metric" => m.metric?, "Success" => m.success? })
|
335
|
+
end
|
336
|
+
pretty_location(m.location)
|
337
|
+
pretty_station(m.station)
|
338
|
+
pretty_timezone(m.timezone)
|
339
|
+
pretty_current(m.current)
|
340
|
+
pretty_forecasts(m.forecast)
|
341
|
+
end
|
342
|
+
|
343
|
+
def pretty_measurements(w)
|
344
|
+
return unless w
|
345
|
+
section("MEASUREMENTS", 1) do
|
346
|
+
blank
|
347
|
+
w.measurements.each do |m|
|
348
|
+
pretty_measurement(m)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def pretty_info
|
354
|
+
title("INFO", 1)
|
355
|
+
value("GitHub", "http://github.com/attack/barometer")
|
356
|
+
value("Barometer Version", VERSION)
|
357
|
+
end
|
358
|
+
|
359
|
+
def pretty_output(barometer)
|
360
|
+
weather = barometer.weather
|
361
|
+
if weather
|
362
|
+
div
|
363
|
+
puts "#"
|
364
|
+
puts "# #{weather.default.location.name || barometer.query.q}"
|
365
|
+
puts "#"
|
366
|
+
div
|
367
|
+
blank
|
368
|
+
pretty_summary(weather)
|
369
|
+
pretty_query(barometer.query)
|
370
|
+
pretty_measurements(weather)
|
371
|
+
pretty_info
|
372
|
+
div("-")
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
def run_web_mode(query=nil)
|
377
|
+
|
378
|
+
puts
|
379
|
+
puts "This is currently disabled"
|
380
|
+
puts
|
381
|
+
|
382
|
+
#require File.expand_path(File.dirname(__FILE__) + '/../lib/webometer/webometer.rb')
|
383
|
+
#require 'vegas'
|
384
|
+
|
385
|
+
#Vegas::Runner.new(Webometer, 'webometer') do |opts, app|
|
386
|
+
# opts is an option parser object
|
387
|
+
# app is your app class
|
388
|
+
#end
|
389
|
+
end
|
390
|
+
|
391
|
+
def geocode_google_key_message
|
58
392
|
puts
|
393
|
+
puts "Please update the key_file '#{KEY_FILE}' with your google api key"
|
394
|
+
puts "Get it here: http://code.google.com/apis/maps/signup.html"
|
395
|
+
puts "The, add this line to the file:"
|
396
|
+
puts "geocode_google: YOUR_KEY_KERE"
|
59
397
|
puts
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
398
|
+
end
|
399
|
+
|
400
|
+
# set API keys
|
401
|
+
if File.exists?(KEY_FILE)
|
402
|
+
keys = YAML.load_file(KEY_FILE)
|
403
|
+
if keys["geocode_google"]
|
404
|
+
Barometer.google_geocode_key = keys["geocode_google"]
|
405
|
+
else
|
406
|
+
geocode_google_key_message
|
407
|
+
exit
|
408
|
+
end
|
409
|
+
else
|
410
|
+
File.open(KEY_FILE, 'w') {|f| f << "geocode_google: YOUR_KEY_KERE" }
|
411
|
+
geocode_google_key_message
|
412
|
+
exit
|
413
|
+
end
|
414
|
+
|
415
|
+
# Create and run the application
|
416
|
+
app = App.new(ARGV, STDIN)
|
417
|
+
app.run
|
data/lib/barometer/data/geo.rb
CHANGED
@@ -8,7 +8,7 @@ module Barometer
|
|
8
8
|
class Geo
|
9
9
|
|
10
10
|
attr_accessor :latitude, :longitude
|
11
|
-
attr_accessor :locality, :region, :country, :country_code
|
11
|
+
attr_accessor :locality, :region, :country, :country_code, :address
|
12
12
|
|
13
13
|
#
|
14
14
|
# this will take a Location object (either generated by Graticule
|
@@ -60,6 +60,7 @@ module Barometer
|
|
60
60
|
@region = location.region
|
61
61
|
@country = location.country
|
62
62
|
@country_code = location.country_code
|
63
|
+
@address = location.address_line
|
63
64
|
end
|
64
65
|
|
65
66
|
def build_from_httparty(location=nil)
|
@@ -87,6 +88,7 @@ module Barometer
|
|
87
88
|
end
|
88
89
|
@country = placemark["AddressDetails"]["Country"]["CountryName"]
|
89
90
|
@country_code = placemark["AddressDetails"]["Country"]["CountryNameCode"]
|
91
|
+
@address = placemark["AddressDetails"]["Country"]["AddressLine"]
|
90
92
|
end
|
91
93
|
end
|
92
94
|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module Graticule
|
2
2
|
class Location
|
3
3
|
|
4
|
-
attr_accessor :country_code
|
4
|
+
attr_accessor :country_code, :address_line
|
5
5
|
|
6
6
|
def attributes
|
7
|
-
[:latitude, :longitude, :street, :locality, :region, :postal_code, :country, :precision, :cuntry_code].inject({}) do |result,attr|
|
7
|
+
[:latitude, :longitude, :street, :locality, :region, :postal_code, :country, :precision, :cuntry_code, :address_line].inject({}) do |result,attr|
|
8
8
|
result[attr] = self.send(attr) unless self.send(attr).blank?
|
9
9
|
result
|
10
10
|
end
|
@@ -39,6 +39,7 @@ module Graticule
|
|
39
39
|
l.postal_code = value(address.elements['.//PostalCodeNumber/text()'])
|
40
40
|
l.country = value(address.elements['.//CountryName/text()'])
|
41
41
|
l.country_code = value(address.elements['.//CountryNameCode/text()'])
|
42
|
+
l.address_line = value(address.elements['.//AddressLine/text()'])
|
42
43
|
l.precision = PRECISION[address.attribute('Accuracy').value.to_i] || :unknown
|
43
44
|
end
|
44
45
|
end
|
data/lib/barometer/query.rb
CHANGED
@@ -31,20 +31,23 @@ module Barometer
|
|
31
31
|
self.analyze!
|
32
32
|
end
|
33
33
|
|
34
|
-
# analyze the saved query to determine the format.
|
35
|
-
# :zipcode and :postalcode the country_code can also be set
|
34
|
+
# analyze the saved query to determine the format.
|
36
35
|
def analyze!
|
37
36
|
return unless @q
|
38
37
|
if Barometer::Query.is_us_zipcode?(@q)
|
39
38
|
@format = :zipcode
|
39
|
+
@country_code = Barometer::Query.format_to_country_code(@format)
|
40
40
|
elsif Barometer::Query.is_canadian_postcode?(@q)
|
41
41
|
@format = :postalcode
|
42
|
+
@country_code = Barometer::Query.format_to_country_code(@format)
|
42
43
|
elsif Barometer::Query.is_coordinates?(@q)
|
43
44
|
@format = :coordinates
|
45
|
+
elsif Barometer::Query.is_icao?(@q)
|
46
|
+
@format = :icao
|
47
|
+
# @country_code = Barometer::Query.icao_to_country_code(@q)
|
44
48
|
else
|
45
49
|
@format = :geocode
|
46
50
|
end
|
47
|
-
@country_code = Barometer::Query.format_to_country_code(@format)
|
48
51
|
end
|
49
52
|
|
50
53
|
# take a list of acceptable (and ordered by preference) formats and convert
|
@@ -93,6 +96,7 @@ module Barometer
|
|
93
96
|
def postalcode?; @format == :postalcode; end
|
94
97
|
def coordinates?; @format == :coordinates; end
|
95
98
|
def geocode?; @format == :geocode; end
|
99
|
+
def icao?; @format == :icao; end
|
96
100
|
|
97
101
|
def self.is_us_zipcode?(query)
|
98
102
|
us_zipcode_regex = /(^[0-9]{5}$)|(^[0-9]{5}-[0-9]{4}$)/
|
@@ -112,12 +116,25 @@ module Barometer
|
|
112
116
|
return !(query =~ coordinates_regex).nil?
|
113
117
|
end
|
114
118
|
|
119
|
+
def self.is_icao?(query)
|
120
|
+
# allow any 3 or 4 letter word ... unfortunately this means some locations
|
121
|
+
# (ie Utah, Goa, Kiev, etc) will be detected as ICAO. This won't matter for
|
122
|
+
# returning weather results ... it will just effect what happens to the query.
|
123
|
+
# For example, wunderground will accept :icao above :coordinates and :geocode,
|
124
|
+
# which means that a city like Kiev would normally get converted to :coordinates
|
125
|
+
# but in this case it will be detected as :icao so it will be passed as is.
|
126
|
+
# Currently, only wunderground accepts ICAO, and they process ICAO the same as a
|
127
|
+
# city name, so it doesn't matter.
|
128
|
+
icao_regex = /^[A-Za-z]{3,4}$/
|
129
|
+
return !(query =~ icao_regex).nil?
|
130
|
+
end
|
131
|
+
|
115
132
|
#
|
116
133
|
# CONVERTERS
|
117
134
|
#
|
118
135
|
|
119
136
|
# this will take all query formats and convert them to coordinates
|
120
|
-
# accepts- :zipcode, :postalcode, :geocode
|
137
|
+
# accepts- :zipcode, :postalcode, :geocode, :icao
|
121
138
|
# returns- :coordinates
|
122
139
|
# if the conversion fails, return nil
|
123
140
|
def self.to_coordinates(query, format)
|
@@ -129,7 +146,7 @@ module Barometer
|
|
129
146
|
end
|
130
147
|
|
131
148
|
# this will take all query formats and convert them to coorinates
|
132
|
-
# accepts- :zipcode, :postalcode, :coordinates
|
149
|
+
# accepts- :zipcode, :postalcode, :coordinates, :icao
|
133
150
|
# returns- :geocode
|
134
151
|
def self.to_geocode(query, format)
|
135
152
|
perform_geocode = false
|
@@ -143,8 +160,17 @@ module Barometer
|
|
143
160
|
if perform_geocode
|
144
161
|
geo = self.geocode(query, country_code)
|
145
162
|
country_code ||= geo.country_code if geo
|
146
|
-
|
147
|
-
|
163
|
+
# different formats have different acceptance criteria
|
164
|
+
q = nil
|
165
|
+
case format
|
166
|
+
when :icao
|
167
|
+
return nil unless geo && geo.address && geo.country
|
168
|
+
q = "#{geo.address}, #{geo.country}"
|
169
|
+
else
|
170
|
+
return nil unless geo && geo.locality && geo.region && geo.country
|
171
|
+
q = "#{geo.locality}, #{geo.region}, #{geo.country}"
|
172
|
+
end
|
173
|
+
return [q, country_code, geo]
|
148
174
|
else
|
149
175
|
# without geocoding, the best we can do is just make use the given query as
|
150
176
|
# the query for the "geocode" format
|
@@ -207,7 +233,6 @@ module Barometer
|
|
207
233
|
},
|
208
234
|
:format => :xml
|
209
235
|
)['kml']['Response']
|
210
|
-
#puts location.inspect
|
211
236
|
geo = Barometer::Geo.new(location)
|
212
237
|
end
|
213
238
|
|
@@ -223,6 +248,30 @@ module Barometer
|
|
223
248
|
end
|
224
249
|
country_code
|
225
250
|
end
|
251
|
+
|
252
|
+
# todo, the fist letter in a 4-letter icao can designate country:
|
253
|
+
# c=canada
|
254
|
+
# k=usa
|
255
|
+
# etc...
|
256
|
+
# def self.icao_to_country_code(icao_code)
|
257
|
+
# return unless icao_code.is_a?(String)
|
258
|
+
# country_code = nil
|
259
|
+
# if icao_code.size == 4
|
260
|
+
# case icao_code.first_letter
|
261
|
+
# when "C"
|
262
|
+
# country_code = "CA"
|
263
|
+
# when "K"
|
264
|
+
# country_code = "US"
|
265
|
+
# end
|
266
|
+
# if coutry_code.nil?
|
267
|
+
# case icao_code.first_two_letters
|
268
|
+
# when "ET"
|
269
|
+
# country_code = "GERMANY"
|
270
|
+
# end
|
271
|
+
# end
|
272
|
+
# end
|
273
|
+
# country_code
|
274
|
+
# end
|
226
275
|
|
227
276
|
end
|
228
277
|
end
|
@@ -39,7 +39,7 @@ module Barometer
|
|
39
39
|
class Wunderground < Service
|
40
40
|
|
41
41
|
def self.accepted_formats
|
42
|
-
[:zipcode, :postalcode, :coordinates, :geocode]
|
42
|
+
[:zipcode, :postalcode, :icao, :coordinates, :geocode]
|
43
43
|
end
|
44
44
|
|
45
45
|
def self.source_name
|
@@ -48,8 +48,9 @@ module Barometer
|
|
48
48
|
|
49
49
|
# these are the icon codes that indicate "wet", used by wet? function
|
50
50
|
def self.wet_icon_codes
|
51
|
-
%w(flurries rain sleet snow tstorms nt_flurries nt_rain nt_sleet nt_snow nt_tstorms)
|
51
|
+
%w(flurries rain sleet snow tstorms nt_flurries nt_rain nt_sleet nt_snow nt_tstorms chancerain)
|
52
52
|
end
|
53
|
+
# these are the icon codes that indicate "sun", used by sunny? function
|
53
54
|
def self.sunny_icon_codes
|
54
55
|
%w(clear mostlysunny partlysunny sunny partlycloudy)
|
55
56
|
end
|
data/lib/barometer/weather.rb
CHANGED
@@ -34,17 +34,9 @@ module Barometer
|
|
34
34
|
# Quick access methods
|
35
35
|
#
|
36
36
|
|
37
|
-
def current
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
def forecast
|
42
|
-
(default = self.default) ? default.forecast : nil
|
43
|
-
end
|
44
|
-
|
45
|
-
def now
|
46
|
-
self.current
|
47
|
-
end
|
37
|
+
def current; (default = self.default) ? default.current : nil; end
|
38
|
+
def forecast; (default = self.default) ? default.forecast : nil; end
|
39
|
+
def now; self.current; end
|
48
40
|
|
49
41
|
def today
|
50
42
|
default = self.default
|
@@ -72,37 +64,48 @@ module Barometer
|
|
72
64
|
# averages
|
73
65
|
#
|
74
66
|
|
75
|
-
#
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
#
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
67
|
+
# TODO: not tested (except via averages)
|
68
|
+
def metric?
|
69
|
+
self.default ? self.default.metric? : true
|
70
|
+
end
|
71
|
+
|
72
|
+
# TODO: not tested (except via averages)
|
73
|
+
# this assumes calculating for current, and that "to_f" for a value
|
74
|
+
# will return the value needed
|
75
|
+
# value_name = the name of the value we are averaging
|
76
|
+
def current_average(value_name)
|
77
|
+
values = []
|
78
|
+
@measurements.each do |measurement|
|
79
|
+
values << measurement.current.send(value_name).to_f if measurement.success?
|
80
|
+
end
|
81
|
+
return nil unless values && values.size > 0
|
82
|
+
values.inject(0.0) { |sum,v| sum += v } / values.size
|
83
|
+
end
|
84
|
+
|
85
|
+
# TODO: not tested (except via averages)
|
86
|
+
def average(value_name, do_average=true, class_name=nil)
|
87
|
+
if class_name
|
88
|
+
if do_average
|
89
|
+
avg = Barometer.const_get(class_name).new(self.metric?)
|
90
|
+
avg << self.current_average(value_name)
|
91
|
+
else
|
92
|
+
avg = self.now.send(value_name)
|
93
|
+
end
|
94
|
+
else
|
95
|
+
avg = (do_average ? self.current_average(value_name) : self.now.send(value_name))
|
96
|
+
end
|
97
|
+
avg
|
98
|
+
end
|
99
|
+
|
100
|
+
# average of all values
|
101
|
+
def humidity(do_average=true); average("humidity",do_average); end
|
102
|
+
def temperature(do_average=true); average("temperature",do_average,"Temperature"); end
|
103
|
+
def wind(do_average=true); average("wind",do_average,"Speed"); end
|
104
|
+
def pressure(do_average=true); average("pressure",do_average,"Pressure"); end
|
105
|
+
def dew_point(do_average=true); average("dew_point",do_average,"Temperature"); end
|
106
|
+
def heat_index(do_average=true); average("heat_index",do_average,"Temperature"); end
|
107
|
+
def wind_chill(do_average=true); average("wind_chill",do_average,"Temperature"); end
|
108
|
+
def visibility(do_average=true); average("visibility",do_average,"Distance"); end
|
106
109
|
|
107
110
|
#
|
108
111
|
# quick access methods
|
data/spec/barometer_spec.rb
CHANGED
data/spec/data_geo_spec.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" ?> <kml xmlns="http://earth.google.com/kml/2.0"><Response> <name>KSFO</name> <Status> <code>200</code> <request>geocode</request> </Status> <Placemark id="p1"> <address>San Francisco Airport, United States</address> <AddressDetails Accuracy="9" xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"><Country><CountryNameCode>US</CountryNameCode><CountryName>USA</CountryName><AddressLine>San Francisco Airport</AddressLine></Country></AddressDetails> <ExtendedData> <LatLonBox north="37.6401700" south="37.6041790" east="-122.3548940" west="-122.4015610" /> </ExtendedData> <Point><coordinates>-122.3899790,37.6152230,0</coordinates></Point> </Placemark> </Response></kml>
|
data/spec/query_spec.rb
CHANGED
@@ -7,6 +7,7 @@ describe "Query" do
|
|
7
7
|
@postal_code = "T5B 4M9"
|
8
8
|
@coordinates = "40.756054,-73.986951"
|
9
9
|
@geocode = "New York, NY"
|
10
|
+
@icao = "KSFO"
|
10
11
|
|
11
12
|
# actual conversions
|
12
13
|
@zipcode_to_coordinates = "34.1030032,-118.4104684"
|
@@ -26,18 +27,28 @@ describe "Query" do
|
|
26
27
|
Barometer::Query.is_us_zipcode?(@zipcode).should be_true
|
27
28
|
Barometer::Query.is_us_zipcode?(@postal_code).should be_false
|
28
29
|
Barometer::Query.is_us_zipcode?(@coordinates).should be_false
|
30
|
+
Barometer::Query.is_coordinates?(@icao).should be_false
|
29
31
|
end
|
30
32
|
|
31
33
|
it "detects a postalcode" do
|
32
34
|
Barometer::Query.is_canadian_postcode?(@postal_code).should be_true
|
33
35
|
Barometer::Query.is_canadian_postcode?(@zipcode).should be_false
|
34
36
|
Barometer::Query.is_canadian_postcode?(@coordinates).should be_false
|
37
|
+
Barometer::Query.is_coordinates?(@icao).should be_false
|
35
38
|
end
|
36
39
|
|
37
40
|
it "detects a coordinates" do
|
38
41
|
Barometer::Query.is_coordinates?(@coordinates).should be_true
|
39
42
|
Barometer::Query.is_coordinates?(@zipcode).should be_false
|
40
43
|
Barometer::Query.is_coordinates?(@postal_code).should be_false
|
44
|
+
Barometer::Query.is_coordinates?(@icao).should be_false
|
45
|
+
end
|
46
|
+
|
47
|
+
it "detects an ICAO" do
|
48
|
+
Barometer::Query.is_icao?(@coordinates).should be_false
|
49
|
+
Barometer::Query.is_icao?(@zipcode).should be_false
|
50
|
+
Barometer::Query.is_icao?(@postal_code).should be_false
|
51
|
+
Barometer::Query.is_icao?(@icao).should be_true
|
41
52
|
end
|
42
53
|
|
43
54
|
end
|
@@ -58,6 +69,7 @@ describe "Query" do
|
|
58
69
|
@query.country_code.should == "US"
|
59
70
|
@query.zipcode?.should be_true
|
60
71
|
@query.postalcode?.should be_false
|
72
|
+
@query.icao?.should be_false
|
61
73
|
@query.coordinates?.should be_false
|
62
74
|
@query.geocode?.should be_false
|
63
75
|
end
|
@@ -71,6 +83,21 @@ describe "Query" do
|
|
71
83
|
@query.country_code.should == "CA"
|
72
84
|
@query.zipcode?.should be_false
|
73
85
|
@query.postalcode?.should be_true
|
86
|
+
@query.icao?.should be_false
|
87
|
+
@query.coordinates?.should be_false
|
88
|
+
@query.geocode?.should be_false
|
89
|
+
end
|
90
|
+
|
91
|
+
it "recognizes icao" do
|
92
|
+
@query.q = @icao
|
93
|
+
@query.format.should be_nil
|
94
|
+
@query.analyze!
|
95
|
+
@query.format.to_sym.should == :icao
|
96
|
+
|
97
|
+
@query.country_code.should be_nil
|
98
|
+
@query.zipcode?.should be_false
|
99
|
+
@query.postalcode?.should be_false
|
100
|
+
@query.icao?.should be_true
|
74
101
|
@query.coordinates?.should be_false
|
75
102
|
@query.geocode?.should be_false
|
76
103
|
end
|
@@ -84,6 +111,7 @@ describe "Query" do
|
|
84
111
|
@query.country_code.should be_nil
|
85
112
|
@query.zipcode?.should be_false
|
86
113
|
@query.postalcode?.should be_false
|
114
|
+
@query.icao?.should be_false
|
87
115
|
@query.coordinates?.should be_true
|
88
116
|
@query.geocode?.should be_false
|
89
117
|
end
|
@@ -97,6 +125,7 @@ describe "Query" do
|
|
97
125
|
@query.country_code.should be_nil
|
98
126
|
@query.zipcode?.should be_false
|
99
127
|
@query.postalcode?.should be_false
|
128
|
+
@query.icao?.should be_false
|
100
129
|
@query.coordinates?.should be_false
|
101
130
|
@query.geocode?.should be_true
|
102
131
|
end
|
@@ -168,7 +197,7 @@ describe "Query" do
|
|
168
197
|
describe "when converting queries" do
|
169
198
|
|
170
199
|
before(:each) do
|
171
|
-
@key =
|
200
|
+
@key = KEY
|
172
201
|
url_start = "http://maps.google.com/maps/geo?"
|
173
202
|
#
|
174
203
|
# for Graticule and/or HTTParty geocoding
|
@@ -221,6 +250,13 @@ describe "Query" do
|
|
221
250
|
'geocode_40_73.xml')
|
222
251
|
)
|
223
252
|
)
|
253
|
+
FakeWeb.register_uri(:get,
|
254
|
+
"#{url_start}output=xml&q=KSFO&gl=&key=#{@key}",
|
255
|
+
:string => File.read(File.join(File.dirname(__FILE__),
|
256
|
+
'fixtures',
|
257
|
+
'geocode_ksfo.xml')
|
258
|
+
)
|
259
|
+
)
|
224
260
|
end
|
225
261
|
|
226
262
|
describe "to coordinates," do
|
@@ -253,6 +289,10 @@ describe "Query" do
|
|
253
289
|
Barometer::Query.to_coordinates(@postal_code, :postalcode).first.should == "53.570447,-113.456083"
|
254
290
|
end
|
255
291
|
|
292
|
+
it "converts from icao" do
|
293
|
+
Barometer::Query.to_coordinates(@icao, :icao).first.should == "37.615223,-122.389979"
|
294
|
+
end
|
295
|
+
|
256
296
|
end
|
257
297
|
|
258
298
|
describe "to geocode" do
|
@@ -275,6 +315,10 @@ describe "Query" do
|
|
275
315
|
Barometer::Query.to_geocode(@postal_code, :postalcode).first.should == @postal_code
|
276
316
|
end
|
277
317
|
|
318
|
+
it "converts from icao" do
|
319
|
+
Barometer::Query.to_geocode(@icao, :icao).first.should == "San Francisco Airport, USA"
|
320
|
+
end
|
321
|
+
|
278
322
|
end
|
279
323
|
|
280
324
|
describe "when Graticule disabled," do
|
@@ -357,7 +401,7 @@ describe "Query" do
|
|
357
401
|
|
358
402
|
before(:each) do
|
359
403
|
@query = Barometer::Query.new(@zipcode)
|
360
|
-
Barometer::Query.google_geocode_key =
|
404
|
+
Barometer::Query.google_geocode_key = KEY
|
361
405
|
end
|
362
406
|
|
363
407
|
it "converts to coordinates" do
|
@@ -384,7 +428,7 @@ describe "Query" do
|
|
384
428
|
|
385
429
|
before(:each) do
|
386
430
|
@query = Barometer::Query.new(@postal_code)
|
387
|
-
Barometer::Query.google_geocode_key =
|
431
|
+
Barometer::Query.google_geocode_key = KEY
|
388
432
|
end
|
389
433
|
|
390
434
|
it "converts to coordinates" do
|
@@ -411,7 +455,7 @@ describe "Query" do
|
|
411
455
|
|
412
456
|
before(:each) do
|
413
457
|
@query = Barometer::Query.new(@geocode)
|
414
|
-
Barometer::Query.google_geocode_key =
|
458
|
+
Barometer::Query.google_geocode_key = KEY
|
415
459
|
end
|
416
460
|
|
417
461
|
it "converts to coordinates" do
|
@@ -438,7 +482,7 @@ describe "Query" do
|
|
438
482
|
|
439
483
|
before(:each) do
|
440
484
|
@query = Barometer::Query.new(@coordinates)
|
441
|
-
Barometer::Query.google_geocode_key =
|
485
|
+
Barometer::Query.google_geocode_key = KEY
|
442
486
|
end
|
443
487
|
|
444
488
|
it "converts to geocode" do
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe "Wunderground" do
|
4
4
|
|
5
5
|
before(:each) do
|
6
|
-
@accepted_formats = [:zipcode, :postalcode, :coordinates, :geocode]
|
6
|
+
@accepted_formats = [:zipcode, :postalcode, :icao, :coordinates, :geocode]
|
7
7
|
@base_uri = "http://api.wunderground.com/auto/wui/geo"
|
8
8
|
end
|
9
9
|
|
data/spec/spec_helper.rb
CHANGED
@@ -2,6 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'spec'
|
3
3
|
require 'fakeweb'
|
4
4
|
require 'cgi'
|
5
|
+
require 'yaml'
|
5
6
|
|
6
7
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
7
8
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
@@ -9,6 +10,30 @@ require 'barometer'
|
|
9
10
|
|
10
11
|
FakeWeb.allow_net_connect = false
|
11
12
|
|
13
|
+
KEY_FILE = File.expand_path(File.join('~', '.barometer'))
|
14
|
+
|
15
|
+
def geocode_google_key_message
|
16
|
+
puts
|
17
|
+
puts "Please update the key_file '#{KEY_FILE}' with your google api key"
|
18
|
+
puts "example:"
|
19
|
+
puts "geocode_google: YOUR_KEY_KERE"
|
20
|
+
puts
|
21
|
+
end
|
22
|
+
|
23
|
+
if File.exists?(KEY_FILE)
|
24
|
+
keys = YAML.load_file(KEY_FILE)
|
25
|
+
if keys["geocode_google"]
|
26
|
+
KEY = keys["geocode_google"]
|
27
|
+
else
|
28
|
+
geocode_google_key_message
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
else
|
32
|
+
File.open(KEY_FILE, 'w') {|f| f << "geocode_google: YOUR_KEY_KERE" }
|
33
|
+
geocode_google_key_message
|
34
|
+
exit
|
35
|
+
end
|
36
|
+
|
12
37
|
Spec::Runner.configure do |config|
|
13
38
|
|
14
39
|
end
|
data/spec/weather_spec.rb
CHANGED
@@ -78,6 +78,174 @@ describe "Weather" do
|
|
78
78
|
|
79
79
|
end
|
80
80
|
|
81
|
+
describe "when calculating averages" do
|
82
|
+
|
83
|
+
before(:each) do
|
84
|
+
@weather = Barometer::Weather.new
|
85
|
+
@wunderground = Barometer::Measurement.new(:wunderground)
|
86
|
+
@wunderground.current = Barometer::CurrentMeasurement.new
|
87
|
+
@wunderground.success = true
|
88
|
+
@yahoo = Barometer::Measurement.new(:yahoo)
|
89
|
+
@yahoo.current = Barometer::CurrentMeasurement.new
|
90
|
+
@yahoo.success = true
|
91
|
+
@google = Barometer::Measurement.new(:google)
|
92
|
+
@weather.measurements << @wunderground
|
93
|
+
@weather.measurements << @yahoo
|
94
|
+
@weather.measurements << @google
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "for temperature" do
|
98
|
+
|
99
|
+
before(:each) do
|
100
|
+
@weather.source(:wunderground).current.temperature = Barometer::Temperature.new
|
101
|
+
@weather.source(:wunderground).current.temperature.c = 10
|
102
|
+
@weather.source(:yahoo).current.temperature = Barometer::Temperature.new
|
103
|
+
@weather.source(:yahoo).current.temperature.c = 6
|
104
|
+
end
|
105
|
+
|
106
|
+
it "returns averages" do
|
107
|
+
@weather.temperature.c.should == 8
|
108
|
+
end
|
109
|
+
|
110
|
+
it "returns default when disabled" do
|
111
|
+
@weather.temperature(false).c.should == 10
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "for wind" do
|
117
|
+
|
118
|
+
before(:each) do
|
119
|
+
@weather.source(:wunderground).current.wind = Barometer::Speed.new
|
120
|
+
@weather.source(:wunderground).current.wind.kph = 10
|
121
|
+
@weather.source(:yahoo).current.wind = Barometer::Speed.new
|
122
|
+
@weather.source(:yahoo).current.wind.kph = 6
|
123
|
+
end
|
124
|
+
|
125
|
+
it "returns averages" do
|
126
|
+
@weather.wind.kph.should == 8
|
127
|
+
end
|
128
|
+
|
129
|
+
it "returns default when disabled" do
|
130
|
+
@weather.wind(false).kph.should == 10
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "for humidity" do
|
136
|
+
|
137
|
+
before(:each) do
|
138
|
+
@weather.source(:wunderground).current.humidity = 10
|
139
|
+
@weather.source(:yahoo).current.humidity = 6
|
140
|
+
end
|
141
|
+
|
142
|
+
it "returns averages" do
|
143
|
+
@weather.humidity.should == 8
|
144
|
+
end
|
145
|
+
|
146
|
+
it "returns default when disabled" do
|
147
|
+
@weather.humidity(false).should == 10
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "for pressure" do
|
153
|
+
|
154
|
+
before(:each) do
|
155
|
+
@weather.source(:wunderground).current.pressure = Barometer::Pressure.new
|
156
|
+
@weather.source(:wunderground).current.pressure.mb = 10
|
157
|
+
@weather.source(:yahoo).current.pressure = Barometer::Pressure.new
|
158
|
+
@weather.source(:yahoo).current.pressure.mb = 6
|
159
|
+
end
|
160
|
+
|
161
|
+
it "returns averages" do
|
162
|
+
@weather.pressure.mb.should == 8
|
163
|
+
end
|
164
|
+
|
165
|
+
it "returns default when disabled" do
|
166
|
+
@weather.pressure(false).mb.should == 10
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "for dew_point" do
|
172
|
+
|
173
|
+
before(:each) do
|
174
|
+
@weather.source(:wunderground).current.dew_point = Barometer::Temperature.new
|
175
|
+
@weather.source(:wunderground).current.dew_point.c = 10
|
176
|
+
@weather.source(:yahoo).current.dew_point = Barometer::Temperature.new
|
177
|
+
@weather.source(:yahoo).current.dew_point.c = 6
|
178
|
+
end
|
179
|
+
|
180
|
+
it "returns averages" do
|
181
|
+
@weather.dew_point.c.should == 8
|
182
|
+
end
|
183
|
+
|
184
|
+
it "returns default when disabled" do
|
185
|
+
@weather.dew_point(false).c.should == 10
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "for heat_index" do
|
191
|
+
|
192
|
+
before(:each) do
|
193
|
+
@weather.source(:wunderground).current.heat_index = Barometer::Temperature.new
|
194
|
+
@weather.source(:wunderground).current.heat_index.c = 10
|
195
|
+
@weather.source(:yahoo).current.heat_index = Barometer::Temperature.new
|
196
|
+
@weather.source(:yahoo).current.heat_index.c = 6
|
197
|
+
end
|
198
|
+
|
199
|
+
it "returns averages" do
|
200
|
+
@weather.heat_index.c.should == 8
|
201
|
+
end
|
202
|
+
|
203
|
+
it "returns default when disabled" do
|
204
|
+
@weather.heat_index(false).c.should == 10
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
describe "for wind_chill" do
|
210
|
+
|
211
|
+
before(:each) do
|
212
|
+
@weather.source(:wunderground).current.wind_chill = Barometer::Temperature.new
|
213
|
+
@weather.source(:wunderground).current.wind_chill.c = 10
|
214
|
+
@weather.source(:yahoo).current.wind_chill = Barometer::Temperature.new
|
215
|
+
@weather.source(:yahoo).current.wind_chill.c = 6
|
216
|
+
end
|
217
|
+
|
218
|
+
it "returns averages" do
|
219
|
+
@weather.wind_chill.c.should == 8
|
220
|
+
end
|
221
|
+
|
222
|
+
it "returns default when disabled" do
|
223
|
+
@weather.wind_chill(false).c.should == 10
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
describe "for visibility" do
|
229
|
+
|
230
|
+
before(:each) do
|
231
|
+
@weather.source(:wunderground).current.visibility = Barometer::Distance.new
|
232
|
+
@weather.source(:wunderground).current.visibility.km = 10
|
233
|
+
@weather.source(:yahoo).current.visibility = Barometer::Distance.new
|
234
|
+
@weather.source(:yahoo).current.visibility.km = 6
|
235
|
+
end
|
236
|
+
|
237
|
+
it "returns averages" do
|
238
|
+
@weather.visibility.km.should == 8
|
239
|
+
end
|
240
|
+
|
241
|
+
it "returns default when disabled" do
|
242
|
+
@weather.visibility(false).km.should == 10
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
|
81
249
|
describe "when answering the simple questions," do
|
82
250
|
|
83
251
|
before(:each) do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attack-barometer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark G
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-05-02 00:00:00 -07:00
|
13
13
|
default_executable: barometer
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -75,6 +75,7 @@ files:
|
|
75
75
|
- spec/fixtures/geocode_40_73.xml
|
76
76
|
- spec/fixtures/geocode_90210.xml
|
77
77
|
- spec/fixtures/geocode_calgary_ab.xml
|
78
|
+
- spec/fixtures/geocode_ksfo.xml
|
78
79
|
- spec/fixtures/geocode_newyork_ny.xml
|
79
80
|
- spec/fixtures/geocode_T5B4M9.xml
|
80
81
|
- spec/fixtures/google_calgary_ab.xml
|