weatherbug 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/weatherbug.rb ADDED
@@ -0,0 +1,117 @@
1
+
2
+ require 'rubygems'
3
+ require 'nokogiri'
4
+ require 'open-uri'
5
+
6
+ module WeatherBug
7
+
8
+ require 'weatherbug/hash_methods'
9
+ require 'weatherbug/transformable_data'
10
+ autoload :Station, 'weatherbug/station'
11
+ autoload :StationHint, 'weatherbug/station_hint'
12
+ autoload :LiveObservation, 'weatherbug/live_observation'
13
+ autoload :Forecast, 'weatherbug/forecast'
14
+
15
+ API_URL = 'datafeed.weatherbug.com'
16
+
17
+
18
+ # Set your application's partner ID
19
+ def self.partner_id=(partner_id)
20
+ @partner_id = partner_id
21
+ end
22
+
23
+ # Get the 25 closest stations, or less in less dense areas
24
+ def self.nearby_stations(lookup_options)
25
+ HashMethods.valid_keys(lookup_options, [:zip_code, :city_code, :postal_code, :include_pws])
26
+ HashMethods.valid_one_only(lookup_options, [:zip_code, :city_code])
27
+ HashMethods.valid_one_only(lookup_options, [:postal_code, :city_code])
28
+ HashMethods.valid_needs(lookup_options, :zip_code, :postal_code) if lookup_options.has_key?(:postal_code)
29
+ response = make_request('StationList', HashMethods.convert_symbols(lookup_options))
30
+
31
+ response.xpath('/aws:weather/aws:station').map { |station| WeatherBug::Station.from_document(station) }
32
+ end
33
+
34
+ def self.closest_station(lookup_options)
35
+ HashMethods.valid_keys(lookup_options, [:zip_code, :city_code, :postal_code, :include_pws])
36
+ HashMethods.valid_one_only(lookup_options, [:zip_code, :city_code])
37
+ HashMethods.valid_one_only(lookup_options, [:postal_code, :city_code])
38
+ HashMethods.valid_needs(lookup_options, :zip_code, :postal_code) if lookup_options.has_key?(:postal_code)
39
+ params = HashMethods.convert_symbols(lookup_options)
40
+ params['ListType'] = '1'
41
+ response = make_request('StationList', params)
42
+
43
+ WeatherBug::Station.from_document response.xpath('/aws:weather/aws:station').first
44
+ end
45
+
46
+ def self.get_station(station_id)
47
+ params = {'StationId' => station_id}
48
+ response = make_request('StationInfo', params)
49
+
50
+ WeatherBug::Station.from_document response.xpath('/aws:weather/aws:station').first
51
+ end
52
+
53
+ def self.live_observation(station, unit_type = :f)
54
+ station_id = station.is_a?(WeatherBug::Station) ? station.station_id : station
55
+ params = {'StationId' => station_id}
56
+ params['UnitType'] = '1' if unit_type == :c
57
+ response = make_request('LiveObservations', params)
58
+
59
+ live_observation = WeatherBug::LiveObservation.from_document response.xpath('/aws:weather/aws:ob').first
60
+ live_observation.send(:station_reference=, station) if station.is_a?(WeatherBug::Station)
61
+ live_observation
62
+ end
63
+
64
+ # Get the list of stations in a bounding box
65
+ # Does not return full stations
66
+ def self.stations_in_box(top_right_lat, top_right_lng, bot_left_lat, bot_left_lng)
67
+ params = {
68
+ 'LatitudeTopRight' => top_right_lat,
69
+ 'LongitudeTopRight' => top_right_lng,
70
+ 'LatitudeBottomLeft' => bot_left_lat,
71
+ 'LongitudeBottomLeft' => bot_left_lng
72
+ }
73
+ response = make_request('StationListByLatLng', params)
74
+
75
+ station_data = response.xpath('/aws:weather/aws:stations/aws:station')
76
+ return nil unless station_data
77
+
78
+ station_data.map do |sdata|
79
+ WeatherBug::StationHint.from_document(sdata)
80
+ end
81
+ end
82
+
83
+ def self.forecast(options)
84
+ self.retrieve_forecast(1, options)
85
+ end
86
+
87
+ private
88
+
89
+ # TODO add validation for fields
90
+ def self.retrieve_forecast(forecast_type, options)
91
+ HashMethods.valid_keys(options, [:latitude, :longitude])
92
+ params = HashMethods.convert_symbols(options)
93
+ params['ForecastType'] = forecast_type
94
+ response = make_request(options.has_key?(:longitude) ? 'ClosestForecastByLatLng' : 'Forecast', params)
95
+
96
+ forecast_data = response.xpath('/aws:weather/aws:forecasts/aws:forecast')
97
+ return nil unless forecast_data
98
+
99
+ forecast_data.map do |fdata|
100
+ WeatherBug::Forecast.from_document(fdata)
101
+ end
102
+ end
103
+
104
+ # Make the actual request, unwrap it and send it back
105
+ # Handles errors that may come up
106
+ def self.make_request(method, params)
107
+ params['RequestType'] = method
108
+ params['PartnerId'] = @partner_id
109
+ data = open("http://#{API_URL}/GetXml.aspx?#{params.map { |k, v| "#{k}=#{v}" }.join('&')}")
110
+ raise Exception.new("Unexpected exception - empty response") unless data
111
+ Nokogiri::XML(data)
112
+ end
113
+
114
+ end
115
+
116
+ WeatherBug.partner_id = 'f0c313fe-fffe-4f5d-96dc-5a5d6ec9eb30'
117
+ stations = WeatherBug::nearby_stations(:zip_code => '08005')
@@ -0,0 +1,18 @@
1
+ module WeatherBug
2
+
3
+ class Forecast < TransformableData
4
+
5
+ register_transformer 'aws:title', :required => true
6
+ register_transformer 'aws:city'
7
+ register_transformer 'aws:short-title'
8
+ register_transformer 'aws:icon'
9
+ register_transformer 'aws:description'
10
+ register_transformer 'aws:prediction'
11
+ register_transformer 'aws:lo', :name => :temp_low, :transform => :to_i
12
+ register_transformer 'aws:hi', :name => :temp_high, :transform => :to_i
13
+ register_transformer 'aws:hi/@units', :name => :temp_units
14
+ register_transformer 'aws:prob-of-precip', :name => :probability_of_precipitation
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,40 @@
1
+ module WeatherBug
2
+
3
+ class HashMethods
4
+
5
+ WB_MAPPING = {
6
+ :zip_code => 'ZipCode',
7
+ :city_code => 'CityCode',
8
+ :postal_code => 'PostalCode',
9
+ :include_pws => 'IncludePWS',
10
+ :latitude => 'Latitude',
11
+ :longitude => 'Longitude'
12
+ }
13
+
14
+ # Raise an argument error if we get invalid keys at all
15
+ def self.valid_keys(hash, allowed)
16
+ invalids = nil
17
+ hash.keys.each { |key| (invalids ||= []; invalids << key) unless allowed.include?(key) }
18
+ raise ArgumentError.new("Invalid options: #{invalids.join(', ')}") unless invalids.nil?
19
+ end
20
+
21
+ def self.valid_one_only(hash, conflictions)
22
+ conflicts = []
23
+ conflictions.each { |c| conflicts << c if hash.keys.include?(c) }
24
+ raise ArgumentError.new("Cannot combine options: #{conflicts.join(', ')}") if conflicts.size > 1
25
+ end
26
+
27
+ def self.valid_needs(hash, key, key_for)
28
+ raise ArgumentError.new("You must supply option #{key} to use #{key_for}") unless hash.has_key?(key)
29
+ end
30
+
31
+ def self.convert_symbols(hash)
32
+ hash.inject({}) do |rhash, (original_key, value)|
33
+ rhash[WB_MAPPING.fetch(original_key, original_key)] = value
34
+ rhash
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,34 @@
1
+ module WeatherBug
2
+
3
+ class LiveObservation < TransformableData
4
+
5
+ register_transformer 'aws:station-id'
6
+ register_transformer 'aws:station', :name => :station_name
7
+ register_transformer 'aws:temp-high/@units', :name => :temp_units
8
+ register_transformer 'aws:temp-high', :name => :temp_high, :transform => :to_i
9
+ register_transformer 'aws:temp-low', :name => :temp_low, :transform => :to_i
10
+ register_transformer 'aws:dew-point', :transform => :to_i
11
+ register_transformer 'aws:elevation', :transform => :to_i
12
+ register_transformer 'aws:feels-like', :transform => :to_i
13
+ register_transformer 'aws:humidity', :transform => :to_i
14
+ register_transformer 'aws:wind-direction'
15
+ register_transformer 'aws:wind-speed', :transform => :to_i
16
+ register_transformer 'aws:pressure', :transform => :to_f
17
+
18
+ def self.from_document(document)
19
+ lops = super
20
+ raise ArgumentError.new('No such station') if lops.station_id.empty?
21
+ lops
22
+ end
23
+
24
+ def station
25
+ @station_reference ||= WeatherBug.get_station(self.station_id)
26
+ end
27
+
28
+ private
29
+
30
+ attr_writer :station_reference
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,33 @@
1
+ module WeatherBug
2
+
3
+ class Station < TransformableData
4
+
5
+ register_transformer '@id', :name => :station_id
6
+ register_transformer '@name'
7
+ register_transformer '@distance', :transform => :to_f
8
+ register_transformer '@zipcode', :name => :zip_code
9
+ register_transformer '@city-code', :transform => :to_i
10
+ register_transformer '@station-type'
11
+ register_transformer '@city'
12
+ register_transformer '@state'
13
+ register_transformer '@country'
14
+ register_transformer '@latitude', :transform => :to_f
15
+ register_transformer '@longitude', :transform => :to_f
16
+
17
+ def self.from_document(document)
18
+ station = super
19
+ raise ArgumentError.new('No such station') if station.station_id.empty?
20
+ station
21
+ end
22
+
23
+ def live_observation(unit = :f)
24
+ WeatherBug.live_observation(self, unit)
25
+ end
26
+
27
+ def forecast
28
+ WeatherBug.forecast(:latitude => latitude, :longitude => longitude)
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,14 @@
1
+ module WeatherBug
2
+
3
+ class StationHint < TransformableData
4
+
5
+ register_transformer '@id', :name => :station_id
6
+ register_transformer '@name'
7
+
8
+ def station
9
+ @station ||= WeatherBug.get_station(station_id)
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -0,0 +1,31 @@
1
+ module WeatherBug
2
+
3
+ # This is a super-convenient class for transorming data and requiring things in the
4
+ # different associated WeatherBug data classes
5
+ class TransformableData
6
+
7
+ def self.register_transformer(name, options = {})
8
+ @transformers ||= {}
9
+ options[:name] = name.split(/[:@]/).last.gsub('-', '_').to_sym unless options.has_key?(:name)
10
+ private; attr_writer options[:name]
11
+ public; attr_reader options[:name]
12
+ @transformers[name] = options
13
+ end
14
+
15
+ def self.from_document(document)
16
+ object = self.new
17
+ @transformers.each do |key, options|
18
+ value = document.xpath(key).text
19
+ # If this thing is empty, skip it
20
+ next if value.nil?
21
+ # If this thing needs to be transformed, transform it (from a string)
22
+ value = value.send(options[:transform]) if options.has_key?(:transform)
23
+ # Assign it on the class
24
+ object.send("#{options[:name]}=", value)
25
+ end
26
+ object
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,9 @@
1
+ module WeatherBug
2
+
3
+ VERSION = [0, 0, 1]
4
+
5
+ def self.version
6
+ VERSION.join('.')
7
+ end
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: weatherbug
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - John Crepezzi
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-19 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ description: Light Wrapper for the WeatherBug partner API
36
+ email: john.crepezzi@patch.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - lib/weatherbug/forecast.rb
45
+ - lib/weatherbug/hash_methods.rb
46
+ - lib/weatherbug/live_observation.rb
47
+ - lib/weatherbug/station.rb
48
+ - lib/weatherbug/station_hint.rb
49
+ - lib/weatherbug/transformable_data.rb
50
+ - lib/weatherbug/version.rb
51
+ - lib/weatherbug.rb
52
+ has_rdoc: true
53
+ homepage: http://github.com/seejohnrun/weatherbug
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options: []
58
+
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ requirements: []
80
+
81
+ rubyforge_project: weatherbug
82
+ rubygems_version: 1.3.7
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: Ruby WeatherBug partner API
86
+ test_files: []
87
+