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.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in global_weather.gemspec
4
+ gemspec
@@ -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.
@@ -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)
@@ -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,5 @@
1
+ further improvement:
2
+
3
+ One good improvement would be to CACHE SOME SOAP REQUESTS for lets say 1
4
+ hour o so.
5
+
@@ -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,3 @@
1
+ module GlobalWeather
2
+ VERSION = "0.0.1"
3
+ end
@@ -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