weatherzone 0.5.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/History.txt +6 -0
  2. data/Manifest.txt +51 -0
  3. data/README.txt +30 -0
  4. data/Rakefile +7 -0
  5. data/bin/weatherzone +0 -0
  6. data/lib/ext/class.rb +175 -0
  7. data/lib/ext/object.rb +74 -0
  8. data/lib/tzinfo/definitions/Australia/CDT.rb +13 -0
  9. data/lib/tzinfo/definitions/Australia/CST.rb +13 -0
  10. data/lib/tzinfo/definitions/Australia/EDT.rb +13 -0
  11. data/lib/tzinfo/definitions/Australia/EST.rb +13 -0
  12. data/lib/tzinfo/definitions/Australia/WDT.rb +13 -0
  13. data/lib/tzinfo/definitions/Australia/WST.rb +13 -0
  14. data/lib/vendor/openuri_memcached/History.txt +18 -0
  15. data/lib/vendor/openuri_memcached/License.txt +20 -0
  16. data/lib/vendor/openuri_memcached/README.markdown +48 -0
  17. data/lib/vendor/openuri_memcached/lib/openuri/common.rb +117 -0
  18. data/lib/vendor/openuri_memcached/lib/openuri/memcached.rb +36 -0
  19. data/lib/vendor/openuri_memcached/lib/openuri/rails-cache.rb +21 -0
  20. data/lib/vendor/openuri_memcached/lib/openuri_memcached.rb +1 -0
  21. data/lib/weatherzone.rb +39 -0
  22. data/lib/weatherzone/connection.rb +85 -0
  23. data/lib/weatherzone/finder.rb +129 -0
  24. data/lib/weatherzone/helpers/almanac_element.rb +28 -0
  25. data/lib/weatherzone/helpers/date_parser.rb +33 -0
  26. data/lib/weatherzone/helpers/units.rb +76 -0
  27. data/lib/weatherzone/resource.rb +46 -0
  28. data/lib/weatherzone/resources/almanac.rb +18 -0
  29. data/lib/weatherzone/resources/almanac_period.rb +51 -0
  30. data/lib/weatherzone/resources/climate_period.rb +8 -0
  31. data/lib/weatherzone/resources/conditions.rb +19 -0
  32. data/lib/weatherzone/resources/country.rb +4 -0
  33. data/lib/weatherzone/resources/daily_observation.rb +10 -0
  34. data/lib/weatherzone/resources/district_forecast.rb +5 -0
  35. data/lib/weatherzone/resources/forecast.rb +47 -0
  36. data/lib/weatherzone/resources/historical_observation.rb +57 -0
  37. data/lib/weatherzone/resources/image.rb +9 -0
  38. data/lib/weatherzone/resources/lift.rb +3 -0
  39. data/lib/weatherzone/resources/location.rb +100 -0
  40. data/lib/weatherzone/resources/marine_forecast.rb +9 -0
  41. data/lib/weatherzone/resources/marine_summary.rb +8 -0
  42. data/lib/weatherzone/resources/moon_phase.rb +23 -0
  43. data/lib/weatherzone/resources/news_item.rb +18 -0
  44. data/lib/weatherzone/resources/point_forecast.rb +40 -0
  45. data/lib/weatherzone/resources/snow_report.rb +5 -0
  46. data/lib/weatherzone/resources/state_forecast.rb +4 -0
  47. data/lib/weatherzone/resources/surf_report.rb +5 -0
  48. data/lib/weatherzone/resources/tide.rb +9 -0
  49. data/lib/weatherzone/resources/warning.rb +21 -0
  50. data/lib/weatherzone/resources/weather.rb +14 -0
  51. data/test/test_almanac.rb +25 -0
  52. data/test/test_almanac_period.rb +33 -0
  53. data/test/test_buoy_observation.rb +24 -0
  54. data/test/test_climate_period.rb +29 -0
  55. data/test/test_conditions.rb +32 -0
  56. data/test/test_connection.rb +28 -0
  57. data/test/test_country.rb +29 -0
  58. data/test/test_daily_observation.rb +29 -0
  59. data/test/test_district_forecast.rb +23 -0
  60. data/test/test_farenheit_conversion_factor.rb +48 -0
  61. data/test/test_finder.rb +148 -0
  62. data/test/test_forecast.rb +30 -0
  63. data/test/test_helper.rb +38 -0
  64. data/test/test_historical_observation.rb +32 -0
  65. data/test/test_image.rb +23 -0
  66. data/test/test_location.rb +70 -0
  67. data/test/test_marine_forecast.rb +27 -0
  68. data/test/test_marine_summary.rb +24 -0
  69. data/test/test_moon_phase.rb +33 -0
  70. data/test/test_news_item.rb +22 -0
  71. data/test/test_point_forecast.rb +24 -0
  72. data/test/test_snow_report.rb +27 -0
  73. data/test/test_state_forecast.rb +23 -0
  74. data/test/test_surf_report.rb +22 -0
  75. data/test/test_tide.rb +23 -0
  76. data/test/test_value_and_unit_helpers.rb +45 -0
  77. data/test/test_warning.rb +23 -0
  78. data/test/test_weather.rb +22 -0
  79. metadata +146 -0
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008-2009 Ben Schwarz
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.
@@ -0,0 +1,48 @@
1
+ # OpenURI with caching
2
+
3
+ Carelessly make OpenURI requests without getting hate mail.
4
+
5
+ ## Running with MemCached
6
+
7
+ Require the library
8
+
9
+ require 'openuri/memcached'
10
+
11
+ Start memcached server
12
+
13
+ ben@Spinners ~/ Ϟ memcached -d
14
+
15
+ Set your memcached host/s (defaults to 127.0.0.1:11211)
16
+
17
+ OpenURI::Cache.host = ['10.1.1.10:11211', '10.1.1.11:11211']
18
+
19
+ The default expiry is 15 minutes, this can be changed using the `expiry` method
20
+
21
+ # Ten long minutes
22
+ OpenURI::Cache.expiry = 600
23
+
24
+ ## Running using Rails cache
25
+
26
+ You can also cache your OpenURI calls using Rails cache.
27
+ require the library using `require openuri/rails-cache`
28
+
29
+ ### Execution
30
+ Use exactly the same as you would OpenURI, only.. enable it.
31
+
32
+ OpenURI::Cache.enable!
33
+ # As slow a wet week
34
+ open("http://ab-c.com.au").read
35
+
36
+ Quit your app (leave memcached running) and re-run the same request, It will come from cache.
37
+
38
+ ### Requirements
39
+
40
+ * Ruby
41
+ * MemCached
42
+ * memcache (gem)
43
+ * You will need to ensure that you have [corresponding version](http://blog.evanweaver.com/files/doc/fauna/memcached/files/COMPATIBILITY.html) of libmemcached to the memcached gem installed for installation to go by breezy
44
+
45
+ ### Contributors
46
+
47
+ * [Ben Askins](http://github.com/benaskins)
48
+ * [Rick Olson](http://github.com/technoweenie)
@@ -0,0 +1,117 @@
1
+ require 'open-uri'
2
+
3
+ module Kernel
4
+ private
5
+ alias openuri_original_open open
6
+ def open(name, *rest, &block)
7
+ if name.respond_to?(:open)
8
+ name.open(*rest, &block)
9
+ elsif name.respond_to?(:to_str) &&
10
+ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ name &&
11
+ (uri = URI.parse(name)).respond_to?(:open)
12
+ OpenURI::open(name, *rest, &block)
13
+ else
14
+ open_uri_original_open(name, *rest, &block)
15
+ end
16
+ end
17
+ module_function :open, :openuri_original_open
18
+ end
19
+
20
+ module OpenURI
21
+ alias original_open open #:nodoc:
22
+ def self.open(uri, *rest, &block)
23
+ if Cache.enabled?
24
+ response = Cache.get(Cache.key_for(uri))
25
+ end
26
+
27
+ unless response
28
+ response = openuri_original_open(uri, *rest).read
29
+ Cache.set(Cache.key_for(uri), response) if Cache.enabled?
30
+ end
31
+
32
+ response = StringIO.new(response)
33
+
34
+ if block_given?
35
+ begin
36
+ yield response
37
+ ensure
38
+ response.close
39
+ end
40
+ else
41
+ response
42
+ end
43
+ end
44
+
45
+ class Cache
46
+
47
+ class KeyHash
48
+ def self.enable!
49
+ @hash_keys = true
50
+ end
51
+
52
+ def self.disable!
53
+ @hash_keys = false
54
+ end
55
+
56
+ def self.enabled?
57
+ @hash_keys
58
+ end
59
+
60
+ def self.disabled?
61
+ !@hash_keys
62
+ end
63
+
64
+ def self.generate(uri)
65
+ Digest::MD5.hexdigest(uri)
66
+ end
67
+ end
68
+
69
+ # Cache is not enabled by default
70
+ @cache_enabled = false
71
+
72
+ class << self
73
+ attr_writer :expiry, :host
74
+
75
+ # Is the cache enabled?
76
+ def enabled?
77
+ @cache_enabled
78
+ end
79
+
80
+ # Enable caching
81
+ def enable!
82
+ raise NotImplementedError
83
+ end
84
+
85
+ # Disable caching - all queries will be run directly
86
+ # using the standard OpenURI `open` method.
87
+ def disable!
88
+ @cache_enabled = false
89
+ end
90
+
91
+ def disabled?
92
+ !@cache_enabled
93
+ end
94
+
95
+ def get(key)
96
+ raise NotImplementedError
97
+ end
98
+
99
+ def set(key, value)
100
+ raise NotImplementedError
101
+ end
102
+
103
+ def key_for(uri)
104
+ Cache::KeyHash.enabled? ? Cache::KeyHash.generate(uri.to_s) : uri.to_s
105
+ end
106
+
107
+ # How long your caches will be kept for (in seconds)
108
+ def expiry
109
+ @expiry ||= 60 * 10
110
+ end
111
+
112
+ def host
113
+ @host ||= "127.0.0.1:11211"
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,36 @@
1
+ require 'openuri/common'
2
+
3
+ begin
4
+ require 'minigems'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ end
8
+
9
+ gem "memcached", ">= 0.10"
10
+ require 'memcached'
11
+
12
+ module OpenURI
13
+ class Cache
14
+ class << self
15
+ # Enable caching
16
+ def enable!
17
+ @cache ||= Memcached.new(host, {
18
+ :namespace => 'openuri',
19
+ :no_block => true,
20
+ :buffer_requests => true
21
+ })
22
+ @cache_enabled = true
23
+ end
24
+
25
+ def get(key)
26
+ @cache.get(key)
27
+ rescue Memcached::NotFound
28
+ false
29
+ end
30
+
31
+ def set(key, value)
32
+ @cache.set(key, value, expiry)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ require 'openuri/common'
2
+
3
+ module OpenURI
4
+ class Cache
5
+ class << self
6
+ # Enable caching
7
+ def enable!
8
+ @cache = Rails.cache
9
+ @cache_enabled = true
10
+ end
11
+
12
+ def get(key)
13
+ @cache.read(key)
14
+ end
15
+
16
+ def set(key, value)
17
+ @cache.write(key, value, :expires_in => expiry)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1 @@
1
+ require 'openuri/memcached'
@@ -0,0 +1,39 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rubygems'
5
+ require 'digest/md5'
6
+ require 'timeout'
7
+
8
+ require 'nokogiri'
9
+ require 'sax-machine'
10
+ require 'tzinfo'
11
+
12
+ begin
13
+ $LOAD_PATH << File.join(File.dirname(__FILE__), *%w[vendor openuri_memcached lib])
14
+ if defined? Rails
15
+ require 'openuri/rails-cache'
16
+ else
17
+ require 'openuri/memcached'
18
+ # Cache for 10 minutes. The web service caches for the same period, so this ensures we've always got
19
+ # the freshest data and the speediest response.
20
+ OpenURI::Cache.expiry = 600
21
+ end
22
+ rescue LoadError
23
+ require 'open-uri'
24
+ end
25
+
26
+ unless defined?(ActiveSupport)
27
+ require 'ext/class'
28
+ require 'ext/object'
29
+ end
30
+
31
+ require 'weatherzone/finder'
32
+ require 'weatherzone/resource'
33
+ require 'weatherzone/helpers/almanac_element'
34
+ require 'weatherzone/helpers/date_parser'
35
+ require 'weatherzone/helpers/units'
36
+
37
+ module Weatherzone
38
+ VERSION = '0.5.8'
39
+ end
@@ -0,0 +1,85 @@
1
+ require 'singleton'
2
+
3
+ module Weatherzone
4
+
5
+ class RequestFailed < Exception
6
+ attr_reader :message
7
+ def initialize(url)
8
+ @message = "Failed to retreive #{url} and no cached version available"
9
+ end
10
+ end
11
+
12
+ class Settings
13
+ include Singleton
14
+ attr_accessor :strip_scale_from_units, :weather_class
15
+ end
16
+
17
+ class Connection
18
+
19
+ DEFAULT_TIMEOUT_AFTER = 1
20
+
21
+ include Singleton
22
+
23
+ attr_accessor :username, :password, :url, :keygen, :logger, :timeout_after
24
+
25
+ def initialize
26
+ @logger = Logger.new(STDOUT)
27
+ @logger.level = Logger::DEBUG
28
+ end
29
+
30
+ def self.connect(username=nil, password=nil, options={}, &block)
31
+ connection = Weatherzone::Connection.instance
32
+ connection.username = username
33
+ connection.password = password
34
+ connection.url = options[:url]
35
+ connection.keygen = block
36
+ connection.logger = options[:logger]
37
+ connection.timeout_after = options[:timeout_after] || DEFAULT_TIMEOUT_AFTER
38
+ end
39
+
40
+ def self.settings
41
+ Weatherzone::Settings.instance
42
+ end
43
+
44
+ def settings
45
+ self.class.settings
46
+ end
47
+
48
+ def key
49
+ @keygen.call
50
+ end
51
+
52
+ def base_url
53
+ @base_url ||= "#{self.url}?u=#{username}&k=#{key}"
54
+ end
55
+
56
+ def wz_url_for(params)
57
+ "#{base_url}#{params}"
58
+ end
59
+
60
+ def request(params)
61
+ url = wz_url_for(params)
62
+ info("GET #{url}")
63
+ timeout(self.timeout_after) do
64
+ response = OpenURI::open(url)
65
+ response.read
66
+ end
67
+ rescue Timeout::Error, SocketError
68
+ error("webservice connection failed")
69
+ raise RequestFailed.new(url)
70
+ end
71
+
72
+ def debug(message)
73
+ @logger.debug("[weatherzone] #{message}") if @logger
74
+ end
75
+
76
+ def info(message)
77
+ @logger.info("[weatherzone] #{message}") if @logger
78
+ end
79
+
80
+ def error(message)
81
+ @logger.error("[weatherzone] #{message}") if @logger
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,129 @@
1
+ module Weatherzone
2
+ module Finder
3
+
4
+ INCLUDES_MAP = {
5
+ :forecasts => "fc=1",
6
+ :point_forecasts => "fc=3",
7
+ :district_forecasts => "dist_fc=1",
8
+ :conditions => "obs=1",
9
+ :warnings => "warn=2",
10
+ :state_forecasts => "state_fc=1",
11
+ :uv_index => "uv=1",
12
+ :sun => "fc_sun=2",
13
+ :moon => "fc_moon=1",
14
+ :historical_observations => "histobs=1",
15
+ :daily_observations => "dlyobs=7",
16
+ :position => "latlon=1",
17
+ :moon_phases => "moon=1",
18
+ :news_items => "news=2",
19
+ :almanac => "almanac=1",
20
+ :links => "links=1",
21
+ :radar_animator => "ra=1",
22
+ :radar_still => "rs=1",
23
+ :satellite_animator => "sa=1",
24
+ :satellite_still => "ss=1",
25
+ :marine_forecast => "marine=1",
26
+ :tides => "tides=4",
27
+ :tide_height => "tideh=1",
28
+ :surf_report => "surf_rpt=2",
29
+ :climate_periods => "climate=1(months=12)",
30
+ :related_locations => "locdet=1",
31
+ :buoy_observations => "buoy=1(period=24)"
32
+ }
33
+
34
+ def self.included(klass)
35
+ klass.class_eval do
36
+ @@connection = Weatherzone::Connection.instance
37
+
38
+ class << self
39
+ def find(options, location_code=nil)
40
+ set_options(options)
41
+ make_request(build_params(location_code, options))
42
+ end
43
+
44
+ def find_by_location_code(location_code, options={})
45
+ options = options.dup
46
+ find(options, location_code)
47
+ end
48
+
49
+ def find_by_twcid(twcid, options={})
50
+ options = options.dup
51
+ options.merge!(:params => "&lt=twcid&lc=#{twcid}")
52
+ find(options)
53
+ end
54
+
55
+ def find_by_location_name(location_name, options={})
56
+ options = options.dup
57
+ location_name = location_name.gsub(" ", "%20").gsub("-", "%20")
58
+ options.merge!(:params => "&lt=aploc&ln=#{location_name}")
59
+ find(options)
60
+ end
61
+
62
+ def find_by_swellnet_code(swellnet_code, options={})
63
+ options = options.dup
64
+ options.merge!(:params => "&lt=swellnet&lc=#{swellnet_code}")
65
+ find(options)
66
+ end
67
+
68
+ def find_by_location_filter(filter, options={})
69
+ options = options.dup
70
+ options.merge!(:params => "&lt=twcid&lf=#{filter}")
71
+ find(options)
72
+ end
73
+
74
+ def find_by_district(district_code, options={})
75
+ options = options.dup
76
+ options.merge!(:params => "&lt=twcid&dist=#{district_code}")
77
+ find(options)
78
+ end
79
+
80
+ def find_districts_by_state(state, options={})
81
+ options = options.dup
82
+ options.merge!(:params => "&lt=dist&state=#{state}")
83
+ find(options).countries.first.locations
84
+ end
85
+
86
+ def build_params(location_code, options)
87
+ params = location_code ? "&lc=#{location_code}" : ""
88
+ params += options.delete(:params) if options[:params]
89
+ params += include_params(options.delete(:include)) if options[:include]
90
+ params += include_image(options.delete(:image)) if options[:image]
91
+ params += parse_params(options) unless options.empty?
92
+ params
93
+ end
94
+
95
+ protected
96
+ def set_options(options)
97
+ @@connection.settings.weather_class ||= self
98
+ self.temperature_unit = options.delete(:temperature_unit)
99
+ end
100
+
101
+ def make_request(params)
102
+ response = @@connection.request(params)
103
+ parse(response)
104
+ end
105
+
106
+ def include_params(includes)
107
+ includes.inject("") do |params, relationship|
108
+ if param = INCLUDES_MAP[relationship]
109
+ params += "&#{param}"
110
+ else
111
+ params
112
+ end
113
+ end
114
+ end
115
+
116
+ def include_image(image)
117
+ "&images=#{image[:type]}(days=#{image[:days]};size=#{image[:size]})"
118
+ end
119
+
120
+ def parse_params(options)
121
+ options.inject("") do |params, (key, value)|
122
+ params += "&#{key}=#{value}"
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end