dhl_express_global 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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +35 -0
- data/Rakefile +2 -0
- data/dhl_express_global.gemspec +35 -0
- data/lib/dhl_express_global.rb +7 -0
- data/lib/dhl_express_global/credentials.rb +16 -0
- data/lib/dhl_express_global/helpers.rb +20 -0
- data/lib/dhl_express_global/label.rb +52 -0
- data/lib/dhl_express_global/request/base.rb +157 -0
- data/lib/dhl_express_global/request/delete.rb +77 -0
- data/lib/dhl_express_global/request/label.rb +30 -0
- data/lib/dhl_express_global/request/rate.rb +114 -0
- data/lib/dhl_express_global/request/shipment.rb +128 -0
- data/lib/dhl_express_global/shipment.rb +32 -0
- data/lib/dhl_express_global/version.rb +3 -0
- data/spec/config/dhl_credentials.example.yml +11 -0
- data/spec/lib/delete_spec.rb +31 -0
- data/spec/lib/label_spec.rb +77 -0
- data/spec/lib/rate_spec.rb +43 -0
- data/spec/lib/shipment_spec.rb +63 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/support/credentials.rb +17 -0
- data/spec/support/vcr.rb +14 -0
- metadata +245 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 25e0e751ae94b63b522255dbf5845e0fd29cf0da
|
4
|
+
data.tar.gz: 547a4abace99495614dfbfbdb37535975949eefb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8ca2bcad9315a1ec247841d40c2a2648199186fde8de11742e0627a4fd81ace783cebd20b408249bf3fc22c760537a0a01427f6120ec4f8c42608e3973b4ba44
|
7
|
+
data.tar.gz: c89c3c5df237c6d5235d922b880f43c0e3c74139048c48a604efe9ce0319ffe872e23dd8eb514d7eceb863f2232936cf247dc182a864558200ebba03c1b59800
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 JONBRWN
|
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,35 @@
|
|
1
|
+
# DhlExpressGlobal
|
2
|
+
|
3
|
+
Ruby API wrapper to interface with DHL Express Global
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'dhl_express_global'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install dhl_express_global
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Testing / Rspec
|
26
|
+
|
27
|
+
This gem is using Rspec for testing. As well VCR is implemented for testing the API. To make VCR work make sure to copy `spec/config/dhl_credentials.example.yml` to `spec/config/dhl_credentials.yml` and update the development credentails with actual account details.
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
1. Fork it ( https://github.com/[my-github-username]/dhl_express_global/fork )
|
32
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
33
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
34
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
35
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dhl_express_global/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "dhl_express_global"
|
8
|
+
spec.version = DhlExpressGlobal::VERSION
|
9
|
+
spec.authors = ["JONBRWN"]
|
10
|
+
spec.email = ["jonathanbrown.a@gmail.com"]
|
11
|
+
spec.summary = %q{Ruby wrapper for the DHL Express Global API}
|
12
|
+
spec.description = %q{Ruby wrapper for the DHL Express Global API}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
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_dependency 'httparty', '>= 0.8.3'
|
22
|
+
spec.add_dependency 'nokogiri', '>= 1.5.6'
|
23
|
+
spec.add_dependency 'builder'
|
24
|
+
spec.add_dependency 'xml-simple'
|
25
|
+
spec.add_dependency 'activesupport'
|
26
|
+
|
27
|
+
|
28
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
29
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
30
|
+
spec.add_development_dependency 'rspec', '~> 3.0.0'
|
31
|
+
spec.add_development_dependency 'vcr', '~> 2.00'
|
32
|
+
spec.add_development_dependency 'webmock', '~> 1.8.0'
|
33
|
+
spec.add_development_dependency 'pry'
|
34
|
+
spec.add_development_dependency 'debugger'
|
35
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'dhl_express_global/helpers'
|
2
|
+
|
3
|
+
module DhlExpressGlobal
|
4
|
+
class Credentials
|
5
|
+
include Helpers
|
6
|
+
attr_reader :username, :password, :account_number, :mode
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
requires!(options, :username, :password, :account_number, :mode)
|
10
|
+
@username = options[:username]
|
11
|
+
@password = options[:password]
|
12
|
+
@account_number = options[:account_number]
|
13
|
+
@mode = options[:mode]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module DhlExpressGlobal
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
private
|
5
|
+
# String or :symbol to CamelCase
|
6
|
+
def camelize(s)
|
7
|
+
# s.to_s.split('_').map { |e| e.capitalize }.join('')
|
8
|
+
s.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
9
|
+
end
|
10
|
+
|
11
|
+
# Helper method to validate required fields
|
12
|
+
def requires!(hash, *params)
|
13
|
+
params.each { |param| raise RateError, "Missing Required Parameter #{param}" if hash[param].nil? }
|
14
|
+
end
|
15
|
+
|
16
|
+
def underscorize(key) #:nodoc:
|
17
|
+
key.to_s.sub(/^(v[0-9]+|ns):/, "").gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module DhlExpressGlobal
|
5
|
+
class Label
|
6
|
+
attr_accessor :options, :image, :response_details
|
7
|
+
|
8
|
+
def initialize(label_details = {})
|
9
|
+
@response_details = label_details[:envelope][:body][:shipment_response]
|
10
|
+
@options = @response_details[:label_image]
|
11
|
+
@options[:tracking_number] = @response_details[:packages_result][:package_result][:tracking_number]
|
12
|
+
@options[:file_name] = label_details[:file_name]
|
13
|
+
@image = Base64.decode64(options[:graphic_image]) if has_image?
|
14
|
+
|
15
|
+
if file_name = @options[:file_name]
|
16
|
+
save(file_name, false)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def name
|
21
|
+
[tracking_number, format].join('.')
|
22
|
+
end
|
23
|
+
|
24
|
+
def format
|
25
|
+
options[:label_image_format]
|
26
|
+
end
|
27
|
+
|
28
|
+
def file_name
|
29
|
+
options[:file_name]
|
30
|
+
end
|
31
|
+
|
32
|
+
def tracking_number
|
33
|
+
options[:tracking_number]
|
34
|
+
end
|
35
|
+
|
36
|
+
def save(path, append_name = true)
|
37
|
+
return unless has_image?
|
38
|
+
|
39
|
+
full_path = Pathname.new(path)
|
40
|
+
full_path = full_path.join(name) if append_name
|
41
|
+
|
42
|
+
File.open(full_path, 'wb') do |f|
|
43
|
+
f.write(@image)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def has_image?
|
48
|
+
options.key?(:graphic_image)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'active_support/core_ext/hash'
|
4
|
+
require 'dhl_express_global/helpers'
|
5
|
+
|
6
|
+
module DhlExpressGlobal
|
7
|
+
module Request
|
8
|
+
class Base
|
9
|
+
include Helpers
|
10
|
+
include HTTParty
|
11
|
+
format :xml
|
12
|
+
|
13
|
+
attr_accessor :debug
|
14
|
+
|
15
|
+
default_options.update(verify: false)
|
16
|
+
|
17
|
+
# DHL Express Global Test URL
|
18
|
+
TEST_URL = 'https://wsb.dhl.com:443/sndpt/expressRateBook?WSDL'
|
19
|
+
|
20
|
+
# DHL Express Global Production URL
|
21
|
+
PRODUCTION_URL = ''
|
22
|
+
|
23
|
+
# SERVICE_CODES = []
|
24
|
+
|
25
|
+
# List of Payment Info codes
|
26
|
+
PAYMENT_INFO_CODES = ["CFR", "CIF", "CIP", "CPT", "DAF", "DDP", "DDU", "DAP", "DEQ", "DES", "EXW", "FAS", "FCA", "FOB"]
|
27
|
+
|
28
|
+
|
29
|
+
def initialize(credentials, options = {})
|
30
|
+
requires!(options, :shipper, :recipient, :packages)
|
31
|
+
@credentials = credentials
|
32
|
+
@shipper, @recipient, @packages, @service_type, @debug = options[:shipper], options[:recipient], options[:packages], options[:service_type], options[:debug]
|
33
|
+
@debug = ENV['DEBUG'] == 'true'
|
34
|
+
@shipping_options = options[:shipping_options] ||= {}
|
35
|
+
@payment_options = options[:payment_options] ||= {}
|
36
|
+
end
|
37
|
+
|
38
|
+
def process_request
|
39
|
+
raise NotImplementedError, 'Override #process_request in subclass'
|
40
|
+
end
|
41
|
+
|
42
|
+
def api_url
|
43
|
+
@credentials.mode == 'production' ? PRODUCTION_URL : TEST_URL
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_xml
|
47
|
+
raise NotImplementedError, 'Override #build_xml in subclass'
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse_response(response)
|
51
|
+
response = Hash.from_xml( response.parsed_response.gsub("\n", "") ) if response.parsed_response.is_a? String
|
52
|
+
response = sanitize_response_keys(response)
|
53
|
+
end
|
54
|
+
|
55
|
+
def sanitize_response_keys(response)
|
56
|
+
if response.is_a? Hash
|
57
|
+
response.inject({}) { |result, (key, value)| result[underscorize(key).to_sym] = sanitize_response_keys(value); result }
|
58
|
+
elsif response.is_a? Array
|
59
|
+
response.collect { |result| sanitize_response_keys(result) }
|
60
|
+
else
|
61
|
+
response
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_ws_authentication_header(xml)
|
66
|
+
xml[:soapenv].Header {
|
67
|
+
xml[:wsse].Security('soapenv:mustUnderstand' => "1" ,
|
68
|
+
'xmlns:wsse' => 'http://schemas.xmlsoap.org/ws/2003/06/secext',
|
69
|
+
'xmlns:wsu' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd') {
|
70
|
+
xml[:wsse].UsernameToken('wsu:Id' => 'UsernameToken') {
|
71
|
+
xml[:wsse].Username @credentials.username
|
72
|
+
xml[:wsse].Password(@credentials.password, 'Type' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText')
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_shipper(xml)
|
79
|
+
xml.Shipper {
|
80
|
+
xml.Contact {
|
81
|
+
xml.PersonName @shipper[:name]
|
82
|
+
xml.CompanyName @shipper[:company]
|
83
|
+
xml.PhoneNumber @shipper[:phone_number]
|
84
|
+
}
|
85
|
+
xml.Address {
|
86
|
+
add_address_street_lines(xml, @shipper[:address])
|
87
|
+
xml.City @shipper[:city]
|
88
|
+
xml.PostalCode @shipper[:postal_code]
|
89
|
+
xml.StateOrProvinceCode @shipper[:state] if @shipper[:state]
|
90
|
+
xml.CountryCode @shipper[:country_code]
|
91
|
+
}
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
def add_recipient(xml)
|
96
|
+
xml.Recipient {
|
97
|
+
xml.Contact {
|
98
|
+
xml.PersonName @recipient[:name]
|
99
|
+
xml.CompanyName @recipient[:company]
|
100
|
+
xml.PhoneNumber @recipient[:phone_number]
|
101
|
+
}
|
102
|
+
xml.Address {
|
103
|
+
add_address_street_lines(xml, @recipient[:address])
|
104
|
+
xml.City @recipient[:city]
|
105
|
+
xml.PostalCode @recipient[:postal_code]
|
106
|
+
xml.StateOrProvinceCode @recipient[:state] if @recipient[:state]
|
107
|
+
xml.CountryCode @recipient[:country_code]
|
108
|
+
}
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
def add_address_street_lines(xml, address)
|
113
|
+
Array(address).take(3).each_with_index do |address_line, i|
|
114
|
+
case i
|
115
|
+
when 0
|
116
|
+
xml.StreetLines address_line
|
117
|
+
when 1
|
118
|
+
xml.StreetLines2 address_line
|
119
|
+
when 2
|
120
|
+
xml.streetLines3 address_line
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def add_requested_packages(xml)
|
126
|
+
@packages.each_with_index do |package, i|
|
127
|
+
xml.RequestedPackages('number' => i + 1) {
|
128
|
+
xml.Weight package[:weight][:value]
|
129
|
+
xml.Dimensions {
|
130
|
+
xml.Length package[:dimensions][:length]
|
131
|
+
xml.Width package[:dimensions][:width]
|
132
|
+
xml.Height package[:dimensions][:height]
|
133
|
+
}
|
134
|
+
}
|
135
|
+
end
|
136
|
+
xml.ShipTimestamp (Time.now + 10*60).strftime("%Y-%m-%dT%H:%M:%SGMT%:z")
|
137
|
+
xml.UnitOfMeasurement @packages.first[:weight][:units] == 'KG' ? 'SI' : 'SU'
|
138
|
+
xml.Content @shipping_options[:package_contents] ||= "NON_DOCUMENTS"
|
139
|
+
end
|
140
|
+
|
141
|
+
# Add information for shipments
|
142
|
+
def add_shipment_info(xml)
|
143
|
+
xml.ShipmentInfo {
|
144
|
+
xml.DropOffType @shipping_options[:drop_off_type] ||= "REGULAR_PICKUP"
|
145
|
+
xml.ServiceType @shipping_options[:service_type]
|
146
|
+
xml.RequestValueAddedServices @shipping_options[:request_value_added_services] ||= "N"
|
147
|
+
xml.NextBusinessDay @shipping_options[:next_business_day] ||= "N"
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
151
|
+
def headers
|
152
|
+
{"Content-Type"=>"text/xml; charset=utf-8"}
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'dhl_express_global/request/base'
|
2
|
+
|
3
|
+
module DhlExpressGlobal
|
4
|
+
module Request
|
5
|
+
class Delete < Base
|
6
|
+
|
7
|
+
attr_reader :pickup_date, :pickup_country, :dispatch_confirmation_number, :requestor_name, :reason_code
|
8
|
+
|
9
|
+
def initialize(credentials, options = {})
|
10
|
+
requires!(options, :pickup_date, :pickup_country, :dispatch_confirmation_number, :requestor_name)
|
11
|
+
@credentials = credentials
|
12
|
+
@pickup_date, @pickup_country, @dispatch_confirmation_number, @requestor_name = options[:pickup_date], options[:pickup_country], options[:dispatch_confirmation_number], options[:requestor_name]
|
13
|
+
@reason_code = options[:reason_code] || "001"
|
14
|
+
end
|
15
|
+
|
16
|
+
def process_request
|
17
|
+
api_response = self.class.post api_url, :body => build_xml, :headers => headers
|
18
|
+
puts api_response if @debug
|
19
|
+
response = parse_response(api_response)
|
20
|
+
unless success?(response)
|
21
|
+
failure_response(response)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def build_xml
|
28
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
29
|
+
xml[:soapenv].Envelope( 'xmlns:soapenv' => "http://schemas.xmlsoap.org/soap/envelope/",
|
30
|
+
'xmlns:del' => "http://scxgxtt.phx-dc.dhl.com/euExpressRateBook/ShipmentMsgRequest") {
|
31
|
+
add_ws_authentication_header(xml)
|
32
|
+
xml[:soapenv].Body {
|
33
|
+
xml[:del].DeleteRequest {
|
34
|
+
xml.PickupDate {
|
35
|
+
xml.parent.namespace = nil
|
36
|
+
xml.text pickup_date
|
37
|
+
}
|
38
|
+
xml.PickupCountry {
|
39
|
+
xml.parent.namespace = nil
|
40
|
+
xml.text pickup_country
|
41
|
+
}
|
42
|
+
xml.DispatchConfirmationNumber {
|
43
|
+
xml.parent.namespace = nil
|
44
|
+
xml.text dispatch_confirmation_number
|
45
|
+
}
|
46
|
+
xml.RequestorName {
|
47
|
+
xml.parent.namespace = nil
|
48
|
+
xml.text requestor_name
|
49
|
+
}
|
50
|
+
xml.Reason {
|
51
|
+
xml.parent.namespace = nil
|
52
|
+
xml.text reason_code
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
end
|
58
|
+
builder.doc.root.to_xml
|
59
|
+
end
|
60
|
+
|
61
|
+
def failure_response(response)
|
62
|
+
error_message = response[:envelope][:body][:delete_response][:notification][:message]
|
63
|
+
raise RateError, error_message
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def success?(response)
|
68
|
+
response[:envelope][:body][:delete_response] && ( response[:envelope][:body][:delete_response][:notification][:code] == "0" )
|
69
|
+
end
|
70
|
+
|
71
|
+
def headers
|
72
|
+
super.merge!('SOAPAction' => 'euExpressRateBook_providerServices_ShipmentHandlingServices_Binder_deleteShipmentRequest')
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'dhl_express_global/label'
|
2
|
+
require 'dhl_express_global/request/base'
|
3
|
+
require 'dhl_express_global/request/shipment'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module DhlExpressGlobal
|
7
|
+
module Request
|
8
|
+
class Label < Shipment
|
9
|
+
|
10
|
+
def initialize(credentials, options = {})
|
11
|
+
super(credentials, options)
|
12
|
+
@filename = options[:filename]
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def success_response(response)
|
18
|
+
super
|
19
|
+
|
20
|
+
label_details = response.merge!({
|
21
|
+
:format => @label_specification[:image_type],
|
22
|
+
:file_name => @filename
|
23
|
+
})
|
24
|
+
|
25
|
+
DhlExpressGlobal::Label.new label_details
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'dhl_express_global/request/base'
|
2
|
+
|
3
|
+
module DhlExpressGlobal
|
4
|
+
module Request
|
5
|
+
class Rate < Base
|
6
|
+
|
7
|
+
def initialize(credentials, options={})
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def process_request
|
12
|
+
api_response = self.class.post api_url, :body => build_xml, :headers => headers
|
13
|
+
puts api_response if @debug
|
14
|
+
response = parse_response(api_response)
|
15
|
+
if success?(response)
|
16
|
+
success_response(response)
|
17
|
+
else
|
18
|
+
failure_response(response)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def build_xml
|
25
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
26
|
+
xml[:soapenv].Envelope( 'xmlns:soapenv' => "http://schemas.xmlsoap.org/soap/envelope/",
|
27
|
+
'xmlns:ship' => "http://scxgxtt.phx-dc.dhl.com/euExpressRateBook/RateMsgRequest") {
|
28
|
+
add_ws_authentication_header(xml)
|
29
|
+
xml[:soapenv].Body {
|
30
|
+
xml.RateRequest {
|
31
|
+
xml.parent.namespace = nil
|
32
|
+
xml.RequestedShipment {
|
33
|
+
xml.DropOffType @shipping_options[:drop_off_type] ||= "REGULAR_PICKUP"
|
34
|
+
xml.NextBusinessDay @shipping_options[:next_day] ||= "N"
|
35
|
+
xml.Ship {
|
36
|
+
add_shipper(xml)
|
37
|
+
add_recipient(xml)
|
38
|
+
}
|
39
|
+
add_requested_packages(xml)
|
40
|
+
xml.PaymentInfo @payment_info
|
41
|
+
xml.Account @credentials.account_number
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
end
|
47
|
+
builder.doc.root.to_xml
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_shipper(xml)
|
51
|
+
xml.Shipper {
|
52
|
+
add_address_street_lines(xml, @shipper[:address])
|
53
|
+
xml.City @shipper[:city]
|
54
|
+
xml.PostalCode @shipper[:postal_code]
|
55
|
+
xml.StateOrProvinceCode @shipper[:state] if @shipper[:state]
|
56
|
+
xml.CountryCode @shipper[:country_code]
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_recipient(xml)
|
61
|
+
xml.Recipient {
|
62
|
+
add_address_street_lines(xml, @recipient[:address])
|
63
|
+
xml.City @recipient[:city]
|
64
|
+
xml.PostalCode @recipient[:postal_code]
|
65
|
+
xml.StateOrProvinceCode @recipient[:state] if @recipient[:state]
|
66
|
+
xml.CountryCode @recipient[:country_code]
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def headers
|
71
|
+
super.merge!("SOAPAction" => "euExpressRateBook_providerServices_ShipmentHandlingServices_Binder_getRateRequest")
|
72
|
+
end
|
73
|
+
## <RateRequest>
|
74
|
+
## <ClientDetail></ClientDetail>
|
75
|
+
## <RequestedShipment>
|
76
|
+
## <DropOffType>REQUEST_COURIER</DropOffType>
|
77
|
+
## <Ship>
|
78
|
+
## <Shipper>
|
79
|
+
## <StreetLines>1-16-24, Minami-gyotoku</StreetLines>
|
80
|
+
## <City>Ichikawa-shi, Chiba</City>
|
81
|
+
## <PostalCode>272-0138</PostalCode>
|
82
|
+
## <CountryCode>JP</CountryCode>
|
83
|
+
## </Shipper>
|
84
|
+
## <Recipient>
|
85
|
+
## <StreetLines>63 RENMIN LU, QINGDAO SHI</StreetLines>
|
86
|
+
## <City>QINGDAO SHI</City>
|
87
|
+
## <PostalCode>266033</PostalCode>
|
88
|
+
## <CountryCode>CN</CountryCode>
|
89
|
+
## </Recipient>
|
90
|
+
## </Ship>
|
91
|
+
## <Packages>
|
92
|
+
## <RequestedPackages number="1">
|
93
|
+
## <Weight>
|
94
|
+
## <Value>2.0</Value>
|
95
|
+
## </Weight>
|
96
|
+
## <Dimensions>
|
97
|
+
## <Length>13</Length>
|
98
|
+
## <Width>12</Width>
|
99
|
+
## <Height>9</Height>
|
100
|
+
## </Dimensions>
|
101
|
+
## </RequestedPackages>
|
102
|
+
## </Packages>
|
103
|
+
## <ShipTimestamp>2010-11-26T12:00:00GMT-06:00</ShipTimestamp>
|
104
|
+
## <UnitOfMeasurement>SU</UnitOfMeasurement>
|
105
|
+
## <Content>NON_DOCUMENTS</Content>
|
106
|
+
## <DeclaredValue>0000000200</DeclaredValue>
|
107
|
+
## <DeclaredValueCurrecyCode>USD</DeclaredValueCurrecyCode>
|
108
|
+
## <PaymentInfo>DDP</PaymentInfo>
|
109
|
+
## <Account>000000000</Account>
|
110
|
+
## </RequestedShipment>
|
111
|
+
## </RateRequest>
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'dhl_express_global/request/base'
|
2
|
+
|
3
|
+
module DhlExpressGlobal
|
4
|
+
module Request
|
5
|
+
class Shipment < Base
|
6
|
+
|
7
|
+
def initialize(credentials, options={})
|
8
|
+
super
|
9
|
+
requires!(options, :service_type, :payment_info, :international_detail)
|
10
|
+
@international_detail = options[:international_detail]
|
11
|
+
requires!(@international_detail, :commodities)
|
12
|
+
@commodities = @international_detail[:commodities]
|
13
|
+
requires!(@commodities, :description, :customs_value)
|
14
|
+
@payment_info = options[:payment_info]
|
15
|
+
@label_specification = {
|
16
|
+
:image_type => 'PDF',
|
17
|
+
:label_template => 'ECOM26_84_001'
|
18
|
+
}
|
19
|
+
|
20
|
+
@label_specification.merge! options[:label_specification] if options[:label_specification]
|
21
|
+
end
|
22
|
+
|
23
|
+
def process_request
|
24
|
+
api_response = self.class.post api_url, :body => build_xml, :headers => headers
|
25
|
+
puts api_response if @debug
|
26
|
+
response = parse_response(api_response)
|
27
|
+
if success?(response)
|
28
|
+
success_response(response)
|
29
|
+
else
|
30
|
+
failure_response(response)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def build_xml
|
37
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
38
|
+
xml[:soapenv].Envelope( 'xmlns:soapenv' => "http://schemas.xmlsoap.org/soap/envelope/",
|
39
|
+
'xmlns:ship' => "http://scxgxtt.phx-dc.dhl.com/euExpressRateBook/ShipmentMsgRequest") {
|
40
|
+
add_ws_authentication_header(xml)
|
41
|
+
xml[:soapenv].Body {
|
42
|
+
xml[:ship].ShipmentRequest {
|
43
|
+
xml.RequestedShipment {
|
44
|
+
xml.parent.namespace = nil
|
45
|
+
add_shipment_info(xml)
|
46
|
+
xml.ShipTimestamp @shipping_options[:ship_timestamp] ||= (Time.now + 10*60).strftime("%Y-%m-%dT%H:%M:%SGMT%:z")
|
47
|
+
xml.PaymentInfo @payment_info
|
48
|
+
add_international_detail(xml)
|
49
|
+
xml.Ship {
|
50
|
+
add_shipper(xml)
|
51
|
+
add_recipient(xml)
|
52
|
+
}
|
53
|
+
add_requested_packages(xml)
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
58
|
+
end
|
59
|
+
builder.doc.root.to_xml
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_shipment_info(xml)
|
63
|
+
xml.ShipmentInfo {
|
64
|
+
xml.DropOffType @shipping_options[:drop_off_type] ||= "REGULAR_PICKUP"
|
65
|
+
xml.ServiceType @service_type
|
66
|
+
xml.Account @credentials.account_number
|
67
|
+
xml.Currency @shipping_options[:currency].upcase
|
68
|
+
xml.UnitOfMeasurement @shipping_options[:unit_of_measurement]
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def add_international_detail(xml)
|
73
|
+
xml.InternationalDetail {
|
74
|
+
xml.Commodities {
|
75
|
+
xml.NumberOfPieces @commodities[:number_of_pieces] if @commodities[:number_of_pieces]
|
76
|
+
xml.Description @commodities[:description]
|
77
|
+
xml.CountryOfManufacture @commodities[:country_of_manufacture] if @commodities[:country_of_manufacture]
|
78
|
+
xml.Quantity @commodities[:quantity] if @commodities[:quantity]
|
79
|
+
xml.UnitPrice @commodities[:unit_price] if @commodities[:unit_price]
|
80
|
+
xml.CustomsValue @commodities[:customs_value]
|
81
|
+
}
|
82
|
+
xml.Content @international_detail[:content] if @international_detail[:content]
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def add_requested_packages(xml)
|
87
|
+
xml.Packages {
|
88
|
+
@packages.each_with_index do |package, i|
|
89
|
+
xml.RequestedPackages('number' => i + 1) {
|
90
|
+
xml.Weight package[:weight][:value]
|
91
|
+
xml.Dimensions {
|
92
|
+
xml.Length package[:dimensions][:length]
|
93
|
+
xml.Width package[:dimensions][:width]
|
94
|
+
xml.Height package[:dimensions][:height]
|
95
|
+
}
|
96
|
+
xml.CustomerReferences @shipping_options[:customer_references] ||= "#{rand(10**10)}"
|
97
|
+
}
|
98
|
+
end
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def failure_response(response)
|
104
|
+
error_message = error_response(response)
|
105
|
+
raise RateError, error_message
|
106
|
+
end
|
107
|
+
|
108
|
+
def success_response(response)
|
109
|
+
@response_details = response[:envelope][:body][:shipment_response]
|
110
|
+
end
|
111
|
+
|
112
|
+
def success?(response)
|
113
|
+
response[:envelope][:body][:shipment_response] && !error_response(response)
|
114
|
+
end
|
115
|
+
|
116
|
+
def error_response(response)
|
117
|
+
response[:envelope][:body][:shipment_response][:notification][:message]
|
118
|
+
rescue
|
119
|
+
response[:envelope][:body][:shipment_response][:notification].first[:message]
|
120
|
+
end
|
121
|
+
|
122
|
+
def headers
|
123
|
+
super.merge!("SOAPAction" => "euExpressRateBook_providerServices_ShipmentHandlingServices_Binder_createShipmentRequest")
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'dhl_express_global/credentials'
|
2
|
+
require 'dhl_express_global/request/shipment'
|
3
|
+
require 'dhl_express_global/request/label'
|
4
|
+
require 'dhl_express_global/request/delete'
|
5
|
+
require 'dhl_express_global/request/rate'
|
6
|
+
|
7
|
+
module DhlExpressGlobal
|
8
|
+
|
9
|
+
class Shipment
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@credentials = Credentials.new(options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def label(options = {})
|
16
|
+
Request::Label.new(@credentials, options).process_request
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete(options = {})
|
20
|
+
Request::Delete.new(@credentials, options).process_request
|
21
|
+
end
|
22
|
+
|
23
|
+
def ship(options = {})
|
24
|
+
Request::Shipment.new(@credentials, options).process_request
|
25
|
+
end
|
26
|
+
|
27
|
+
def rate(options = {})
|
28
|
+
Request::Rate.new(@credentials, options).process_request
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module DhlExpressGlobal
|
5
|
+
describe Shipment do
|
6
|
+
let (:dhl) { Shipment.new(dhl_credentials) }
|
7
|
+
context '#delete' do
|
8
|
+
context 'delete shipment with dispatch confirmation number', :vcr do
|
9
|
+
let (:tomorrow) { DateTime.now.next_day.to_date.to_s }
|
10
|
+
let(:options) do
|
11
|
+
{ :pickup_date => tomorrow, :pickup_country => "IT",
|
12
|
+
:dispatch_confirmation_number => "FLR-804", :requestor_name => "Requestor", :reason_code => "001" }
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'deletes a shipment' do
|
16
|
+
expect{ dhl.delete(options) }.to_not raise_error
|
17
|
+
end
|
18
|
+
end
|
19
|
+
context 'context raise an error when the pickup date is invalid', :vcr do
|
20
|
+
let(:options) do
|
21
|
+
{ :pickup_date => "2014-07-21", :pickup_country => "IT",
|
22
|
+
:dispatch_confirmation_number => "FLR-804", :requestor_name => "Requestor", :reason_code => "001" }
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'raises an error' do
|
26
|
+
expect { dhl.delete(options) }.to raise_error(DhlExpressGlobal::RateError, "Cancellation of booking was not successful. Requested Pickup was not found.")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tmpdir'
|
3
|
+
|
4
|
+
module DhlExpressGlobal
|
5
|
+
describe Label do
|
6
|
+
describe 'ship service for label' do
|
7
|
+
let(:dhl) { Shipment.new(dhl_credentials) }
|
8
|
+
let(:shipper) do
|
9
|
+
{ :name => "Sender", :company => "Company", :phone_number => "555-555-5555", :address => "35 Great Jones St", :city => "New York", :state => "NY", :postal_code => "10012", :country_code => "US" }
|
10
|
+
end
|
11
|
+
let(:recipient) do
|
12
|
+
{ :name => "Recipient", :company => "Company", :phone_number => "555-555-5555", :address => "Bruehlstrasse, 10", :city => "Ettingen", :state => "CH", :postal_code => "4107", :country_code => "CH" }
|
13
|
+
end
|
14
|
+
let(:packages) do
|
15
|
+
[
|
16
|
+
{
|
17
|
+
:weight => { :units => "KG", :value => 2.86 },
|
18
|
+
:dimensions => { :length => 40, :width => 30, :height => 20, units: "CM" }
|
19
|
+
}
|
20
|
+
]
|
21
|
+
end
|
22
|
+
let(:commodities) do
|
23
|
+
{ :number_of_pieces => 1, :description => "Clothing", :customs_value => 300.00 }
|
24
|
+
end
|
25
|
+
let(:international_detail) do
|
26
|
+
{ :commodities => commodities, :content => "NON_DOCUMENTS" }
|
27
|
+
end
|
28
|
+
let(:filename) {
|
29
|
+
require 'tmpdir'
|
30
|
+
File.join(Dir.tmpdir, "label#{rand(15000)}.pdf")
|
31
|
+
}
|
32
|
+
let(:shipping_options) do
|
33
|
+
{ :currency => "EUR", :unit_of_measurement => "SI" }
|
34
|
+
end
|
35
|
+
let(:options) do
|
36
|
+
{
|
37
|
+
:shipper => shipper,
|
38
|
+
:recipient => recipient,
|
39
|
+
:packages => packages,
|
40
|
+
:payment_info => "DDP",
|
41
|
+
:international_detail => international_detail,
|
42
|
+
:shipping_options => shipping_options,
|
43
|
+
:service_type => "P",
|
44
|
+
:filename => filename
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'label', :vcr do
|
49
|
+
before do
|
50
|
+
@label = dhl.label(options)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should create a label' do
|
54
|
+
expect(File).to exist(filename)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should return a tracking number' do
|
58
|
+
expect(@label).to respond_to('tracking_number')
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should expose complete response' do
|
62
|
+
expect(@label).to respond_to('response_details')
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should expose the file_name' do
|
66
|
+
expect(@label).to respond_to('file_name')
|
67
|
+
end
|
68
|
+
|
69
|
+
after do
|
70
|
+
require 'fileutils'
|
71
|
+
FileUtils.rm_r(filename) if File.exists?(filename)
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dhl_express_global'
|
3
|
+
|
4
|
+
describe DhlExpressGlobal::Request::Rate do
|
5
|
+
describe 'ship service' do
|
6
|
+
let(:dhl) { DhlExpressGlobal::Shipment.new(dhl_credentials) }
|
7
|
+
let(:shipper) do
|
8
|
+
{ :name => "Sender", :company => "Company", :phone_number => "555-555-5555", :address => "35 Great Jones St", :city => "New York", :state => "NY", :postal_code => "10012", :country_code => "US" }
|
9
|
+
end
|
10
|
+
let(:recipient) do
|
11
|
+
{ :name => "Recipient", :company => "Company", :phone_number => "555-555-5555", :address => "Bruehlstrasse, 10", :city => "Ettingen", :state => "CH", :postal_code => "4107", :country_code => "CH" }
|
12
|
+
end
|
13
|
+
let(:packages) do
|
14
|
+
[
|
15
|
+
{
|
16
|
+
:weight => { :units => "KG", :value => 2.86 },
|
17
|
+
:dimensions => { :length => 40, :width => 30, :height => 20, units: "CM" }
|
18
|
+
}
|
19
|
+
]
|
20
|
+
end
|
21
|
+
|
22
|
+
context "international rate request", :vcr do
|
23
|
+
let(:options) do
|
24
|
+
{
|
25
|
+
shipper: shipper,
|
26
|
+
recipient: recipient,
|
27
|
+
packages: packages,
|
28
|
+
payment_info: "DDP"
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
it "succeeds" do
|
33
|
+
expect {
|
34
|
+
@rate = dhl.rate(options)
|
35
|
+
}.to_not raise_error
|
36
|
+
|
37
|
+
expect(@rate.class).to_not eq(DhlExpressGlobal::RateError)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dhl_express_global'
|
3
|
+
|
4
|
+
describe DhlExpressGlobal::Request::Shipment do
|
5
|
+
describe 'ship service' do
|
6
|
+
let(:dhl) { DhlExpressGlobal::Shipment.new(dhl_credentials) }
|
7
|
+
let(:shipper) do
|
8
|
+
{ :name => "Sender", :company => "Company", :phone_number => "555-555-5555", :address => "35 Great Jones St", :city => "New York", :state => "NY", :postal_code => "10012", :country_code => "US" }
|
9
|
+
end
|
10
|
+
let(:recipient) do
|
11
|
+
{ :name => "Recipient", :company => "Company", :phone_number => "555-555-5555", :address => "Bruehlstrasse, 10", :city => "Ettingen", :state => "CH", :postal_code => "4107", :country_code => "CH" }
|
12
|
+
end
|
13
|
+
let(:packages) do
|
14
|
+
[
|
15
|
+
{
|
16
|
+
:weight => { :units => "KG", :value => 2.86 },
|
17
|
+
:dimensions => { :length => 40, :width => 30, :height => 20, units: "CM" }
|
18
|
+
}
|
19
|
+
]
|
20
|
+
end
|
21
|
+
let(:commodities) do
|
22
|
+
{ :number_of_pieces => 1, :description => "Clothing", :customs_value => 300.00 }
|
23
|
+
end
|
24
|
+
let(:international_detail) do
|
25
|
+
{ :commodities => commodities, :content => "NON_DOCUMENTS" }
|
26
|
+
end
|
27
|
+
let(:filename) {
|
28
|
+
require 'tmpdir'
|
29
|
+
File.join(Dir.tmpdir, "label#{rand(15000)}.pdf")
|
30
|
+
}
|
31
|
+
let(:shipping_options) do
|
32
|
+
{ :currency => "EUR", :unit_of_measurement => "SI" }
|
33
|
+
end
|
34
|
+
|
35
|
+
context "international shipment", :vcr do
|
36
|
+
let(:options) do
|
37
|
+
{ :shipper => shipper, :recipient => recipient, :packages => packages, :service_type => "P", :payment_info => "DDP", :international_detail => international_detail, :shipping_options => shipping_options }
|
38
|
+
end
|
39
|
+
|
40
|
+
it "succeeds" do
|
41
|
+
expect {
|
42
|
+
@shipment = dhl.ship(options)
|
43
|
+
}.to_not raise_error
|
44
|
+
|
45
|
+
expect(@shipment.class).to_not eq(DhlExpressGlobal::RateError)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
context "without service_type specified", :vcr do
|
51
|
+
let(:options) do
|
52
|
+
{ :shipper => shipper, :recipient => recipient, :packages => packages, :payment_info => "DDP", :international_detail => international_detail, :shipping_options => shipping_options }
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'raises error' do
|
56
|
+
expect {
|
57
|
+
@shipment = dhl.ship(options)
|
58
|
+
}.to raise_error('Missing Required Parameter service_type')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'dhl_express_global'
|
3
|
+
require 'support/vcr'
|
4
|
+
require 'support/credentials'
|
5
|
+
|
6
|
+
RSpec.configure do |c|
|
7
|
+
c.filter_run_excluding :production unless dhl_production_credentials
|
8
|
+
c.expect_with :rspec do |expect_config|
|
9
|
+
expect_config.syntax = :expect
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
def dhl_credentials
|
2
|
+
@dhl_credentials ||= credentials["development"]
|
3
|
+
end
|
4
|
+
|
5
|
+
def dhl_production_credentials
|
6
|
+
@dhl_production_credentials ||= credentials["production"]
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def credentials
|
12
|
+
@credentials ||= YAML.load_file(credentials_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def credentials_path
|
16
|
+
File.expand_path("../../config/dhl_credentials.yml", __FILE__)
|
17
|
+
end
|
data/spec/support/vcr.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'vcr'
|
2
|
+
|
3
|
+
VCR.configure do |c|
|
4
|
+
c.cassette_library_dir = File.expand_path('../../vcr', __FILE__)
|
5
|
+
c.hook_into :webmock
|
6
|
+
end
|
7
|
+
|
8
|
+
RSpec.configure do |c|
|
9
|
+
c.include DhlExpressGlobal::Helpers
|
10
|
+
c.around(:each, :vcr) do |example|
|
11
|
+
name = underscorize(example.metadata[:full_description].split(/\s+/, 2).join("/")).gsub(/[^\w\/]+/, "_")
|
12
|
+
VCR.use_cassette(name) { example.call }
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dhl_express_global
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- JONBRWN
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httparty
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.8.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.8.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.5.6
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.5.6
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: builder
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: xml-simple
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: activesupport
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: bundler
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.7'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.7'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '10.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '10.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 3.0.0
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 3.0.0
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: vcr
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ~>
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '2.00'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ~>
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '2.00'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: webmock
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ~>
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 1.8.0
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ~>
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 1.8.0
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: pry
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: debugger
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - '>='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
description: Ruby wrapper for the DHL Express Global API
|
182
|
+
email:
|
183
|
+
- jonathanbrown.a@gmail.com
|
184
|
+
executables: []
|
185
|
+
extensions: []
|
186
|
+
extra_rdoc_files: []
|
187
|
+
files:
|
188
|
+
- .gitignore
|
189
|
+
- Gemfile
|
190
|
+
- LICENSE.txt
|
191
|
+
- README.md
|
192
|
+
- Rakefile
|
193
|
+
- dhl_express_global.gemspec
|
194
|
+
- lib/dhl_express_global.rb
|
195
|
+
- lib/dhl_express_global/credentials.rb
|
196
|
+
- lib/dhl_express_global/helpers.rb
|
197
|
+
- lib/dhl_express_global/label.rb
|
198
|
+
- lib/dhl_express_global/request/base.rb
|
199
|
+
- lib/dhl_express_global/request/delete.rb
|
200
|
+
- lib/dhl_express_global/request/label.rb
|
201
|
+
- lib/dhl_express_global/request/rate.rb
|
202
|
+
- lib/dhl_express_global/request/shipment.rb
|
203
|
+
- lib/dhl_express_global/shipment.rb
|
204
|
+
- lib/dhl_express_global/version.rb
|
205
|
+
- spec/config/dhl_credentials.example.yml
|
206
|
+
- spec/lib/delete_spec.rb
|
207
|
+
- spec/lib/label_spec.rb
|
208
|
+
- spec/lib/rate_spec.rb
|
209
|
+
- spec/lib/shipment_spec.rb
|
210
|
+
- spec/spec_helper.rb
|
211
|
+
- spec/support/credentials.rb
|
212
|
+
- spec/support/vcr.rb
|
213
|
+
homepage: ''
|
214
|
+
licenses:
|
215
|
+
- MIT
|
216
|
+
metadata: {}
|
217
|
+
post_install_message:
|
218
|
+
rdoc_options: []
|
219
|
+
require_paths:
|
220
|
+
- lib
|
221
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
222
|
+
requirements:
|
223
|
+
- - '>='
|
224
|
+
- !ruby/object:Gem::Version
|
225
|
+
version: '0'
|
226
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
227
|
+
requirements:
|
228
|
+
- - '>='
|
229
|
+
- !ruby/object:Gem::Version
|
230
|
+
version: '0'
|
231
|
+
requirements: []
|
232
|
+
rubyforge_project:
|
233
|
+
rubygems_version: 2.0.6
|
234
|
+
signing_key:
|
235
|
+
specification_version: 4
|
236
|
+
summary: Ruby wrapper for the DHL Express Global API
|
237
|
+
test_files:
|
238
|
+
- spec/config/dhl_credentials.example.yml
|
239
|
+
- spec/lib/delete_spec.rb
|
240
|
+
- spec/lib/label_spec.rb
|
241
|
+
- spec/lib/rate_spec.rb
|
242
|
+
- spec/lib/shipment_spec.rb
|
243
|
+
- spec/spec_helper.rb
|
244
|
+
- spec/support/credentials.rb
|
245
|
+
- spec/support/vcr.rb
|