rubyweather 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
- Simple usage example:
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
- The above will print out a list of available locations and their codes. We can
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 day sinto the future
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
- There are a lot of attributes you can fetch for a forecast day. Here are just a few:
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 #test/test_weather.xml to see what data is
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
- == Download
95
+ === Caching Forecast Data
71
96
 
72
- You can download the latest stable version of RubyWeather from http://rubyforge.org/projects/rubyweather/.
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
- You can also check out the latest copy via subversion from svn://rubyforge.org//var/svn/rubyweather/trunk.
75
- If you would like to contribute back your changes to the code, please contact me via the rubyforge project
76
- site to obtain a subversion account.
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
- RubyWeather can also be installed as a Rails plugin using the following command from your Rails application's
79
- directory:
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
- ./script/plugin install -x svn://rubyforge.org//var/svn/rubyweather/trunk
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
- == Sample Rails Controller
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:
@@ -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
- # (i.e. <tt>myforecast.each { |d| print d.outlook }</tt>).
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) element to individual days to make parsing easier later on
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
- lsup = @xml.root.elements['dayf'].elements['lsup'].text
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 (roughly "days") are based on.
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,
@@ -48,7 +48,17 @@ module Weather
48
48
 
49
49
  # puts "Using url: "+url
50
50
 
51
- xml = Net::HTTP.get(host, url);
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
@@ -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.0.0
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