barometer 0.1.0
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 +20 -0
- data/README.rdoc +266 -0
- data/VERSION.yml +4 -0
- data/bin/barometer +63 -0
- data/lib/barometer.rb +52 -0
- data/lib/barometer/base.rb +52 -0
- data/lib/barometer/data.rb +15 -0
- data/lib/barometer/data/current.rb +93 -0
- data/lib/barometer/data/distance.rb +131 -0
- data/lib/barometer/data/forecast.rb +66 -0
- data/lib/barometer/data/geo.rb +98 -0
- data/lib/barometer/data/location.rb +20 -0
- data/lib/barometer/data/measurement.rb +161 -0
- data/lib/barometer/data/pressure.rb +133 -0
- data/lib/barometer/data/speed.rb +147 -0
- data/lib/barometer/data/sun.rb +35 -0
- data/lib/barometer/data/temperature.rb +164 -0
- data/lib/barometer/data/units.rb +55 -0
- data/lib/barometer/data/zone.rb +124 -0
- data/lib/barometer/extensions/graticule.rb +50 -0
- data/lib/barometer/extensions/httparty.rb +21 -0
- data/lib/barometer/query.rb +228 -0
- data/lib/barometer/services.rb +6 -0
- data/lib/barometer/services/google.rb +146 -0
- data/lib/barometer/services/noaa.rb +6 -0
- data/lib/barometer/services/service.rb +324 -0
- data/lib/barometer/services/weather_bug.rb +6 -0
- data/lib/barometer/services/weather_dot_com.rb +6 -0
- data/lib/barometer/services/wunderground.rb +285 -0
- data/lib/barometer/services/yahoo.rb +274 -0
- data/lib/barometer/weather.rb +187 -0
- data/spec/barometer_spec.rb +162 -0
- data/spec/data_current_spec.rb +225 -0
- data/spec/data_distance_spec.rb +336 -0
- data/spec/data_forecast_spec.rb +150 -0
- data/spec/data_geo_spec.rb +90 -0
- data/spec/data_location_spec.rb +59 -0
- data/spec/data_measurement_spec.rb +411 -0
- data/spec/data_pressure_spec.rb +336 -0
- data/spec/data_speed_spec.rb +374 -0
- data/spec/data_sun_spec.rb +76 -0
- data/spec/data_temperature_spec.rb +396 -0
- data/spec/data_zone_spec.rb +133 -0
- data/spec/fixtures/current_calgary_ab.xml +1 -0
- data/spec/fixtures/forecast_calgary_ab.xml +1 -0
- data/spec/fixtures/geocode_40_73.xml +1 -0
- data/spec/fixtures/geocode_90210.xml +1 -0
- data/spec/fixtures/geocode_T5B4M9.xml +1 -0
- data/spec/fixtures/geocode_calgary_ab.xml +1 -0
- data/spec/fixtures/geocode_newyork_ny.xml +1 -0
- data/spec/fixtures/google_calgary_ab.xml +1 -0
- data/spec/fixtures/yahoo_90210.xml +1 -0
- data/spec/query_spec.rb +469 -0
- data/spec/service_google_spec.rb +144 -0
- data/spec/service_wunderground_spec.rb +330 -0
- data/spec/service_yahoo_spec.rb +299 -0
- data/spec/services_spec.rb +1106 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/units_spec.rb +101 -0
- data/spec/weather_spec.rb +265 -0
- metadata +119 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Mark
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,266 @@
|
|
1
|
+
= barometer
|
2
|
+
|
3
|
+
A multi API consuming weather forecasting superstar.
|
4
|
+
|
5
|
+
Barometer provides a common public API to one or more weather services (APIs)
|
6
|
+
of your choice. Weather services can co-exist to retrieve extensive
|
7
|
+
information, or they can be used in a hierarchical configuration where lower
|
8
|
+
preferred weather services are only used if previous services are
|
9
|
+
unavailable.
|
10
|
+
|
11
|
+
== status
|
12
|
+
|
13
|
+
Currently this project is in development and will only work for a few weather
|
14
|
+
services (wunderground, google, yahoo).
|
15
|
+
|
16
|
+
Features to be added before first release:
|
17
|
+
- gem setup/config, apply to rubyforge
|
18
|
+
|
19
|
+
Features to be added in future releases:
|
20
|
+
- 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
|
+
= dependencies
|
25
|
+
|
26
|
+
=== HTTParty
|
27
|
+
|
28
|
+
Why? HTTParty was created and designed specifically for consuming web services.
|
29
|
+
I choose to use this over using the Net::HTTP library directly to allow for
|
30
|
+
faster development of this project.
|
31
|
+
|
32
|
+
HTTParty is also extended to include configurable Timoout support.
|
33
|
+
|
34
|
+
=== tzinfo
|
35
|
+
|
36
|
+
Why? Barometer deals with time information for locations all over the world.
|
37
|
+
This information doesn't mean that much if it can't be converted to times
|
38
|
+
that don't correspond to the applicable timezone.
|
39
|
+
Tzinfo handles this time zone manipulation.
|
40
|
+
|
41
|
+
=== graticule (very soft dependency)
|
42
|
+
|
43
|
+
Why? Barometer returns the weather for a given location. Most weather service
|
44
|
+
APIs are somewhat restricted on the query format they receive. To bridge
|
45
|
+
this gap and allow for maximum flexibility on the 'barometer' query format,
|
46
|
+
the query will be geo-coded using the Google geocoding service, if required.
|
47
|
+
Graticule can provide this geocoding interface.
|
48
|
+
|
49
|
+
Using Graticule requires a free Google API key for geocoding. It is possible
|
50
|
+
to use barometer without geocoding, though your query format will be
|
51
|
+
limited to that of the weather service API.
|
52
|
+
|
53
|
+
ALTERNATE: If you supply a Google API key but don't install the Graticule gem,
|
54
|
+
HTTParty will be used instead to provide the same geocoding. Basically
|
55
|
+
Graticule is only used if it exists.
|
56
|
+
|
57
|
+
NOTE: you can force Barometer not to use Graticule, even if you have it installed
|
58
|
+
using the following:
|
59
|
+
|
60
|
+
Barometer.skip_graticule = true
|
61
|
+
|
62
|
+
= usage
|
63
|
+
|
64
|
+
You can use barometer right out of the box, as it is configured to use one
|
65
|
+
register-less (no API key required) international weather service
|
66
|
+
(wunderground.com).
|
67
|
+
|
68
|
+
For better results, signup for a google-map key and enhance your barometer
|
69
|
+
with geo-coding.
|
70
|
+
|
71
|
+
require 'barometer'
|
72
|
+
|
73
|
+
Barometer.google_geocode_key = "THE_GOOGLE_API_KEY"
|
74
|
+
barometer = Barometer.new("Paris")
|
75
|
+
weather = barometer.measure
|
76
|
+
|
77
|
+
puts weather.current.temperture
|
78
|
+
|
79
|
+
== multiple weather API, with hierarchy
|
80
|
+
|
81
|
+
require 'barometer'
|
82
|
+
|
83
|
+
Barometer.google_geocode_key = "THE_GOOGLE_API_KEY"
|
84
|
+
# use yahoo and google, if they both fail, use wunderground
|
85
|
+
Barometer.selection = { 1 => [:yahoo, :google], 2 => :wunderground }
|
86
|
+
|
87
|
+
barometer = Barometer.new("Paris")
|
88
|
+
weather = barometer.measure
|
89
|
+
|
90
|
+
puts weather.current.temperture
|
91
|
+
|
92
|
+
== command line
|
93
|
+
|
94
|
+
You can use barometer from the command line.
|
95
|
+
|
96
|
+
# barometer berlin
|
97
|
+
|
98
|
+
This will output the weather information for the given query.
|
99
|
+
|
100
|
+
=== fail
|
101
|
+
|
102
|
+
What would cause a weather service to fail? The most obvious is that the
|
103
|
+
particular weather service in currently unavailable or not reachable.
|
104
|
+
Other possible reasons would include not having the API (or a valid API
|
105
|
+
key for the particular weather service, if required), not providing a
|
106
|
+
valid query, or providing a query for a location not supported by the
|
107
|
+
weather service.
|
108
|
+
|
109
|
+
For example, if you look at the example above, the query of "Paris" refers
|
110
|
+
to a city in France. Yahoo weather services only supports
|
111
|
+
weather results for USA (at least at the time of writing). Therefore,
|
112
|
+
Barometer would not use Yahoo, just Google and failover to use Wunderground
|
113
|
+
(if needed).
|
114
|
+
|
115
|
+
=== bootstrapping
|
116
|
+
|
117
|
+
You can use weather service drivers directly. Below is an example to use
|
118
|
+
Wunderground, but since the driver interface is abstracted it will be the
|
119
|
+
same for all supported services.
|
120
|
+
|
121
|
+
require 'barometer'
|
122
|
+
Barometer.google_geocode_key = "THE_GOOGLE_API_KEY"
|
123
|
+
|
124
|
+
query = Barometer::Query.new("Paris")
|
125
|
+
weather = Barometer::Service.source(:wunderground).measure(query)
|
126
|
+
|
127
|
+
puts weather.current.temperture
|
128
|
+
|
129
|
+
# OR, even more raw
|
130
|
+
|
131
|
+
measurement = Barometer::Measurement.new
|
132
|
+
weather = Barometer::Wunderground.measure_all(measurement, "Paris")
|
133
|
+
|
134
|
+
puts weather.current.temperture
|
135
|
+
|
136
|
+
|
137
|
+
NOTE: The disadvantage to using the drivers directly is that you lose the
|
138
|
+
advantage of redundancy/failover added by the Module as a whole.
|
139
|
+
|
140
|
+
NOTE: You still must create the Barometer::Query object with your query
|
141
|
+
string instead of directly feeding the query string to the service (as in
|
142
|
+
bootstrap example #1). The Barometer::Query object has behavior required
|
143
|
+
by the service that a regular String doesn't have. Using a driver directly
|
144
|
+
WILL accept a String (as in bootstrap example #2).
|
145
|
+
|
146
|
+
== searching
|
147
|
+
|
148
|
+
After you have measured the data, Barometer provides several methods to help
|
149
|
+
you get the data you are after. All examples assume you already have measured
|
150
|
+
the data as shown in the above examples.
|
151
|
+
|
152
|
+
=== by preference (default service)
|
153
|
+
|
154
|
+
weather.default # returns measurement for default source
|
155
|
+
weather.current # returns current_measurement for default
|
156
|
+
weather.now # returns current_measurement for default
|
157
|
+
weather.forecast # returns all forecast_measurements for default
|
158
|
+
weather.today # returns forecast_measurement for default today
|
159
|
+
weather.tomorrow # returns forecast_measurement for default tomorrow
|
160
|
+
|
161
|
+
puts weather.now.temperature.c
|
162
|
+
puts weather.tomorrow.high.c
|
163
|
+
|
164
|
+
=== by source
|
165
|
+
|
166
|
+
weather.source(:wunderground) # returns measurement for specified source
|
167
|
+
weather.sources # lists all successful sources
|
168
|
+
|
169
|
+
puts weather.source(:wunderground).current.temperature.c
|
170
|
+
|
171
|
+
=== by date
|
172
|
+
|
173
|
+
# note, the date is the date of the locations weather, not the date of the
|
174
|
+
# user measuring the weather
|
175
|
+
date = Date.parse("01-01-2009")
|
176
|
+
weather.for(date) # returns forecast_measurement for default on date
|
177
|
+
weather.source(:wunderground).for(date) # same as above but specific source
|
178
|
+
|
179
|
+
puts weather.source(:wunderground).for(date).high.c
|
180
|
+
|
181
|
+
=== by time
|
182
|
+
|
183
|
+
# note, the time is the time of the locations weather, not the time of the
|
184
|
+
# user measuring the weather
|
185
|
+
time = Time.parse("13:00 01-01-2009")
|
186
|
+
weather.for(time) # returns forecast_measurement for default at time
|
187
|
+
weather.source(:wunderground).for(time) # same as above but specific source
|
188
|
+
|
189
|
+
puts weather.source(:wunderground).for(time).low.f
|
190
|
+
|
191
|
+
== simple answers
|
192
|
+
|
193
|
+
After you have measured the data, Barometer provides several "simple answer"
|
194
|
+
methods to help you get answers to some basic questions. All examples assume
|
195
|
+
you already have measured the data as shown in the above examples.
|
196
|
+
|
197
|
+
All of these questions are ultimately specific to the weather source(s) you
|
198
|
+
are configured to use. All sources that have successfully measured data
|
199
|
+
will be asked, but if there is no data that can answer the question then
|
200
|
+
there will be no answer.
|
201
|
+
|
202
|
+
=== is it windy?
|
203
|
+
|
204
|
+
# 1st parameter is the threshold wind speed for being windy
|
205
|
+
# 2nd parameter is the utc_time for which you want to know the answer,
|
206
|
+
# this defaults to the current time
|
207
|
+
# NOTE: in my example the values are metric, so the threshold is 10 kph
|
208
|
+
|
209
|
+
weather.windy?(10)
|
210
|
+
|
211
|
+
=== is it wet?
|
212
|
+
|
213
|
+
# 1st parameter is the threshold pop (%) for being wet
|
214
|
+
# 2nd parameter is the utc_time for which you want to know the answer,
|
215
|
+
# this defaults to the current time
|
216
|
+
# NOTE: in my example the threshold is 50 %
|
217
|
+
|
218
|
+
weather.wet?(50)
|
219
|
+
|
220
|
+
=== is it sunny?
|
221
|
+
|
222
|
+
# 1st parameter is the utc_time for which you want to know the answer,
|
223
|
+
# this defaults to the current time
|
224
|
+
|
225
|
+
weather.sunny?
|
226
|
+
|
227
|
+
=== is it day?
|
228
|
+
|
229
|
+
# 1st parameter is the utc_time for which you want to know the answer,
|
230
|
+
# this defaults to the current time
|
231
|
+
|
232
|
+
weather.day?
|
233
|
+
|
234
|
+
=== is it night?
|
235
|
+
|
236
|
+
# 1st parameter is the utc_time for which you want to know the answer,
|
237
|
+
# this defaults to the current time
|
238
|
+
|
239
|
+
weather.night?
|
240
|
+
|
241
|
+
= design
|
242
|
+
|
243
|
+
- create a Barometer instance
|
244
|
+
- supply a query, there are very little restrictions on the format:
|
245
|
+
- city, country, specific address (basically anything Google will geocode)
|
246
|
+
- US zip code (skips geocoding if weather service accepts this directly)
|
247
|
+
- postal code (skips geocoding if weather service accepts this directly)
|
248
|
+
- latitude and longitude (skips geocoding if weather service accepts this
|
249
|
+
directly)
|
250
|
+
- TODO: international airport code (skips geocoding if weather service
|
251
|
+
accepts this directly)
|
252
|
+
- if geocoding required, geocode the query
|
253
|
+
- determine which weather services will be queried (one or multiple)
|
254
|
+
- query the weather services
|
255
|
+
- save the data
|
256
|
+
- repeat weather service queries as needed
|
257
|
+
|
258
|
+
= extending
|
259
|
+
|
260
|
+
Barometer attempts to be a common API to any weather service API. I have included
|
261
|
+
several weather service 'drivers', but I know there are many more available.
|
262
|
+
Please use the provided ones as examples to create more.
|
263
|
+
|
264
|
+
== copyright
|
265
|
+
|
266
|
+
Copyright (c) 2009 Mark G. See LICENSE for details.
|
data/VERSION.yml
ADDED
data/bin/barometer
ADDED
@@ -0,0 +1,63 @@
|
|
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
|
35
|
+
end
|
36
|
+
|
37
|
+
barometer = Barometer.new(ARGV[0])
|
38
|
+
weather = barometer.measure(true)
|
39
|
+
|
40
|
+
def y(value)
|
41
|
+
value ? "yes" : "no"
|
42
|
+
end
|
43
|
+
|
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}"
|
52
|
+
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?)}"
|
58
|
+
puts
|
59
|
+
puts
|
60
|
+
puts " -- INFO --"
|
61
|
+
puts " http://github.com/attack/barometer"
|
62
|
+
puts "---------------------------------------------------"
|
63
|
+
end
|
data/lib/barometer.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'barometer/base'
|
4
|
+
require 'barometer/query'
|
5
|
+
require 'barometer/weather'
|
6
|
+
require 'barometer/services'
|
7
|
+
require 'barometer/data'
|
8
|
+
|
9
|
+
module Barometer
|
10
|
+
|
11
|
+
@@google_geocode_key = nil
|
12
|
+
def self.google_geocode_key; @@google_geocode_key; end;
|
13
|
+
def self.google_geocode_key=(key); @@google_geocode_key = key; end;
|
14
|
+
|
15
|
+
@@skip_graticule = false
|
16
|
+
def self.skip_graticule; @@skip_graticule; end;
|
17
|
+
def self.skip_graticule=(value); @@skip_graticule = value; end;
|
18
|
+
|
19
|
+
# sometimes a query is used as is and never gets geocoded (ie zipcode)
|
20
|
+
# often, it is useful have queries geocoded to know where in the
|
21
|
+
# world that query points to. you can force the geocoding of
|
22
|
+
# queries (even when not required) so that you have the geocoded
|
23
|
+
# data. the reason this isn't the default is that it will use an
|
24
|
+
# extra web service query when not normally required
|
25
|
+
@@force_geocode = false
|
26
|
+
def self.force_geocode; @@force_geocode; end;
|
27
|
+
def self.force_geocode=(value); @@force_geocode = value; end;
|
28
|
+
def self.force_geocode!; @@force_geocode = true; end;
|
29
|
+
|
30
|
+
@@timeout = 15
|
31
|
+
def self.timeout; @@timeout; end;
|
32
|
+
def self.timeout=(value); @@timeout = value; end;
|
33
|
+
|
34
|
+
def self.new(query=nil)
|
35
|
+
Barometer::Base.new(query)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.selection=(selection=nil)
|
39
|
+
Barometer::Base.selection = selection
|
40
|
+
end
|
41
|
+
|
42
|
+
# shortcut to Barometer::Service.source method
|
43
|
+
# allows Barometer.source(:wunderground)
|
44
|
+
def self.source(source)
|
45
|
+
Barometer::Service.source(source)
|
46
|
+
end
|
47
|
+
|
48
|
+
# custom errors
|
49
|
+
class OutOfSources < StandardError; end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Barometer
|
2
|
+
|
3
|
+
class Base
|
4
|
+
|
5
|
+
# allow the configuration of specific weather APIs to be used,
|
6
|
+
# and the order in which they would be used
|
7
|
+
@@selection = { 1 => [:wunderground] }
|
8
|
+
def self.selection; @@selection; end;
|
9
|
+
def self.selection=(hash); @@selection = hash; end;
|
10
|
+
|
11
|
+
attr_reader :query
|
12
|
+
attr_accessor :weather, :success
|
13
|
+
|
14
|
+
def initialize(query=nil)
|
15
|
+
@query = Barometer::Query.new(query)
|
16
|
+
@weather = Barometer::Weather.new
|
17
|
+
@success = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def measure(metric=nil)
|
21
|
+
return nil unless @query
|
22
|
+
|
23
|
+
level = 1
|
24
|
+
until self.success?
|
25
|
+
if sources = @@selection[level]
|
26
|
+
if sources.is_a?(Array)
|
27
|
+
sources.each do |source|
|
28
|
+
measurement = Barometer.source(source.to_sym).measure(@query, metric)
|
29
|
+
@success = true if measurement.success?
|
30
|
+
@weather.measurements << measurement
|
31
|
+
end
|
32
|
+
else
|
33
|
+
measurement = Barometer.source(sources.to_sym).measure(@query, metric)
|
34
|
+
@success = true if measurement.success?
|
35
|
+
@weather.measurements << measurement
|
36
|
+
end
|
37
|
+
else
|
38
|
+
raise OutOfSources
|
39
|
+
end
|
40
|
+
level += 1
|
41
|
+
end
|
42
|
+
|
43
|
+
@weather
|
44
|
+
end
|
45
|
+
|
46
|
+
def success?
|
47
|
+
@success
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|