dhl-get_quote 0.4.25

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
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
18
+ .rvmrc
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.rvmrc.example ADDED
@@ -0,0 +1 @@
1
+ rvm --create use ruby-1.9.3@dhl-get_quote
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - rbx-18mode
6
+ - rbx-19mode
7
+ - 1.8.7
8
+ - ree
9
+ - jruby-19mode
10
+ - jruby-18mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dhl-get_quote.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Deseret Book
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,357 @@
1
+ [![Build Status](https://travis-ci.org/deseretbook/dhl-get_quote.png)](https://travis-ci.org/deseretbook/dhl-get_quote)
2
+
3
+ # Dhl::GetQuote
4
+
5
+ Get shipping quotes from DHL's XML-PI Service.
6
+
7
+ Use of the XML-PI Service requires you to have Site ID and Password from DHL. You can sign up here: https://myaccount.dhl.com/MyAccount/jsp/TermsAndConditionsIndex.htm
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'dhl-get_quote'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install dhl-get_quote
22
+
23
+ ## Basic Usage
24
+
25
+ ```ruby
26
+ require 'dhl-get_quote'
27
+
28
+ r = Dhl::GetQuote::Request.new(
29
+ :site_id => "SiteIdHere",
30
+ :password => "p4ssw0rd",
31
+ :test_mode => true # changes the url being hit
32
+ )
33
+
34
+ r.kilograms!
35
+ r.centimeters!
36
+
37
+ r.add_special_service("DD")
38
+
39
+ r.to('CA', "T1H 0A1")
40
+ r.from('US', 84010)
41
+
42
+ r.pieces << Dhl::GetQuote::Piece.new(
43
+ :height => 20,
44
+ :weight => 20,
45
+ :width => 20,
46
+ :depth => 19
47
+ )
48
+
49
+ resp = r.post
50
+ if response.error?
51
+ raise "There was an error: #{resp.raw_xml}"
52
+ else
53
+ puts "Your cost to ship will be: #{resp.total_amount} in #{resp.currency_code}."
54
+ end
55
+ ```
56
+
57
+ ---
58
+
59
+ ### Dhl::GetQuote::Request
60
+
61
+ #### Making a new request
62
+
63
+ This is where the magic starts. It accepts a hash that, at minimum, requires :site\_id and :password. Optionally, :test\_mode may be passed in to tell the gem to use the XML-PI test URL. The default is to *not* use test mode and to hit the production URL.
64
+
65
+ ```ruby
66
+ request = Dhl::GetQuote::Request.new(
67
+ :site_id => "SiteIdHere",
68
+ :password => "p4ssw0rd",
69
+ :test_mode => false
70
+ )
71
+ ```
72
+
73
+ *NOTE*: You can also set default beforehand in, for example, an initializer. For more information on this, please see the section "Initializers with Dhl::GetQuote"
74
+
75
+ #### Package Source and Destination
76
+
77
+ To set the source and destination, use the #to() and #from() methods:
78
+
79
+ #to(_country_code_, _postal_code_), #from(_country_code_, _postal_code_)
80
+
81
+ The country code must be the two-letter capitalized ISO country code. The postal code will be cast in to a string.
82
+
83
+ Example:
84
+
85
+ ```ruby
86
+ request.from('US', 84111)
87
+ request.to('CA', 'T1H 0A1')
88
+ ```
89
+
90
+ #### Measurement Units
91
+
92
+ DHL can accept weights and measures in both Metric and US Customary units. Weights are given in either pounds or kilograms, dimensions in either inches or centimeters. This gem defaults to use metric measurements.
93
+
94
+ To set to US Customary, use:
95
+
96
+ ```ruby
97
+ request.inches! # set dimensions to inches
98
+ request.pounds! # set weight to pounds
99
+ ```
100
+
101
+ To set back to Metric, use
102
+
103
+ ```ruby
104
+ request.centimeters! # set dimensions to centimeters
105
+ request.centimetres! # alternate spelling
106
+
107
+ request.kilograms! # set weight to kilograms
108
+ request.kilogrammes! # alternate spelling
109
+ ```
110
+
111
+ To query what measurement system the object is currently using, use the following boolean calls:
112
+
113
+ ```ruby
114
+ request.inches?
115
+ request.centimeters? # or request.centimetres?
116
+
117
+ request.pounds?
118
+ request.kilograms? # or request.kilogrammes?
119
+ ```
120
+
121
+ You can also get the value directly:
122
+
123
+ ```ruby
124
+ request.dimensions_unit # will return either "CM" or "IN"
125
+ request.weight_unit # will return either "KG" or "LB"
126
+ ```
127
+
128
+ #### Setting Duty
129
+
130
+ You can toggle whether or not a shipment is dutiable with the #dutiable!() and #not_dutiable!() methods.
131
+
132
+ ```ruby
133
+ request.dutiable!
134
+ request.not_dutiable!
135
+ ```
136
+
137
+ You can absolutely set the dutiable stage with the #dutiable() method:
138
+
139
+ ```ruby
140
+ request.dutiable(true)
141
+ request.dutiable(false)
142
+ ```
143
+
144
+ You can query the current state with #dutiable?:
145
+
146
+ ```ruby
147
+ request.dutiable? # returns true or false
148
+ ```
149
+
150
+ The default is for the request is "not dutiable".
151
+
152
+ #### Shipment Services
153
+
154
+ Shipment services (speed, features, etc) can be added, listed and removed.
155
+
156
+ To add a special service, call #add_special_service as pass in DHL-standard code for the service:
157
+
158
+ ```ruby
159
+ request.add_special_service("D")
160
+ ```
161
+
162
+ To list all services currently added to a request, use #special_services:
163
+
164
+ ```ruby
165
+ request.special_services
166
+ ```
167
+
168
+ To remove a special service, use #remove_special_service and pass the code:
169
+
170
+ ```ruby
171
+ request.remove_special_service("D")
172
+ ```
173
+
174
+ The interface will not allow the same special service code to be added twice, it will be silently ignored if you try.
175
+
176
+
177
+ #### Adding items to the request
178
+
179
+ To add items to the shipping quote request, generate a new Dhl::GetQuote::Piece instance and append it to #pieces:
180
+
181
+ ```ruby
182
+ # minimal
183
+ request.pieces << Dhl::GetQuote::Piece.new( :weight => 20 )
184
+
185
+ # more details
186
+ request.pieces << Dhl::GetQuote::Piece.new(
187
+ :weight => 20, :height => 20, :width => 20, :depth => 19
188
+ )
189
+ ```
190
+
191
+ Dhl::GetQuote::Piece requires *at least* :weight to be specified, and it must be a nonzero integer. Optionally, you can provide :width, :depth and :height. The measurement options must all be added at once and cannot be added individually. They must be integers.
192
+
193
+ #### Posting to DHL
194
+
195
+ Once the request is prepared, call #post() to post the request to the DHL XML-PI. This will return a Dhl::GetQuote::Response object.
196
+
197
+ ```ruby
198
+ response = request.post
199
+ response.class == Dhl::GetQuote::Response # true
200
+ ```
201
+
202
+ ---
203
+
204
+ ### Dhl::GetQuote::Response
205
+
206
+ Once a post is sent to DHL, this gem will interpret the XML returned and create a Dhl::GetQuote::Response object.
207
+
208
+ #### Checking for errors
209
+
210
+ To check for errors in the response (both local and upstream), query the #error? and #error methods
211
+
212
+ ```ruby
213
+ response.error? # true
214
+ response.error
215
+ # => < Dhl::GetQuote::Upstream::ValidationFailureError: your site id is not correct blah blah >
216
+ ```
217
+
218
+ #### Getting costs
219
+
220
+ The response object exposes the following values:
221
+
222
+ * currency_code
223
+ * currency_role_type_code
224
+ * weight_charge
225
+ * total_amount
226
+ * total_tax_amount
227
+ * weight_charge_tax
228
+
229
+ To find the total change:
230
+
231
+ ```ruby
232
+ puts "Your cost to ship will be: #{response.total_amount} in #{response.currency_code}."
233
+ # Your cost to ship will be: 337.360 in USD.
234
+ ```
235
+
236
+ DHL can return the cost in currency unit based on sender location or reciever location. It is unlikely this will be used much, but you can change the currency by calling #load_costs with the CurrencyRoleTypeCode:
237
+
238
+ ```ruby
239
+ response.load_costs('PULCL')
240
+ puts "Your cost to ship will be: #{response.total_amount} in #{response.currency_code}."
241
+ # Your cost to ship will be: 341.360 in CAD.
242
+ ```
243
+
244
+ CurrencyRoleTypeCodes that can be used are:
245
+
246
+ * BILLCU – Billing currency
247
+ * PULCL – Country of pickup local currency
248
+ * INVCU – Invoice currency
249
+ * BASEC – Base currency
250
+
251
+ #### Accessing the raw response
252
+
253
+ If you need data from the response that is not exposed by this gem, you can access both the raw xml and parsed xml directly:
254
+
255
+ ```ruby
256
+ response.raw_xml # raw xml string as returned by DHL
257
+ response.parsed_xml # xml parsed in to a Hash for easy traversal
258
+ ```
259
+
260
+ \#parsed\_xml() is not always available in the case of errors. #raw\_xml() is, except in cases of network transport errors.
261
+
262
+ #### Accessing offered services
263
+
264
+ In cases where you have either sent many special service code, or you are evaluating all available services (via the 'OSINFO' special service code), you can obtain a list of all the services with the #offred_services() and #all_services() methods. Both methods return an array of Dhl::GetQuote::MarketService objects.
265
+
266
+ The #offered_services() method returns only those services intended to be shown to the end user an optional services (XCH) they can apply. These would be services with either 'TransInd' or 'MrkSrvInd' set to 'Y'.
267
+
268
+ The #all_services() methods returns all associated services, including everything in #offered_services and also including possible fees (FEE) and surcharges (SCH).
269
+
270
+ If using 'OSINFO' to obtain all offered services, you pass the value of #code() in to Request#add_special_service() to apply this services to another request:
271
+
272
+ ```ruby
273
+ # assume we already did an 'OSINFO' request.
274
+ new_request.add_special_service(response.offered_services.first.code)
275
+ ```
276
+
277
+ ---
278
+
279
+ ### Dhl::GetQuote::MarketService
280
+
281
+ Instances of this object are returned from Response#offered_services() and Response#all_services().
282
+
283
+ #### Methods for parameters
284
+
285
+ All XML parameters in the response will be added as methods to this object. They may vary but generally include:
286
+
287
+ * #local\_product\_code() - code for user-offered services (signature, tracking, overnight, etc)
288
+ * #local\_service_type() code for non-offered special services (fees, surcharges, etc)
289
+ * #local\_service\_type\_name() - Name for a non-offered service
290
+ * #local\_product\_name() - Name for a user-offered service
291
+ * #mrk\_srv\_ind() - Should this be offered to the user?
292
+ * #trans\_ind() - Should this be shown to every user regardless of shipping options?
293
+
294
+ #### Getting the code for a service
295
+
296
+ The #code() method will return the code for a given service. It will work on both non-offered and user-offered services, it queries both LocalProductCode and LocalServiceType.
297
+
298
+ ```ruby
299
+ market_service.code # "D"
300
+ ```
301
+
302
+ This code can be passed in to Request#add_special_service()
303
+
304
+ #### Getting the name for a service
305
+
306
+ The #name() method will return the name for a given service. It will work on both non-offered and user-offered services, it queries both LocalProductName and LocalServiceTypeName.
307
+
308
+ ```ruby
309
+ market_service.name # "EXPRESS WORLDWIDE DOC"
310
+ ```
311
+
312
+ ---
313
+
314
+ ### Initializers with Dhl::GetQuote
315
+
316
+ If you don't want to have to pass email, password, weight setting, etc, every time you build a new request object you can set these defaults beforehand. This works well in cases where you want to put setting in something like a Rails initializer.
317
+
318
+ To do this, call Dhl::GetQuote::configure and pass a block:
319
+
320
+ ```ruby
321
+ Dhl::GetQuote::configure do |c|
322
+
323
+ c.side_id "SomeSiteId"
324
+ c.password "p4ssw0rd"
325
+
326
+ c.production_mode! # or test_mode!
327
+
328
+ c.kilograms! # or kilogrammes!
329
+ c.centimeters! # or centimetres!
330
+ c.inches!
331
+ c.pounds!
332
+
333
+ c.dutiable! # or not_dutiable!
334
+
335
+ end
336
+ ```
337
+
338
+ The above block sets defaults for use thereafter. You would then not have to pass site\_id or password in to Dhl::GetQuote::new():
339
+
340
+ ```ruby
341
+ Dhl::GetQuote::configure do
342
+ side_id "SomeSiteId"
343
+ password "p4ssw0rd"
344
+ end
345
+
346
+ request = Dhl::GetQuote::new()
347
+ ```
348
+
349
+ *Note*: options passed in to _Dhl::GetQuote::new()_ will override setting in the _Dhl::GetQuote::configure_ block.
350
+ ## Contributing
351
+
352
+ 1. Fork it
353
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
354
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
355
+ 4. Add tests, make sure existing tests pass.
356
+ 5. Push to the branch (`git push origin my-new-feature`)
357
+ 6. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require "rspec/core/rake_task"
4
+ require "rspec/core/version"
5
+
6
+ task :default => :spec
7
+
8
+ desc "Run all examples"
9
+ RSpec::Core::RakeTask.new(:spec, :default) do |t|
10
+ t.ruby_opts = %w[-w]
11
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dhl/get_quote/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "dhl-get_quote"
8
+ gem.version = Dhl::GetQuote::VERSION
9
+ gem.authors = ["Deseret Book", "Matthew Nielsen"]
10
+ gem.email = ["mnielsen@deseretbook.com"]
11
+ gem.description = %q{Get shipping quotes from DHL}
12
+ gem.summary = %q{Gem to interface with DHL's XML-PI shipping quote service.}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'httparty', '~>0.10.2'
21
+ gem.add_dependency 'multi_xml', '~>0.5.3'
22
+
23
+ gem.add_development_dependency 'rake', '~>10.0.4'
24
+ gem.add_development_dependency 'rake', '10.0.4'
25
+ gem.add_development_dependency 'rspec', '2.13.0'
26
+ gem.add_development_dependency 'rspec-must', '0.0.1'
27
+ # gem.add_development_dependency 'autotest'
28
+ # gem.add_development_dependency 'debugger'
29
+
30
+ end
@@ -0,0 +1,13 @@
1
+ class Dhl::GetQuote
2
+ class InputError < StandardError; end
3
+ class OptionsError < InputError; end
4
+ class FromNotSetError < InputError; end
5
+ class ToNotSetError < InputError; end
6
+ class CountryCodeError < InputError; end
7
+ class PieceError < InputError; end
8
+
9
+ class Upstream < StandardError
10
+ class UnknownError < Upstream; end
11
+ class ValidationFailureError < Upstream; end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module Dhl::GetQuote::Helper
2
+ def underscore(camel_cased_word)
3
+ self.class.underscore(camel_cased_word)
4
+ end
5
+
6
+ module ClassMethods
7
+ def underscore(camel_cased_word)
8
+ camel_cased_word.gsub(/([A-Z]{1})([A-Z]{1})/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
9
+ end
10
+ end
11
+
12
+ def self.included(base)
13
+ base.extend(ClassMethods)
14
+ end
15
+
16
+ end
@@ -0,0 +1,42 @@
1
+ class Dhl::GetQuote::MarketService
2
+ include Dhl::GetQuote::Helper
3
+
4
+ def initialize(options)
5
+ @options = {}
6
+ if options.class.to_s == 'Hash'
7
+ build_from_hash(options)
8
+ else
9
+ build_from_xml(options.to_s)
10
+ end
11
+ end
12
+
13
+ def code
14
+ @local_service_type || @local_product_code
15
+ end
16
+
17
+ def name
18
+ @local_service_type_name || @local_product_name
19
+ end
20
+
21
+ protected
22
+
23
+ def build_from_xml(xml_string)
24
+ @parsed_xml = MultiXml.parse(xml_string)
25
+
26
+ @parsed_xml['MrkSrv'].each do |k,v|
27
+ @options[k] = v
28
+ instance_variable_set("@#{underscore(k)}".to_sym, v)
29
+ self.class.class_eval { attr_reader underscore(k).to_sym }
30
+ end
31
+ end
32
+
33
+ def build_from_hash(options)
34
+ options.each do |k,v|
35
+ k = underscore(k) if k =~ /[A-Z]/
36
+ @options[k] = v
37
+ instance_variable_set("@#{k}".to_sym, v)
38
+ self.class.class_eval { attr_reader k.to_sym }
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,59 @@
1
+ class Dhl::GetQuote::Piece
2
+ attr_accessor :piece_id
3
+
4
+ def initialize(options = {})
5
+ [ :width, :height, :depth, :weight ].each do |i|
6
+ options[i] = options[i].to_i if !!options[i]
7
+ end
8
+
9
+ if options[:weight] && options[:weight] > 0
10
+ @weight = options[:weight]
11
+ else
12
+ raise Dhl::GetQuote::OptionsError, required_option_error_message(:weight)
13
+ end
14
+
15
+ if options[:width] || options[:height] || options[:depth]
16
+ [ :width, :height, :depth ].each do |req|
17
+ if options[req].to_i > 0
18
+ instance_variable_set("@#{req}", options[req].to_i)
19
+ else
20
+ raise Dhl::GetQuote::OptionsError, required_option_error_message(req)
21
+ end
22
+ end
23
+ end
24
+
25
+ @piece_id = options[:piece_id] || 1
26
+
27
+ end
28
+
29
+ def to_h
30
+ h = {}
31
+ [ :width, :height, :depth, :weight ].each do |req|
32
+ if x = instance_variable_get("@#{req}")
33
+ h[req.to_s.capitalize] = x
34
+ end
35
+ end
36
+ h
37
+ end
38
+
39
+ def to_xml
40
+ xml_str = <<eos
41
+ <Piece>
42
+ <PieceID>#{@piece_id}</PieceID>
43
+ eos
44
+
45
+ xml_str << " <Height>#{@height}</Height>\n" if @height
46
+ xml_str << " <Depth>#{@depth}</Depth>\n" if @depth
47
+ xml_str << " <Width>#{@width}</Width>\n" if @width
48
+ xml_str << " <Weight>#{@weight}</Weight>\n" if @weight
49
+
50
+ xml_str += "</Piece>\n"
51
+ xml_str
52
+ end
53
+
54
+ private
55
+
56
+ def required_option_error_message(field)
57
+ ":#{field} is a required for Dhl::GetQuote::Piece. Must be nonzero integer."
58
+ end
59
+ end