europe 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2e7e1e3027e935421fd4e15e7164cff183a1345deed66a5a2c764b35e63667a
4
- data.tar.gz: ffbaee22b0ec7bbccedb5a9ae2e269deed652007ed28e3bed0faf5b8818cda7b
3
+ metadata.gz: c273b711c76924d23e6bdd6f597501e79c4af5c1da3006f24696fe12c02aca99
4
+ data.tar.gz: e4721f22a5f68bb779dbc5b807eec55fb87fc5df7ddf0faa67134dcaf4cfbafc
5
5
  SHA512:
6
- metadata.gz: 559cecc3c404bf11e187c1ff2641ede7721bb66b34152be7d7981302561c1bd0593755eac3b8f964ede852b27f37794200cf5623d124d4c61f2ef1b1f3d79755
7
- data.tar.gz: 8b68f205e4b6bb3972ba988771c965948b6b8bb7e8295f40922c54d8741b6ec615cb70d2caa9e7491518797bd2c2c08b9482cbbdb1cb0195d64ce8502505ad81
6
+ metadata.gz: 1c93ed548787b6d5fd8589d439eb70481d8de30b49298b8ccfd65fd78976cde09cff637db0f512a76e0a93e4e6ffdcf65a35dc3bceaa3e0c86d941d3303ebbc3
7
+ data.tar.gz: 3168fe73cabbf2dd4c6bc65686659e2786f9295e7ba49dd960025f710adbcc979a7317aa361a4af86786fedf5ae4229ef7f880b7442197af851ebb66c1ab0456
data/.gitignore CHANGED
@@ -13,4 +13,5 @@
13
13
  *.a
14
14
  mkmf.log
15
15
  .DS_Store
16
- coverage
16
+ coverage
17
+ .byebug_history
@@ -2,9 +2,7 @@ language: ruby
2
2
  cache: bundler
3
3
 
4
4
  rvm:
5
- - 1.9.3
6
- - 2.0.0
7
- - 2.1.8
8
- - 2.2.0
9
- - 2.2.3
10
- - 2.2.4
5
+ - 2.2.2
6
+ - 2.4.0
7
+ - 2.5.0
8
+ - 2.5.1
@@ -2,6 +2,15 @@
2
2
  All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
+ # 0.0.9
6
+ - Removed all external depdencies, only Ruby's standard library is used
7
+ - Added Postal code format validator
8
+ - Used different source for exchange rates which returns more currencies
9
+
10
+ # 0.0.8
11
+ - Changed URL for retrieving VAT rates. The old one was no longer valid
12
+ - Skipped Eurostat tests as URL changed. Eurostat use is still expirimental
13
+
5
14
  # 0.0.7
6
15
  - Fixed currencies for Estonia, Lithuania and Sweden
7
16
  - Added eurozone call to Countries which returns all countries with EUR currency
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015 Vincent
3
+ Copyright (c) 2018 Vincent
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
-
data/README.md CHANGED
@@ -8,6 +8,7 @@ This gem provides EU governmental data, extracted from various EU / EC websites.
8
8
  - [Usage](#usage)
9
9
  - [Validating VAT numbers](#validating-vat-numbers)
10
10
  - [Validate VAT number format](#validate-vat-number-format)
11
+ - [Validate Postal code format](#validate-postal-code-format)
11
12
  - [Retrieving VAT rates for each EC/EU member](#retrieving-vat-rates-for-each-eceu-member)
12
13
  - [Retrieving currency exchange rates](#retrieving-currency-exchange-rates)
13
14
  - [Retrieving currency information](#retrieving-currency-information)
@@ -64,6 +65,15 @@ Response
64
65
  => true
65
66
  ```
66
67
 
68
+ ### Validate Postal Code format
69
+ Call
70
+ ```ruby
71
+ Europe::Postal.validate('NL', '1000 AP')
72
+ ```
73
+ Response
74
+ ```ruby
75
+ => true
76
+ ```
67
77
 
68
78
  ### Retrieving VAT rates for each EC/EU member
69
79
  Call
@@ -22,9 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
23
  spec.require_paths = ['lib']
24
24
 
25
- spec.add_dependency 'nokogiri'
26
- spec.add_dependency 'savon'
27
-
25
+ spec.add_development_dependency 'byebug'
28
26
  spec.add_development_dependency 'bundler'
29
27
  spec.add_development_dependency 'rake'
30
28
  spec.add_development_dependency 'minitest'
@@ -1,6 +1,7 @@
1
1
  require 'europe/version'
2
2
  require 'europe/countries/countries'
3
3
  require 'europe/currency/currency'
4
+ require 'europe/postal/postal'
4
5
  require 'europe/vat/vat'
5
6
  require 'europe/eurostat/eurostat'
6
7
 
@@ -1,5 +1,6 @@
1
- require 'open-uri'
2
- require 'nokogiri'
1
+ require 'rexml/document'
2
+ require 'net/http'
3
+ require 'date'
3
4
 
4
5
  # Europe Gem
5
6
  module Europe
@@ -7,18 +8,25 @@ module Europe
7
8
  module Currency
8
9
  # exchange rates
9
10
  module ExchangeRates
10
- EXCHANGE_URL = 'http://www.ecb.europa.eu/stats/' \
11
- 'eurofxref/eurofxref-daily.xml'.freeze
11
+ EXCHANGE_URL = 'https://www.floatrates.com/daily/eur.xml'.freeze
12
+
12
13
  def self.retrieve
13
- extract_rates(Nokogiri::XML(open(EXCHANGE_URL)))
14
+ resp = Net::HTTP.get_response(URI.parse(EXCHANGE_URL))
15
+ resp.code.to_i == 200 ? extract_rates(resp.body) : :failed
14
16
  end
15
17
 
16
18
  def self.extract_rates(doc)
17
- rates = { date: Date.parse(doc.css('Cube Cube').first['time']),
19
+ xml = REXML::Document.new(doc)
20
+
21
+ rates = { date: Date.parse(xml.elements.first.elements[7].text),
18
22
  rates: {} }
19
- doc.css('Cube Cube Cube').each do |rate|
20
- rates[:rates][rate.xpath('@currency').text.to_sym] =
21
- rate.xpath('@rate').text.to_f
23
+
24
+ filter_rates(xml, rates)
25
+ end
26
+
27
+ def self.filter_rates(xml, rates)
28
+ xml.elements.each('channel/item') do |item|
29
+ rates[:rates][item[13].text.to_sym] = item[17].text.delete(',').to_f
22
30
  end
23
31
  rates
24
32
  end
@@ -0,0 +1,54 @@
1
+ # Europe Gem
2
+ module Europe
3
+ # VAT
4
+ module Postal
5
+ # rubocop:disable LineLength
6
+ POSTAL_REGEX = {
7
+ AT: /^\d{4}$/,
8
+ BE: /^\d{4}$/,
9
+ BG: /^\d{4}$/,
10
+ CY: /^\d{4}$/,
11
+ CZ: /^\d{3} \d{2}$/,
12
+ DE: /^\d{5}$/,
13
+ DK: /^\d{4}$/,
14
+ EE: /^\d{5}$/,
15
+ EL: /^\d{3} \d{2}$/,
16
+ ES: /^\d{5}$/,
17
+ FI: /^\d{5}$/,
18
+ FR: /^\d{3} \d{2}$/,
19
+ GB: /GIR[ ]?0AA|((AB|AL|B|BA|BB|BD|BH|BL|BN|BR|BS|BT|CA|CB|CF|CH|CM|CO|CR|CT|CV|CW|DA|DD|DE|DG|DH|DL|DN|DT|DY|E|EC|EH|EN|EX|FK|FY|G|GL|GY|GU|HA|HD|HG|HP|HR|HS|HU|HX|IG|IM|IP|IV|JE|KA|KT|KW|KY|L|LA|LD|LE|LL|LN|LS|LU|M|ME|MK|ML|N|NE|NG|NN|NP|NR|NW|OL|OX|PA|PE|PH|PL|PO|PR|RG|RH|RM|S|SA|SE|SG|SK|SL|SM|SN|SO|SP|SR|SS|ST|SW|SY|TA|TD|TF|TN|TQ|TR|TS|TW|UB|W|WA|WC|WD|WF|WN|WR|WS|WV|YO|ZE)(\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}))|BFPO[ ]?\d{1,4}/,
20
+ HR: /^\d{5}$/,
21
+ HU: /^\d{4}$/,
22
+ IE: /^IE\d[A-Z0-9\+\*|\d]\d{5}([A-Z]|WI)$/,
23
+ IT: /^\d{5}$/,
24
+ LT: /^LT\-\d{5}$/,
25
+ LU: /^\d{4}$/,
26
+ LV: /^LV\-\d{4}$/,
27
+ MT: /^\w{3} \d{2,4}$/,
28
+ NL: /^\d{4}[ ]?[A-Z]{2}$/,
29
+ PL: /^\d{2}-\d{3}$/,
30
+ PT: /^\d{4}([\-]\d{3})?$/,
31
+ RO: /^\d{6}$/,
32
+ SE: /^\d{3}[ ]?\d{2}$/,
33
+ SI: /^\d{4}$/,
34
+ SK: /^\d{3}[ ]?\d{2}$/
35
+ }.freeze
36
+ # rubocop:enable LineLength
37
+
38
+ def self.validate(country_code, postal_code)
39
+ return false unless POSTAL_REGEX.keys.include?(country_code.to_sym)
40
+ match_postal_code(postal_code, country_code)
41
+ end
42
+
43
+ def self.match_postal_code(postal_code, country_code)
44
+ if POSTAL_REGEX[country_code.to_sym].is_a?(Array)
45
+ POSTAL_REGEX[country_code.to_sym].each do |regex|
46
+ return true if regex.match(postal_code)
47
+ end
48
+ elsif POSTAL_REGEX[country_code.to_sym].match(postal_code)
49
+ return true
50
+ end
51
+ false
52
+ end
53
+ end
54
+ end
@@ -1,3 +1,5 @@
1
+ require 'rexml/document'
2
+
1
3
  module Europe
2
4
  module Vat
3
5
  # Rates
@@ -12,14 +14,23 @@ module Europe
12
14
 
13
15
  def self.extract_rates(resp)
14
16
  rates = {}
15
- doc = Nokogiri::XML(resp)
16
- doc.css('body tbody tr')[2..-1].each do |data|
17
- key = data.css('td').children[1].text.to_sym
18
- rates[key] = data.css('td').children[2].text.to_f
17
+
18
+ data = resp.scan(%r{\<tbody\>(.*)\<\/tbody\>}m).first.first.strip
19
+ xml = REXML::Document.new("<root>#{data}</root>")
20
+ xml.first.elements.each('tr') do |result|
21
+ next if result[3].nil?
22
+ rates = filter_rate(result, rates)
19
23
  end
20
24
  rates
21
25
  end
22
26
 
27
+ def self.filter_rate(result, rates)
28
+ country = result[3].text
29
+ rate = result[5].text
30
+ rates[country.to_sym] = rate.to_f if country
31
+ rates
32
+ end
33
+
23
34
  def self.fetch_rates
24
35
  resp = Net::HTTP.get_response(URI.parse(RATES_URL))
25
36
  resp.code.to_i == 200 ? resp.body : :failed
@@ -1,22 +1,64 @@
1
1
  require 'europe/vat/rates'
2
2
  require 'europe/vat/format'
3
- require 'savon'
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'rexml/document'
6
+ require 'date'
4
7
 
5
8
  # Europe Gem
6
9
  module Europe
7
10
  # VAT
8
11
  module Vat
9
- WSDL = 'http://ec.europa.eu/taxation_customs/' \
10
- 'vies/checkVatService.wsdl'.freeze
11
- NS = 'urn:ec.europa.eu:taxud:vies:services:checkVat:types'.freeze
12
+ WSDL = 'http://ec.europa.eu/taxation_customs/vies/' \
13
+ 'services/checkVatService'.freeze
14
+ HEADERS = {
15
+ 'Content-Type' => 'text/xml;charset=UTF-8',
16
+ 'SOAPAction' => ''
17
+ }.freeze
18
+
19
+ BODY = <<-XML.freeze
20
+ <soapenv:Envelope
21
+ xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
22
+ xmlns:urn="urn:ec.europa.eu:taxud:vies:services:checkVat:types">
23
+ <soapenv:Header/>
24
+ <soapenv:Body>
25
+ <urn:checkVat>
26
+ <urn:countryCode>{COUNTRY_CODE}</urn:countryCode>
27
+ <urn:vatNumber>{NUMBER}</urn:vatNumber>
28
+ </urn:checkVat>
29
+ </soapenv:Body>
30
+ </soapenv:Envelope>
31
+ XML
32
+
12
33
  def self.validate(number)
13
34
  response = send_request(number[0..1], number[2..-1])
14
- return :failed unless response.success?
15
- response.body[:check_vat_response].tap { |x| x.delete(:@xmlns) }
16
- rescue Savon::HTTPError, Savon::SOAPFault
17
- return :failed
18
- rescue Timeout::Error
35
+ return :failed unless response.is_a? Net::HTTPSuccess
36
+ setup_response(response)
37
+ rescue Net::OpenTimeout
19
38
  return :timeout
39
+ rescue Net::HTTPServerError
40
+ return :server_error
41
+ end
42
+
43
+ def self.setup_response(response)
44
+ body = response_xml(response)
45
+ {
46
+ valid: extract_data(body, 4) == 'true',
47
+ country_code: extract_data(body, 1),
48
+ vat_number: extract_data(body, 2),
49
+ request_date: Date.parse(extract_data(body, 3)),
50
+ name: extract_data(body, 5),
51
+ address: extract_data(body, 6)
52
+ }
53
+ end
54
+
55
+ def self.response_xml(response)
56
+ xml = REXML::Document.new(response.body)
57
+ xml.first.elements.first.elements.first.elements
58
+ end
59
+
60
+ def self.extract_data(body, position)
61
+ body[position].text
20
62
  end
21
63
 
22
64
  def self.charge_vat?(origin_country, number)
@@ -30,12 +72,18 @@ module Europe
30
72
  end
31
73
 
32
74
  def self.send_request(country_code, number)
33
- client = Savon.client(wsdl: WSDL, namespace: NS,
34
- open_timeout: 10, read_timeout: 10)
75
+ uri = URI.parse(WSDL)
76
+
77
+ body = BODY.dup.gsub('{COUNTRY_CODE}', country_code)
78
+ body = body.gsub('{NUMBER}', number)
79
+
80
+ # Create the HTTP objects
81
+ http = Net::HTTP.new(uri.host, uri.port)
82
+ request = Net::HTTP::Post.new(uri.request_uri, HEADERS)
83
+ request.body = body
35
84
 
36
- client.call(:check_vat,
37
- message: { countryCode: country_code,
38
- vatNumber: number.delete(' ') })
85
+ # Send the request
86
+ http.request(request)
39
87
  end
40
88
  end
41
89
  end
@@ -1,4 +1,4 @@
1
1
  # Europe version
2
2
  module Europe
3
- VERSION = '0.0.8'.freeze
3
+ VERSION = '0.0.9'.freeze
4
4
  end
@@ -0,0 +1,30 @@
1
+ require 'test_helper'
2
+
3
+ module Europe
4
+ module Postal
5
+ # FormatTest
6
+ class FormatTest < Minitest::Test
7
+ def test_several_postal_codes
8
+ # Netherlands
9
+ assert Europe::Postal.validate('NL', '1000 AP')
10
+
11
+ # German
12
+ assert Europe::Postal.validate('DE', '02782')
13
+
14
+ # UK
15
+ assert Europe::Postal.validate('GB', 'SW1W 0NY')
16
+ end
17
+
18
+ def test_fail_several_postal_codes
19
+ # Netherlands
20
+ refute Europe::Postal.validate('NL', '1000x AP')
21
+
22
+ # German
23
+ refute Europe::Postal.validate('DE', 'BW02782')
24
+
25
+ # UK
26
+ refute Europe::Postal.validate('GB', 'XSW1Wxx 0NY')
27
+ end
28
+ end
29
+ end
30
+ end
@@ -34,11 +34,10 @@ module Europe
34
34
 
35
35
  def test_failed_request_to_soap_service
36
36
  WebMock.enable!
37
- stub_request(:any, 'http://ec.europa.eu/taxation_customs' \
38
- '/vies/checkVatService.wsdl').to_timeout
37
+ stub_request(:any, 'http://ec.europa.eu/taxation_customs/vies/services/checkVatService').to_timeout
39
38
  Europe::Vat.validate('DE115235681')
40
39
 
41
- stub_request(:get, 'http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl')
40
+ stub_request(:get, 'http://ec.europa.eu/taxation_customs/vies/services/checkVatService')
42
41
  .with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' })
43
42
  .to_return(status: 421, body: '')
44
43
  Europe::Vat.validate('DE115235681')
metadata CHANGED
@@ -1,37 +1,23 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: europe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - VvanGemert
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-26 00:00:00.000000000 Z
11
+ date: 2018-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: nokogiri
14
+ name: byebug
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: savon
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
20
+ type: :development
35
21
  prerelease: false
36
22
  version_requirements: !ruby/object:Gem::Requirement
37
23
  requirements:
@@ -166,7 +152,6 @@ files:
166
152
  - CHANGELOG.md
167
153
  - Gemfile
168
154
  - LICENSE
169
- - LICENSE.txt
170
155
  - README.md
171
156
  - Rakefile
172
157
  - europe.gemspec
@@ -176,6 +161,7 @@ files:
176
161
  - lib/europe/currency/currency.rb
177
162
  - lib/europe/currency/exchange_rates.rb
178
163
  - lib/europe/eurostat/eurostat.rb
164
+ - lib/europe/postal/postal.rb
179
165
  - lib/europe/rake_task.rb
180
166
  - lib/europe/tasks/eurostat.rake
181
167
  - lib/europe/vat/format.rb
@@ -186,6 +172,7 @@ files:
186
172
  - test/europe/currency/currency_test.rb
187
173
  - test/europe/currency/exchange_rates_test.rb
188
174
  - test/europe/eurostat/eurostat_test.rb
175
+ - test/europe/postal/format_test.rb
189
176
  - test/europe/vat/format_test.rb
190
177
  - test/europe/vat/rates_test.rb
191
178
  - test/europe/vat/validation_test.rb
@@ -219,6 +206,7 @@ test_files:
219
206
  - test/europe/currency/currency_test.rb
220
207
  - test/europe/currency/exchange_rates_test.rb
221
208
  - test/europe/eurostat/eurostat_test.rb
209
+ - test/europe/postal/format_test.rb
222
210
  - test/europe/vat/format_test.rb
223
211
  - test/europe/vat/rates_test.rb
224
212
  - test/europe/vat/validation_test.rb
@@ -1,22 +0,0 @@
1
- Copyright (c) 2015 VvanGemert
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.