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 CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Mark
1
+ Copyright (c) 2009 Mark G
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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 before first release:
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 Timoout support.
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
@@ -1,4 +1,4 @@
1
1
  ---
2
+ :patch: 3
2
3
  :major: 0
3
- :minor: 1
4
- :patch: 0
4
+ :minor: 2
data/bin/barometer CHANGED
@@ -1,63 +1,417 @@
1
1
  #!/usr/bin/env ruby
2
-
3
- require File.dirname(__FILE__) + '/../lib/barometer'
4
- #require 'rubygems'
5
- #require 'attack-barometer'
6
-
7
- # TODO
8
-
9
- # set default Google Key ... maybe need a config file?
10
- Barometer.google_geocode_key = "ABQIAAAAq8TH4offRcGrok8JVY_MyxRi_j0U6kJrkFvY4-OX2XYmEAa76BSFwMlSow1YgX8BOPUeve_shMG7xw"
11
-
12
- # take command line paramters
13
- # service: --yahoo, --wunderground, --google
14
- # units: -m --metric, -i --imperial
15
- # geocode: -g --geocode (force geocode)
16
- # timeout: -t 15 --timeout 15
17
- # skip: --skip (skip graticule)
18
- # help: -h --help
19
- # wet threshold: -w --wet
20
- # windy threshold: -v --wind
21
- # pop threshold: -p --pop
22
- # time: -a --at
23
-
24
- # prettier output
25
- # show simple answers
26
- # more help
27
- # error display (out of sources, etc.)
28
-
29
- if ARGV.size == 0
30
- puts 'Barometer [Powered by wunderground]'
31
- puts 'USAGE: barometer [query]'
32
- puts 'EXAMPLES:'
33
- puts ' barometer paris'
34
- exit
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
- barometer = Barometer.new(ARGV[0])
38
- weather = barometer.measure(true)
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
- if weather
45
- puts "###################################################"
46
- puts "# #{weather.default.location.name}"
47
- puts "#"
48
- puts "# (lat: #{weather.default.location.latitude}, long: #{weather.default.location.longitude})"
49
- puts "###################################################"
50
- puts " -- CURRENT --"
51
- puts " temperature: #{weather.now.temperature}"
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
- puts " -- QUESTIONS --"
54
- puts " day? : #{y(weather.day?)}"
55
- puts " sunny?: #{y(weather.sunny?)}"
56
- puts " windy?: #{y(weather.windy?)}"
57
- puts " wet? : #{y(weather.wet?)}"
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
- puts " -- INFO --"
61
- puts " http://github.com/attack/barometer"
62
- puts "---------------------------------------------------"
63
- end
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
@@ -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
@@ -31,20 +31,23 @@ module Barometer
31
31
  self.analyze!
32
32
  end
33
33
 
34
- # analyze the saved query to determine the format. for the format of
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
- return nil unless geo && geo.locality && geo.region && geo.country
147
- return ["#{geo.locality}, #{geo.region}, #{geo.country}", country_code, geo]
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
@@ -34,17 +34,9 @@ module Barometer
34
34
  # Quick access methods
35
35
  #
36
36
 
37
- def current
38
- (default = self.default) ? default.current : nil
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
- # average of all humidity values
76
- # def humidity
77
- # end
78
- #
79
- # # average of all temperature values
80
- # def temperature
81
- # end
82
- #
83
- # # average of all wind speed values
84
- # def wind
85
- # end
86
- #
87
- # # average of all pressure values
88
- # def pressure
89
- # end
90
- #
91
- # # average of all dew_point values
92
- # def dew_point
93
- # end
94
- #
95
- # # average of all heat_index values
96
- # def heat_index
97
- # end
98
- #
99
- # # average of all wind_chill values
100
- # def wind_chill
101
- # end
102
- #
103
- # # average of all visibility values
104
- # def visibility
105
- # end
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
@@ -4,7 +4,7 @@ describe "Barometer" do
4
4
 
5
5
  before(:each) do
6
6
  @preference_hash = { 1 => [:wunderground] }
7
- @key = "ABQIAAAAq8TH4offRcGrok8JVY_MyxRi_j0U6kJrkFvY4-OX2XYmEAa76BSFwMlSow1YgX8BOPUeve_shMG7xw"
7
+ @key = KEY
8
8
  end
9
9
 
10
10
  describe "and class methods" do
@@ -35,6 +35,10 @@ describe "Geo" do
35
35
  @geo.country.should be_nil
36
36
  end
37
37
 
38
+ it "responds to address" do
39
+ @geo.address.should be_nil
40
+ end
41
+
38
42
  it "responds to coordinates" do
39
43
  @geo.longitude = "99.99"
40
44
  @geo.latitude = "88.88"
@@ -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 = "ABQIAAAAq8TH4offRcGrok8JVY_MyxRi_j0U6kJrkFvY4-OX2XYmEAa76BSFwMlSow1YgX8BOPUeve_shMG7xw"
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 = "ABQIAAAAq8TH4offRcGrok8JVY_MyxRi_j0U6kJrkFvY4-OX2XYmEAa76BSFwMlSow1YgX8BOPUeve_shMG7xw"
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 = "ABQIAAAAq8TH4offRcGrok8JVY_MyxRi_j0U6kJrkFvY4-OX2XYmEAa76BSFwMlSow1YgX8BOPUeve_shMG7xw"
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 = "ABQIAAAAq8TH4offRcGrok8JVY_MyxRi_j0U6kJrkFvY4-OX2XYmEAa76BSFwMlSow1YgX8BOPUeve_shMG7xw"
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 = "ABQIAAAAq8TH4offRcGrok8JVY_MyxRi_j0U6kJrkFvY4-OX2XYmEAa76BSFwMlSow1YgX8BOPUeve_shMG7xw"
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.1.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-04-30 00:00:00 -07:00
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