global_weather 0.0.1
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/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +86 -0
- data/Rakefile +18 -0
- data/TODO +5 -0
- data/global_weather.gemspec +29 -0
- data/lib/global_weather.rb +11 -0
- data/lib/global_weather/client.rb +29 -0
- data/lib/global_weather/country.rb +49 -0
- data/lib/global_weather/errors.rb +10 -0
- data/lib/global_weather/utils.rb +21 -0
- data/lib/global_weather/version.rb +3 -0
- data/lib/global_weather/weather.rb +85 -0
- data/test/functional/country_functional_test.rb +26 -0
- data/test/functional/weather_functional_test.rb +47 -0
- data/test/test_helper.rb +17 -0
- data/test/unit/utils_test.rb +16 -0
- data/test/unit/weather_test.rb +51 -0
- data/test/vcr/vcr_cassettes/cities_by_country_basic_call.yml +197 -0
- data/test/vcr/vcr_cassettes/cities_by_country_invalid_country.yml +49 -0
- data/test/vcr/vcr_cassettes/get_weather_basic_call.yml +56 -0
- data/test/vcr/vcr_cassettes/get_weather_invalid_1.yml +48 -0
- data/test/vcr/vcr_cassettes/get_weather_invalid_2.yml +48 -0
- data/test/vcr/vcr_cassettes/get_weather_invalid_3.yml +48 -0
- data/wsdl/globalweather.asmx.xml +209 -0
- metadata +211 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Eugen Ciur
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# GlobalWeather
|
2
|
+
|
3
|
+
[Global Weather SOAP
|
4
|
+
service](http://www.webservicex.net/WS/WSDetails.aspx?WSID=56&CATID=12)
|
5
|
+
ruby wrapper.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'global_weather'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install global_weather
|
20
|
+
|
21
|
+
This gem was tested on ruby 1.9.3p327
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
Basically GlobalWeather gem provides 2 classes Country (wraps
|
26
|
+
GetCitiesByCountry soap method call) and Weather (wraps GetWeather method call)
|
27
|
+
|
28
|
+
To get cities by country (e.g. Germany)
|
29
|
+
|
30
|
+
|
31
|
+
country = GlobalWeather::Country.new 'Germany'
|
32
|
+
|
33
|
+
country.cities # returns an array of strings
|
34
|
+
|
35
|
+
|
36
|
+
In order to get current weather (e.g. country 'Germany', city 'Berlin')
|
37
|
+
|
38
|
+
|
39
|
+
weather = GlobalWeather::Weather.new 'Germany', 'Berlin'
|
40
|
+
|
41
|
+
puts weather.time[:UTC]
|
42
|
+
|
43
|
+
puts weather.temperature[:C]
|
44
|
+
|
45
|
+
puts weather.temperature[:F]
|
46
|
+
|
47
|
+
puts weather.pressure[:Hg]
|
48
|
+
|
49
|
+
puts weather.pressure[:hPa]
|
50
|
+
|
51
|
+
|
52
|
+
See full list of attributes
|
53
|
+
|
54
|
+
|
55
|
+
GlobalWeather::Weather::ATTRIBUTES
|
56
|
+
|
57
|
+
|
58
|
+
Can it be simplier?
|
59
|
+
|
60
|
+
## Test
|
61
|
+
|
62
|
+
Command
|
63
|
+
|
64
|
+
|
65
|
+
$ rake
|
66
|
+
|
67
|
+
or
|
68
|
+
|
69
|
+
|
70
|
+
$ rake test
|
71
|
+
|
72
|
+
will run all tests
|
73
|
+
|
74
|
+
|
75
|
+
## Configuration
|
76
|
+
|
77
|
+
There is only one client instance shared among all instances of GlobalWeather
|
78
|
+
Weather and Country objects.
|
79
|
+
|
80
|
+
GlobalWeather::Client.configure do |config|
|
81
|
+
config.log false
|
82
|
+
config.proxy 'http://my_company_internal_proxy'
|
83
|
+
end
|
84
|
+
|
85
|
+
options provided this way will be passed futher down to Savon.client. See full list of all
|
86
|
+
at [savon documentation](http://savonrb.com/version2/globals.html)
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
|
5
|
+
Rake::TestTask.new(:test) do |test|
|
6
|
+
test.libs << 'lib' << 'test'
|
7
|
+
test.pattern = 'test/**/*_test.rb'
|
8
|
+
test.verbose = false
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
Rake::TestTask.new(:test_unit) do |test|
|
13
|
+
test.libs << 'lib' << 'test'
|
14
|
+
test.pattern = 'test/unit/**/*_test.rb'
|
15
|
+
test.verbose = false
|
16
|
+
end
|
17
|
+
|
18
|
+
task :default => :test
|
data/TODO
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'global_weather/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "global_weather"
|
8
|
+
spec.version = GlobalWeather::VERSION
|
9
|
+
spec.authors = ["Eugen Ciur"]
|
10
|
+
spec.email = ["ciur.eugen@gmail.com"]
|
11
|
+
spec.description = %q{GlobalWeather SOAP service wrapper}
|
12
|
+
spec.summary = %q{Provides Weather and Country objects to use easy GlobalWeather service}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT or Do whatever you want with this gem!"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', "~> 1.3"
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'debugger'
|
24
|
+
spec.add_development_dependency 'vcr'
|
25
|
+
spec.add_development_dependency 'webmock', '~> 1.8'
|
26
|
+
spec.add_dependency 'excon', '0.22'
|
27
|
+
spec.add_dependency 'savon', '~> 2.0'
|
28
|
+
spec.add_dependency 'nori', "~> 2.3.0"
|
29
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'savon'
|
2
|
+
require "global_weather/version"
|
3
|
+
require "global_weather/utils"
|
4
|
+
require "global_weather/client"
|
5
|
+
require "global_weather/errors"
|
6
|
+
require "global_weather/country"
|
7
|
+
require "global_weather/weather"
|
8
|
+
|
9
|
+
module GlobalWeather
|
10
|
+
include Errors
|
11
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module GlobalWeather
|
2
|
+
|
3
|
+
class Client
|
4
|
+
@@client = nil
|
5
|
+
@@config = {}
|
6
|
+
|
7
|
+
def self.connect(options = {})
|
8
|
+
|
9
|
+
@@config.merge!(options)
|
10
|
+
|
11
|
+
if @@client.nil?
|
12
|
+
@@client = Savon.client({wsdl: Utils.local_wsdl_file}.merge(@@config))
|
13
|
+
else
|
14
|
+
@@client
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.configure(&block)
|
19
|
+
class_eval &block
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def method_missing(name, *args)
|
24
|
+
@@config[name.to_sym] = args[0]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module GlobalWeather
|
2
|
+
class Country
|
3
|
+
|
4
|
+
attr_reader :name, :cities
|
5
|
+
|
6
|
+
def initialize(name = nil, options = {})
|
7
|
+
|
8
|
+
client = Client.connect(options)
|
9
|
+
|
10
|
+
raise Errors::CountryNotProvided if name.nil?
|
11
|
+
|
12
|
+
@name = name
|
13
|
+
|
14
|
+
response = client.call(:get_cities_by_country) do |locals|
|
15
|
+
locals.message 'CountryName' => name
|
16
|
+
end
|
17
|
+
|
18
|
+
if response.success?
|
19
|
+
body = response.hash[:envelope] && response.hash[:envelope][:body]
|
20
|
+
if body
|
21
|
+
result = body[:get_cities_by_country_response] && body[:get_cities_by_country_response][:get_cities_by_country_result]
|
22
|
+
create_attributes(result)
|
23
|
+
end
|
24
|
+
else
|
25
|
+
raise Errors::RequestFailure, 'Request to SOAP service failed'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
include Utils
|
32
|
+
|
33
|
+
# Instaniates attribute 'cites' with an array of strings (cities)
|
34
|
+
def create_attributes(result)
|
35
|
+
hrep = Nori.new.parse(result)
|
36
|
+
new_data_set = hrep && hrep['NewDataSet']
|
37
|
+
if new_data_set
|
38
|
+
hash_of_cities = new_data_set && new_data_set['Table']
|
39
|
+
if hash_of_cities
|
40
|
+
@cities = hash_of_cities.map{|item| item['City']}
|
41
|
+
else
|
42
|
+
raise Errors::InvalidResponseFormat, 'Unexpected response format'
|
43
|
+
end
|
44
|
+
else
|
45
|
+
raise Errors::CountryInvalid, 'Returned data set is empty, probably because provided country is invalid'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module GlobalWeather
|
2
|
+
module Errors
|
3
|
+
class CityNotProvided < Exception; end
|
4
|
+
class CountryNotProvided < Exception; end
|
5
|
+
class CityOrCountryInvalid < Exception; end
|
6
|
+
class CountryInvalid < Exception; end
|
7
|
+
class InvalidResponseFormat < Exception; end
|
8
|
+
class RequestFailure < Exception; end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GlobalWeather
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
# Returns local copy of wsdl schema file
|
5
|
+
def self.local_wsdl_file
|
6
|
+
current_file = File.expand_path(File.dirname(__FILE__))
|
7
|
+
File.open(File.join(current_file,'..','..','wsdl','globalweather.asmx.xml'))
|
8
|
+
end
|
9
|
+
|
10
|
+
# Camelize input string
|
11
|
+
def camelize(lower_case_and_underscored_word)
|
12
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
13
|
+
end
|
14
|
+
|
15
|
+
# Removes <?xml ... /xml> header from xml string
|
16
|
+
def fix_header!(string_xml)
|
17
|
+
string_xml.gsub!(/\<\?.+\?\>/,'')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module GlobalWeather
|
2
|
+
class Weather
|
3
|
+
|
4
|
+
ATTRIBUTES = %w(location time wind temperature
|
5
|
+
dew_point relative_humidity pressure sky_conditions
|
6
|
+
visibility)
|
7
|
+
|
8
|
+
attr_reader *ATTRIBUTES
|
9
|
+
|
10
|
+
def initialize(country = nil, city = nil, options = {})
|
11
|
+
|
12
|
+
client = Client.connect(options)
|
13
|
+
|
14
|
+
raise Errors::CityNotProvided if city.nil?
|
15
|
+
raise Errors::CountryNotProvided if country.nil?
|
16
|
+
|
17
|
+
response = client.call(:get_weather) do |locals|
|
18
|
+
locals.message 'CountryName' => country, 'CityName' => city
|
19
|
+
end
|
20
|
+
|
21
|
+
if response.success?
|
22
|
+
body = response.hash[:envelope] && response.hash[:envelope][:body]
|
23
|
+
if body
|
24
|
+
result = body[:get_weather_response] && body[:get_weather_response][:get_weather_result]
|
25
|
+
create_attributes(result)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
raise Errors::RequestFailure, 'Request to SOAP service failed'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
include Utils
|
35
|
+
|
36
|
+
def create_attributes(string_xml_or_hash)
|
37
|
+
# Current weather information are returning as xml string instead of hash
|
38
|
+
# That is why I parse it here with Nori into a hash
|
39
|
+
hrep = if string_xml_or_hash.is_a?(String)
|
40
|
+
raise Errors::CityOrCountryInvalid if string_xml_or_hash =~ /Data Not Found/
|
41
|
+
Nori.new.parse(fix_header!(string_xml_or_hash))
|
42
|
+
# in case service will return result as hash, do nothing
|
43
|
+
elsif string_xml_or_hash.is_a?(Hash)
|
44
|
+
string_xml_or_hash
|
45
|
+
end
|
46
|
+
|
47
|
+
ATTRIBUTES.each do |attribute|
|
48
|
+
# camelize method is included from Utils
|
49
|
+
instance_variable_set("@#{attribute}", hrep['CurrentWeather'][camelize(attribute)])
|
50
|
+
end
|
51
|
+
|
52
|
+
@temperature = Weather.convert_temperature_to_hash(@temperature)
|
53
|
+
@time = Weather.convert_time_to_datetime(@time)
|
54
|
+
@dew_point = Weather.convert_temperature_to_hash(@dew_point)
|
55
|
+
@pressure = Weather.convert_pressure_to_hash(@pressure)
|
56
|
+
@relative_humidity = @relative_humidity.to_i
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.convert_temperature_to_hash(input)
|
60
|
+
output = {}
|
61
|
+
/\s*(?<fahrenheit>\-?\d+) F\s*\((?<celsius>\-?\d+) C\)/ =~ input
|
62
|
+
output[:c] = output[:C] = celsius.to_i
|
63
|
+
output[:f] = output[:F] = fahrenheit.to_i
|
64
|
+
output
|
65
|
+
end
|
66
|
+
# Aug 24, 2013 - 11:20 PM EDT / 2013.08.25 0320 UTC&
|
67
|
+
def self.convert_time_to_datetime(input)
|
68
|
+
output = {}
|
69
|
+
edt_and_utc = input.split('/')
|
70
|
+
if edt_and_utc
|
71
|
+
output[:EDT] = output[:edt] = DateTime.parse(edt_and_utc[0].strip) unless edt_and_utc[0].nil?
|
72
|
+
output[:UTC] = output[:utc] = DateTime.strptime(edt_and_utc[1].strip,'%Y.%m.%d %H%M UTC') unless edt_and_utc[1].nil?
|
73
|
+
end
|
74
|
+
output
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.convert_pressure_to_hash(input)
|
78
|
+
output = {}
|
79
|
+
/\s*(?<hg>\d+\.*\d* in\. Hg) \s*\((?<hpa>\d+\.*\d*) hPa\)/ =~ input
|
80
|
+
output[:Hg] = output[:hg] = hg.to_f
|
81
|
+
output[:hPa] = output[:hpa] = hpa.to_f
|
82
|
+
output
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
|
3
|
+
class CountryPositiveTests < Test::Unit::TestCase
|
4
|
+
def test_basic_call
|
5
|
+
VCR.use_cassette('cities_by_country_basic_call') do
|
6
|
+
country = GlobalWeather::Country.new 'Germany'
|
7
|
+
assert country.cities.include?('Hannover'), "Following list of cities #{country.cities} does not include Hannover"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class CountryNegativeTests < Test::Unit::TestCase
|
13
|
+
def test_no_arguments_provided
|
14
|
+
assert_raise GlobalWeather::CountryNotProvided do
|
15
|
+
GlobalWeather::Country.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_invalid_country_provided
|
20
|
+
VCR.use_cassette('cities_by_country_invalid_country') do
|
21
|
+
assert_raise GlobalWeather::CountryInvalid do
|
22
|
+
GlobalWeather::Country.new 'FarFarFarAway'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
|
3
|
+
class GlobalWeatherPositiveTests < Test::Unit::TestCase
|
4
|
+
def test_basic_call
|
5
|
+
# This is REAL service call
|
6
|
+
weather = GlobalWeather::Weather.new 'Germany', 'Berlin', log: false
|
7
|
+
|
8
|
+
assert !weather.time.nil?
|
9
|
+
assert !weather.location.nil?
|
10
|
+
assert !weather.temperature.nil?
|
11
|
+
assert !weather.pressure.nil?
|
12
|
+
assert !weather.relative_humidity.nil?
|
13
|
+
assert !weather.dew_point.nil?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class GlobalWeatherNegativeTests < Test::Unit::TestCase
|
18
|
+
def test_no_arguments_provided
|
19
|
+
assert_raise GlobalWeather::CityNotProvided, GlobalWeather::CountryNotProvided do
|
20
|
+
GlobalWeather::Weather.new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_city_or_country_invalid_1
|
25
|
+
VCR.use_cassette('get_weather_invalid_1') do
|
26
|
+
assert_raise GlobalWeather::CityOrCountryInvalid do
|
27
|
+
GlobalWeather::Weather.new 'Blah', 'Berlin'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_city_or_country_invalid_2
|
33
|
+
VCR.use_cassette('get_weather_invalid_2') do
|
34
|
+
assert_raise GlobalWeather::CityOrCountryInvalid do
|
35
|
+
GlobalWeather::Weather.new 'Germany', 'Blah'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_city_or_country_invalid_3
|
41
|
+
VCR.use_cassette('get_weather_invalid_3') do
|
42
|
+
assert_raise GlobalWeather::CityOrCountryInvalid do
|
43
|
+
GlobalWeather::Weather.new 'Blah', 'Blah'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|