rubyweather 1.0.0 → 1.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/README +53 -17
- data/lib/weather/forecast.rb +27 -5
- data/lib/weather/service.rb +72 -1
- data/test/service_test.rb +23 -0
- metadata +2 -2
data/README
CHANGED
@@ -6,7 +6,29 @@ License:: GNU Lesser General Public License v2.1 (LGPL 2.1)
|
|
6
6
|
|
7
7
|
<b>RubyWeather is a Ruby[http://ruby-lang.org] library for fetching weather-related data from weather.com[http://www.weather.com/services/xmloap.html].</b>
|
8
8
|
|
9
|
-
|
9
|
+
|
10
|
+
=== Download & Install
|
11
|
+
|
12
|
+
You can download the latest stable version of RubyWeather from http://rubyforge.org/projects/rubyweather
|
13
|
+
or install as a RubyGem:
|
14
|
+
|
15
|
+
sudo gem install rubyweather
|
16
|
+
|
17
|
+
You can also check out the latest copy via subversion from svn://rubyforge.org//var/svn/rubyweather/trunk.
|
18
|
+
If you would like to contribute back your changes to the code, please contact me via the rubyforge project
|
19
|
+
site to obtain a subversion account.
|
20
|
+
|
21
|
+
RubyWeather can also be installed as a Rails plugin using the following command from your Rails application's
|
22
|
+
directory:
|
23
|
+
|
24
|
+
./script/plugin install -x svn://rubyforge.org//var/svn/rubyweather/trunk
|
25
|
+
|
26
|
+
|
27
|
+
=== Examples
|
28
|
+
|
29
|
+
First, we need to find out the weather.com location code for your city.
|
30
|
+
This will print out a list of locations and their codes matching the
|
31
|
+
string "Toronto":
|
10
32
|
|
11
33
|
require 'rubygems'
|
12
34
|
require_gem 'rubyweather'
|
@@ -20,8 +42,7 @@ Simple usage example:
|
|
20
42
|
locations = service.find_location('Toronto')
|
21
43
|
puts "Matching Locations: " + locations.inspect
|
22
44
|
|
23
|
-
|
24
|
-
now use these codes to fetch the weather data for our city:
|
45
|
+
We can now use the code to fetch the weather data for our city:
|
25
46
|
|
26
47
|
forecast = service.fetch_forecast("CAXX0504", 5)
|
27
48
|
|
@@ -34,13 +55,17 @@ now use these codes to fetch the weather data for our city:
|
|
34
55
|
puts "Tomorrow's Outlook: %s" % forecast.tomorrow.outlook
|
35
56
|
puts "Tomorrow's Wind Direction: %s" % forecast.tomorrow.wind.direction
|
36
57
|
|
37
|
-
Forecasts for days in the future are accessed via <tt>forecast.day(#)</tt> where <tt>#</tt> is the number of
|
58
|
+
Forecasts for days in the future are accessed via <tt>forecast.day(#)</tt> where <tt>#</tt> is the number of days into the future
|
38
59
|
(assuming that you've fetched data for as many days in your <tt>service.fetch_forecast</tt> request):
|
39
60
|
|
40
61
|
puts "High 3 days from now: %s" % forecast.day(3).high
|
41
62
|
puts "Probability of precipitation 4 days from now: %s" % forecast.day(4).pop
|
42
63
|
|
43
|
-
|
64
|
+
Nighttime data is also available via <tt>forecast.night(#)</tt>:
|
65
|
+
|
66
|
+
puts "Probability of precipitation three nights from now: %s" % forecast.night(3).pop
|
67
|
+
|
68
|
+
There are a lot of attributes you can fetch for a forecast. Here are just a few:
|
44
69
|
|
45
70
|
+temp+, +temperature+:: The temperature. For future days this is equivalent to the low for nighttime, and high for daytime.
|
46
71
|
+icon+:: The number of the icon gif file from the weather.com SDK[http://www.weather.com/services/xmloap.html] that
|
@@ -58,29 +83,40 @@ There are a lot of attributes you can fetch for a forecast day. Here are just a
|
|
58
83
|
+latest_update+:: The datetime when the conditions were last measured/forecast.
|
59
84
|
|
60
85
|
Additionally, most of the attributes for a given day in the raw weather.com xml data are
|
61
|
-
also directly accessible. For example, you can call forecast.tomorrow.dewp to get the dewpoint, because the xml file
|
62
|
-
contains a <tt>dewp</tt> element for that day. Have a look at
|
86
|
+
also directly accessible. For example, you can call <tt>forecast.tomorrow.dewp</tt> to get the dewpoint, because the xml file
|
87
|
+
contains a <tt>dewp</tt> element for that day. Have a look at <tt>test/test_weather.xml</tt> to see what data is
|
63
88
|
available in the xml file. Note though that raw xml elements will be returned as a string, without any nice
|
64
89
|
class casting or unit conversion.
|
65
90
|
|
66
|
-
Other programmers are encouraged to add more functionality to the lib/forecast.rb module to provide better
|
91
|
+
Other programmers are encouraged to add more functionality to the <tt>lib/forecast.rb</tt> module to provide better
|
67
92
|
accessor methods for the underlying xml data. See below for how to obtain subversion access to contribute
|
68
93
|
your changes back to the project.
|
69
94
|
|
70
|
-
|
95
|
+
=== Caching Forecast Data
|
71
96
|
|
72
|
-
|
97
|
+
RubyWeather supports data caching using the memcached[http://www.danga.com/memcached/] daemon. This allows for
|
98
|
+
much quicker response time -- especially if you have a lot of clients accessing the weather data -- and is
|
99
|
+
just a polite thing to do in regards weather.com's servers.
|
100
|
+
If you have a memcached server running, you can turn on data caching as follows:
|
73
101
|
|
74
|
-
|
75
|
-
|
76
|
-
|
102
|
+
s = Weather::Service.new
|
103
|
+
s.enable_cache
|
104
|
+
s.cache_expiry = 60 # cached data will expire after 60 seconds; if omitted, the default is 10 minutes
|
105
|
+
s.cache.servers = ['127.0.0.1:11211']
|
106
|
+
|
107
|
+
From now on, any fetch_forecast calls made on this service will cache their data. This means that the weather.com
|
108
|
+
server will not be queried again as long as the data is cached.
|
77
109
|
|
78
|
-
|
79
|
-
|
110
|
+
You can check if a forecast came from the cache by calling <tt>#from_cache?</tt>, which returns true if this
|
111
|
+
forecast came from the local cache, or <tt>#cached_on</tt>, which returns the datetime when this forecast was
|
112
|
+
entered into the cache or nil if the forecast didn't come from the cache.
|
80
113
|
|
81
|
-
|
114
|
+
The above requires that a ruby memcache client be installed. RubyWeather has been tested with the
|
115
|
+
Ruby-MemCache[http://www.deveiate.org/projects/RMemCache] implementation which you can install using:
|
116
|
+
|
117
|
+
gem install Ruby-MemCache
|
82
118
|
|
83
|
-
|
119
|
+
=== Sample Rails Controller
|
84
120
|
|
85
121
|
In the <tt>example</tt> directory you will find a sample Rails controller that uses RubyWeather to show
|
86
122
|
a simple weather forecast. To try this out:
|
data/lib/weather/forecast.rb
CHANGED
@@ -14,8 +14,11 @@ module Weather
|
|
14
14
|
|
15
15
|
# Contains several days of weather data.
|
16
16
|
# The forecast object includes the Enumerable mixin, so that you can iterate
|
17
|
-
# over all of the days in the forecast using the standard ruby mechanisms
|
18
|
-
#
|
17
|
+
# over all of the days in the forecast using the standard ruby mechanisms as follows:
|
18
|
+
#
|
19
|
+
# myforecast.each do |d|
|
20
|
+
# print d.outlook
|
21
|
+
# end
|
19
22
|
class Forecast
|
20
23
|
include Enumerable
|
21
24
|
|
@@ -29,9 +32,12 @@ module Weather
|
|
29
32
|
|
30
33
|
@xml = weather_xmldoc
|
31
34
|
|
32
|
-
# add the lsup (latest update)
|
35
|
+
# add the lsup (latest update) and cached_on elements to individual days to make parsing easier later on
|
33
36
|
# FIXME: I can't seem to add the lsup as an element (which would be the consistent way to do it)... adding it as an attribute seems to work though
|
34
|
-
|
37
|
+
if @xml.root.elements['dayf'] and @xml.root.elements['dayf'].elements['lsup']
|
38
|
+
lsup = @xml.root.elements['dayf'].elements['lsup'].text
|
39
|
+
end
|
40
|
+
|
35
41
|
REXML::XPath.match(@xml, "//dayf/day").each do |dxml|
|
36
42
|
dxml.add_attribute "lsup", lsup
|
37
43
|
end
|
@@ -121,9 +127,25 @@ module Weather
|
|
121
127
|
def latest_update
|
122
128
|
Time.parse(xml.root.elements['dayf'].elements['lsup'].text)
|
123
129
|
end
|
130
|
+
|
131
|
+
# The date and time when this forecast was last locally cached.
|
132
|
+
# This attribute will be nil when the forecast comes directly from the weather.com
|
133
|
+
# server or when you do not have the local cache enabled.
|
134
|
+
# See Weather::Service#enable_cache and also the README for instructions on
|
135
|
+
# how to enable local caching using memcached.
|
136
|
+
def cached_on
|
137
|
+
cached_on = xml.root.attributes['cached_on']
|
138
|
+
Time.parse(cached_on) if cached_on
|
139
|
+
end
|
140
|
+
|
141
|
+
# True if this forecast came from the local cache; false otherwise.
|
142
|
+
# See Weather::Forecast.cached_on and Weather::Service#enable_cache.
|
143
|
+
def from_cache?
|
144
|
+
not cached_on.nil?
|
145
|
+
end
|
124
146
|
end
|
125
147
|
|
126
|
-
# Abstract class that all Forecast entities
|
148
|
+
# Abstract class that all Forecast entities are based on.
|
127
149
|
class Conditions
|
128
150
|
|
129
151
|
# For elements in the forecast that we have not defined an explicit accessor,
|
data/lib/weather/service.rb
CHANGED
@@ -48,7 +48,17 @@ module Weather
|
|
48
48
|
|
49
49
|
# puts "Using url: "+url
|
50
50
|
|
51
|
-
xml =
|
51
|
+
if cache? and xml = cache.get("#{location_id}:#{days}")
|
52
|
+
else
|
53
|
+
xml = Net::HTTP.get(host, url)
|
54
|
+
|
55
|
+
if cache?
|
56
|
+
doc = REXML::Document.new(xml)
|
57
|
+
doc.root.attributes['cached_on'] = Time.now
|
58
|
+
cache.set("#{location_id}:#{days}", doc.to_s, cache_expiry)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
52
62
|
doc = REXML::Document.new(xml)
|
53
63
|
|
54
64
|
Forecast::Forecast.new(doc)
|
@@ -80,5 +90,66 @@ module Weather
|
|
80
90
|
|
81
91
|
return locations
|
82
92
|
end
|
93
|
+
|
94
|
+
|
95
|
+
@cache = false
|
96
|
+
|
97
|
+
# Turns on weather forecast caching.
|
98
|
+
# See Weather::Service::Cache
|
99
|
+
def enable_cache(enable = true)
|
100
|
+
if enable
|
101
|
+
extend Cache
|
102
|
+
@cache = true
|
103
|
+
else
|
104
|
+
@cache = false
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# True if caching is enabled, false otherwise.
|
109
|
+
def cache?
|
110
|
+
@cache and cache.active?
|
111
|
+
end
|
112
|
+
|
113
|
+
# Turns off weather forecast caching.
|
114
|
+
# See Weather::Service::Cache
|
115
|
+
def disable_cache
|
116
|
+
enable_cache false
|
117
|
+
end
|
118
|
+
|
119
|
+
# Memcache functionality for Weather::Service.
|
120
|
+
# This is automatically mixed in when you call Weather::Service#enable_cache
|
121
|
+
module Cache
|
122
|
+
|
123
|
+
# The MemCache client instance currently being used.
|
124
|
+
# To set the memcache servers, use:
|
125
|
+
#
|
126
|
+
# service.cache.servers = ["127.0.0.1:11211"]
|
127
|
+
def cache
|
128
|
+
@memcache ||= MemCache.new(:namespace => 'RubyWeather')
|
129
|
+
end
|
130
|
+
|
131
|
+
# Sets how long forecast data should be cached (in seconds).
|
132
|
+
def cache_expiry=(seconds)
|
133
|
+
@cache_expiry = seconds
|
134
|
+
end
|
135
|
+
# The current cache_expiry setting, in seconds.
|
136
|
+
def cache_expiry
|
137
|
+
@cache_expiry || 60 * 10
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
def self.extend_object(o)
|
142
|
+
begin
|
143
|
+
require 'memcache'
|
144
|
+
rescue LoadError
|
145
|
+
require 'rubygems'
|
146
|
+
# We use Ruby-MemCache because it works. Despite my best efforts, I
|
147
|
+
# couldn't get the memcache-client implementation working properly.
|
148
|
+
require_gem 'Ruby-MemCache'
|
149
|
+
require 'memcache'
|
150
|
+
end
|
151
|
+
super
|
152
|
+
end
|
153
|
+
end
|
83
154
|
end
|
84
155
|
end
|
data/test/service_test.rb
CHANGED
@@ -59,4 +59,27 @@ class ServiceTest < Test::Unit::TestCase
|
|
59
59
|
locations = @service.find_location("Toronto")
|
60
60
|
assert(locations.has_key?(TEST_LOCATION))
|
61
61
|
end
|
62
|
+
|
63
|
+
def test_caching
|
64
|
+
# This assumes that we have a memcache server running on localhost:11211!
|
65
|
+
@service.enable_cache
|
66
|
+
@service.cache.servers = "localhost:11211"
|
67
|
+
|
68
|
+
@service.cache.delete("#{TEST_LOCATION}:5")
|
69
|
+
|
70
|
+
assert_equal @service.fetch_forecast(TEST_LOCATION, 5).xml.to_s, @service.fetch_forecast(TEST_LOCATION, 5).xml.to_s.gsub(/ cached_on='.*?'/, '')
|
71
|
+
|
72
|
+
assert @service.fetch_forecast(TEST_LOCATION, 5).from_cache?
|
73
|
+
|
74
|
+
xml = @service.cache.get("#{TEST_LOCATION}:5")
|
75
|
+
assert xml and !xml.empty?
|
76
|
+
|
77
|
+
@service.cache_expiry = 1
|
78
|
+
|
79
|
+
@service.fetch_forecast(TEST_LOCATION, 2)
|
80
|
+
sleep(2)
|
81
|
+
assert_nil @service.cache.get("#{TEST_LOCATION}:2")
|
82
|
+
|
83
|
+
assert !@service.fetch_forecast(TEST_LOCATION, 2).from_cache?
|
84
|
+
end
|
62
85
|
end
|
metadata
CHANGED
@@ -3,7 +3,7 @@ rubygems_version: 0.8.11
|
|
3
3
|
specification_version: 1
|
4
4
|
name: rubyweather
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.
|
6
|
+
version: 1.1.0
|
7
7
|
date: 2006-07-28 00:00:00 -04:00
|
8
8
|
summary: Client library for accessing weather.com's xoap weather data.
|
9
9
|
require_paths:
|
@@ -98,7 +98,7 @@ test_files:
|
|
98
98
|
- test/service_test.rb
|
99
99
|
rdoc_options:
|
100
100
|
- --title
|
101
|
-
- RubyWeather RDocs
|
101
|
+
- RubyWeather 1.1.0 RDocs
|
102
102
|
- --main
|
103
103
|
- README
|
104
104
|
- --line-numbers
|