global_weather 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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