active_shipping 1.0.0.pre4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.yardopts +0 -1
  5. data/CHANGELOG.md +17 -0
  6. data/CONTRIBUTING.md +2 -2
  7. data/Gemfile.activesupport32 +1 -0
  8. data/README.md +1 -1
  9. data/Rakefile +1 -1
  10. data/active_shipping.gemspec +2 -2
  11. data/lib/active_shipping.rb +2 -3
  12. data/lib/active_shipping/carriers.rb +1 -0
  13. data/lib/active_shipping/carriers/canada_post_pws.rb +281 -266
  14. data/lib/active_shipping/carriers/correios.rb +285 -0
  15. data/lib/active_shipping/carriers/fedex.rb +205 -199
  16. data/lib/active_shipping/carriers/stamps.rb +218 -219
  17. data/lib/active_shipping/carriers/ups.rb +287 -48
  18. data/lib/active_shipping/carriers/usps.rb +3 -3
  19. data/lib/active_shipping/delivery_date_estimate.rb +21 -0
  20. data/lib/active_shipping/delivery_date_estimates_response.rb +11 -0
  21. data/lib/active_shipping/errors.rb +20 -1
  22. data/lib/active_shipping/location.rb +0 -5
  23. data/lib/active_shipping/response.rb +0 -15
  24. data/lib/active_shipping/version.rb +1 -1
  25. data/test/credentials.yml +11 -3
  26. data/test/fixtures/xml/correios/book_response.xml +13 -0
  27. data/test/fixtures/xml/correios/book_response_invalid.xml +13 -0
  28. data/test/fixtures/xml/correios/clothes_response.xml +43 -0
  29. data/test/fixtures/xml/correios/poster_response.xml +23 -0
  30. data/test/fixtures/xml/correios/shoes_response.xml +43 -0
  31. data/test/fixtures/xml/fedex/tracking_request.xml +13 -11
  32. data/test/fixtures/xml/fedex/tracking_response_bad_tracking_number.xml +20 -0
  33. data/test/fixtures/xml/fedex/tracking_response_delivered_at_door.xml +254 -0
  34. data/test/fixtures/xml/fedex/tracking_response_delivered_at_facility.xml +403 -0
  35. data/test/fixtures/xml/fedex/tracking_response_delivered_with_signature.xml +269 -0
  36. data/test/fixtures/xml/fedex/tracking_response_in_transit.xml +127 -0
  37. data/test/fixtures/xml/fedex/tracking_response_multiple_results.xml +100 -0
  38. data/test/fixtures/xml/fedex/tracking_response_not_found.xml +52 -0
  39. data/test/fixtures/xml/fedex/tracking_response_shipment_exception.xml +209 -0
  40. data/test/fixtures/xml/ups/delivery_dates_response.xml +140 -0
  41. data/test/fixtures/xml/ups/package_exceeds_maximum_length.xml +12 -0
  42. data/test/fixtures/xml/ups/rate_single_service.xml +54 -0
  43. data/test/fixtures/xml/ups/rescheduled_shipment.xml +204 -0
  44. data/test/fixtures/xml/ups/test_real_home_as_residential_destination_response.xml +290 -1
  45. data/test/fixtures/xml/usps/delivered_extended_tracking_response.xml +11 -0
  46. data/test/remote/canada_post_pws_platform_test.rb +35 -22
  47. data/test/remote/canada_post_pws_test.rb +32 -40
  48. data/test/remote/correios_test.rb +83 -0
  49. data/test/remote/fedex_test.rb +95 -13
  50. data/test/remote/stamps_test.rb +1 -0
  51. data/test/remote/ups_test.rb +77 -40
  52. data/test/remote/usps_test.rb +13 -1
  53. data/test/test_helper.rb +12 -2
  54. data/test/unit/carriers/canada_post_pws_rating_test.rb +66 -59
  55. data/test/unit/carriers/canada_post_pws_shipping_test.rb +34 -23
  56. data/test/unit/carriers/correios_test.rb +244 -0
  57. data/test/unit/carriers/fedex_test.rb +161 -156
  58. data/test/unit/carriers/ups_test.rb +193 -1
  59. data/test/unit/carriers/usps_test.rb +14 -0
  60. data/test/unit/location_test.rb +0 -10
  61. metadata +63 -46
  62. metadata.gz.sig +0 -0
  63. data/lib/vendor/test_helper.rb +0 -6
  64. data/lib/vendor/xml_node/README +0 -36
  65. data/lib/vendor/xml_node/Rakefile +0 -21
  66. data/lib/vendor/xml_node/benchmark/bench_generation.rb +0 -30
  67. data/lib/vendor/xml_node/init.rb +0 -1
  68. data/lib/vendor/xml_node/lib/xml_node.rb +0 -221
  69. data/lib/vendor/xml_node/test/test_generating.rb +0 -89
  70. data/lib/vendor/xml_node/test/test_parsing.rb +0 -40
  71. data/test/fixtures/xml/fedex/tracking_response.xml +0 -151
  72. data/test/fixtures/xml/fedex/tracking_response_empty_destination.xml +0 -76
  73. data/test/fixtures/xml/fedex/tracking_response_no_destination.xml +0 -139
  74. data/test/fixtures/xml/fedex/tracking_response_no_ship_time.xml +0 -150
  75. data/test/fixtures/xml/fedex/tracking_response_with_estimated_delivery_date.xml +0 -95
  76. data/test/fixtures/xml/fedex/tracking_response_with_shipper_address.xml +0 -71
@@ -1,12 +1,8 @@
1
- require 'builder'
2
-
3
1
  module ActiveShipping
4
2
  # Stamps.com integration for rating, tracking, address validation, and label generation
5
3
  # Integration ID can be requested from Stamps.com
6
4
 
7
5
  class Stamps < Carrier
8
- self.ssl_version = :SSLv3
9
-
10
6
  cattr_reader :name
11
7
  @@name = 'Stamps'
12
8
 
@@ -225,27 +221,27 @@ module ActiveShipping
225
221
  end
226
222
 
227
223
  def build_header
228
- xml = Builder::XmlMarkup.new
229
- xml.instruct!
230
- xml.soap(:Envelope,
231
- 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/',
232
- 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
233
- 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
234
- 'xmlns:tns' => 'http://stamps.com/xml/namespace/2014/01/swsim/swsimv34'
235
- ) do
236
- xml.soap :Body do
237
- yield(xml)
224
+ Nokogiri::XML::Builder.new do |xml|
225
+ xml['soap'].Envelope(
226
+ 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/',
227
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
228
+ 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
229
+ 'xmlns:tns' => 'http://stamps.com/xml/namespace/2014/01/swsim/swsimv34'
230
+ ) do
231
+ xml['soap'].Body do
232
+ yield(xml)
233
+ end
238
234
  end
239
- end
235
+ end.to_xml
240
236
  end
241
237
 
242
238
  def build_authenticate_user_request
243
239
  build_header do |xml|
244
- xml.tns :AuthenticateUser do
245
- xml.tns :Credentials do
246
- xml.tns(:IntegrationID, @options[:integration_id])
247
- xml.tns(:Username, @options[:username])
248
- xml.tns(:Password, @options[:password])
240
+ xml['tns'].AuthenticateUser do
241
+ xml['tns'].Credentials do
242
+ xml['tns'].IntegrationID(@options[:integration_id])
243
+ xml['tns'].Username(@options[:username])
244
+ xml['tns'].Password(@options[:password])
249
245
  end
250
246
  end
251
247
  end
@@ -253,67 +249,67 @@ module ActiveShipping
253
249
 
254
250
  def build_get_account_info_request
255
251
  build_header do |xml|
256
- xml.tns :GetAccountInfo do
257
- xml.tns(:Authenticator, authenticator)
252
+ xml['tns'].GetAccountInfo do
253
+ xml['tns'].Authenticator(authenticator)
258
254
  end
259
255
  end
260
256
  end
261
257
 
262
258
  def build_purchase_postage_request(purchase_amount, control_total)
263
259
  build_header do |xml|
264
- xml.tns :PurchasePostage do
265
- xml.tns(:Authenticator, authenticator)
266
- xml.tns(:PurchaseAmount, purchase_amount)
267
- xml.tns(:ControlTotal, control_total)
260
+ xml['tns'].PurchasePostage do
261
+ xml['tns'].Authenticator(authenticator)
262
+ xml['tns'].PurchaseAmount(purchase_amount)
263
+ xml['tns'].ControlTotal(control_total)
268
264
  end
269
265
  end
270
266
  end
271
267
 
272
268
  def build_get_purchase_status(transaction_id)
273
269
  build_header do |xml|
274
- xml.tns :GetPurchaseStatus do
275
- xml.tns(:Authenticator, authenticator)
276
- xml.tns(:TransactionID, transaction_id)
270
+ xml['tns'].GetPurchaseStatus do
271
+ xml['tns'].Authenticator(authenticator)
272
+ xml['tns'].TransactionID(transaction_id)
277
273
  end
278
274
  end
279
275
  end
280
276
 
281
277
  def build_cleanse_address_request(address)
282
278
  build_header do |xml|
283
- xml.tns :CleanseAddress do
284
- xml.tns(:Authenticator, authenticator)
279
+ xml['tns'].CleanseAddress do
280
+ xml['tns'].Authenticator(authenticator)
285
281
  add_address(xml, address)
286
282
  end
287
283
  end
288
284
  end
289
285
 
290
286
  def add_address(xml, address, object_type = :Address)
291
- xml.tns object_type do
292
- xml.tns(:FullName, address.name) unless address.name.blank?
293
- xml.tns(:Company, address.company) unless address.company.blank?
294
- xml.tns(:Address1, address.address1)
295
- xml.tns(:Address2, address.address2) unless address.address2.blank?
296
- xml.tns(:Address3, address.address3) unless address.address3.blank?
297
- xml.tns(:City, address.city) unless address.city.blank?
287
+ xml['tns'].public_send(object_type) do
288
+ xml['tns'].FullName( address.name) unless address.name.blank?
289
+ xml['tns'].Company( address.company) unless address.company.blank?
290
+ xml['tns'].Address1( address.address1)
291
+ xml['tns'].Address2( address.address2) unless address.address2.blank?
292
+ xml['tns'].Address3( address.address3) unless address.address3.blank?
293
+ xml['tns'].City( address.city) unless address.city.blank?
298
294
  if domestic?(address)
299
- xml.tns(:State, address.state) unless address.state.blank?
295
+ xml['tns'].State( address.state) unless address.state.blank?
300
296
 
301
297
  zip = (address.postal_code || '').match(/^(\d{5})?-?(\d{4})?$/)
302
- xml.tns(:ZIPCode, zip[1]) unless zip[1].nil?
303
- xml.tns(:ZIPCodeAddOn, zip[2]) unless zip[2].nil?
298
+ xml['tns'].ZIPCode( zip[1]) unless zip[1].nil?
299
+ xml['tns'].ZIPCodeAddOn(zip[2]) unless zip[2].nil?
304
300
  else
305
- xml.tns(:Province, address.province) unless address.province.blank?
306
- xml.tns(:PostalCode, address.postal_code) unless address.postal_code.blank?
301
+ xml['tns'].Province( address.province) unless address.province.blank?
302
+ xml['tns'].PostalCode( address.postal_code) unless address.postal_code.blank?
307
303
  end
308
- xml.tns(:Country, address.country_code) unless address.country_code.blank?
309
- xml.tns(:PhoneNumber, address.phone) unless address.phone.blank?
304
+ xml['tns'].Country( address.country_code) unless address.country_code.blank?
305
+ xml['tns'].PhoneNumber( address.phone) unless address.phone.blank?
310
306
  end
311
307
  end
312
308
 
313
309
  def build_rate_request(origin, destination, package, options)
314
310
  build_header do |xml|
315
- xml.tns :GetRates do
316
- xml.tns(:Authenticator, authenticator)
311
+ xml['tns'].GetRates do
312
+ xml['tns'].Authenticator(authenticator)
317
313
  add_rate(xml, origin, destination, package, options)
318
314
  end
319
315
  end
@@ -324,21 +320,21 @@ module ActiveShipping
324
320
  options[:insured_value] ||= value
325
321
  options[:declared_value] ||= value if international?(destination)
326
322
 
327
- xml.tns :Rate do
328
- xml.tns(:FromZIPCode, origin.postal_code) unless origin.postal_code.blank?
329
- xml.tns(:ToZIPCode, destination.postal_code) unless destination.postal_code.blank?
330
- xml.tns(:ToCountry, destination.country_code) unless destination.country_code.blank?
331
- xml.tns(:ServiceType, options[:service]) unless options[:service].blank?
332
- xml.tns(:PrintLayout, options[:print_layout]) unless options[:print_layout].blank?
333
- xml.tns(:WeightOz, [package.ounces, 1].max)
334
- xml.tns(:PackageType, options[:package_type] || 'Package')
335
- xml.tns(:Length, package.inches(:length)) if package.inches(:length)
336
- xml.tns(:Width, package.inches(:width)) if package.inches(:width)
337
- xml.tns(:Height, package.inches(:height)) if package.inches(:height)
338
- xml.tns(:ShipDate, options[:ship_date] || Date.today)
339
- xml.tns(:InsuredValue, options[:insured_value]) unless options[:insured_value].blank?
340
- xml.tns(:CODValue, options[:cod_value]) unless options[:cod_value].blank?
341
- xml.tns(:DeclaredValue, options[:declared_value]) unless options[:declared_value].blank?
323
+ xml['tns'].Rate do
324
+ xml['tns'].FromZIPCode( origin.postal_code) unless origin.postal_code.blank?
325
+ xml['tns'].ToZIPCode( destination.postal_code) unless destination.postal_code.blank?
326
+ xml['tns'].ToCountry( destination.country_code) unless destination.country_code.blank?
327
+ xml['tns'].ServiceType( options[:service]) unless options[:service].blank?
328
+ xml['tns'].PrintLayout( options[:print_layout]) unless options[:print_layout].blank?
329
+ xml['tns'].WeightOz( [package.ounces, 1].max)
330
+ xml['tns'].PackageType( options[:package_type] || 'Package')
331
+ xml['tns'].Length( package.inches(:length)) if package.inches(:length)
332
+ xml['tns'].Width( package.inches(:width)) if package.inches(:width)
333
+ xml['tns'].Height( package.inches(:height)) if package.inches(:height)
334
+ xml['tns'].ShipDate( options[:ship_date] || Date.today)
335
+ xml['tns'].InsuredValue( options[:insured_value]) unless options[:insured_value].blank?
336
+ xml['tns'].CODValue( options[:cod_value]) unless options[:cod_value].blank?
337
+ xml['tns'].DeclaredValue( options[:declared_value]) unless options[:declared_value].blank?
342
338
 
343
339
  machinable = if package.options.has_key?(:machinable)
344
340
  package.options[:machinable] ? true : false
@@ -346,54 +342,54 @@ module ActiveShipping
346
342
  USPS.package_machinable?(package)
347
343
  end
348
344
 
349
- xml.tns(:NonMachinable, true) unless machinable
345
+ xml['tns'].NonMachinable( true) unless machinable
350
346
 
351
- xml.tns(:RectangularShaped, !package.cylinder?)
352
- xml.tns(:GEMNotes, options[:gem_notes]) unless options[:gem_notes].blank?
347
+ xml['tns'].RectangularShaped(!package.cylinder?)
348
+ xml['tns'].GEMNotes( options[:gem_notes]) unless options[:gem_notes].blank?
353
349
 
354
350
  add_ons = Array(options[:add_ons])
355
351
  unless add_ons.empty?
356
- xml.tns(:AddOns) do
352
+ xml['tns'].AddOns do
357
353
  add_ons.each do |add_on|
358
- xml.tns(:AddOnV5) do
359
- xml.tns(:AddOnType, add_on)
354
+ xml['tns'].AddOnV5 do
355
+ xml['tns'].AddOnType(add_on)
360
356
  end
361
357
  end
362
358
  end
363
359
  end
364
360
 
365
- xml.tns(:ToState, destination.province) unless destination.province.blank?
361
+ xml['tns'].ToState(destination.province) unless destination.province.blank?
366
362
  end
367
363
  end
368
364
 
369
365
  def build_create_indicium_request(origin, destination, package, line_items, options)
370
366
  build_header do |xml|
371
- xml.tns :CreateIndicium do
372
- xml.tns(:Authenticator, authenticator)
373
- xml.tns(:IntegratorTxID, options[:integrator_tx_id] || SecureRandom::uuid)
367
+ xml['tns'].CreateIndicium do
368
+ xml['tns'].Authenticator( authenticator)
369
+ xml['tns'].IntegratorTxID( options[:integrator_tx_id] || SecureRandom::uuid)
374
370
 
375
371
  add_rate(xml, origin, destination, package, options)
376
372
  add_address(xml, origin, :From)
377
373
  add_address(xml, destination, :To)
378
374
  add_customs(xml, line_items, options) unless options[:content_type].blank?
379
375
 
380
- xml.tns(:SampleOnly, options[:sample_only]) unless options[:sample_only].blank?
381
- xml.tns(:ImageType, options[:image_type]) unless options[:image_type].blank?
382
- xml.tns(:EltronPrinterDPIType, options[:label_resolution]) unless options[:label_resolution].blank?
383
- xml.tns(:memo, options[:memo]) unless options[:memo].blank?
384
- xml.tns(:deliveryNotification, options[:delivery_notification]) unless options[:delivery_notification].blank?
376
+ xml['tns'].SampleOnly( options[:sample_only]) unless options[:sample_only].blank?
377
+ xml['tns'].ImageType( options[:image_type]) unless options[:image_type].blank?
378
+ xml['tns'].EltronPrinterDPIType( options[:label_resolution]) unless options[:label_resolution].blank?
379
+ xml['tns'].memo( options[:memo]) unless options[:memo].blank?
380
+ xml['tns'].deliveryNotification( options[:delivery_notification]) unless options[:delivery_notification].blank?
385
381
 
386
382
  add_shipment_notification(xml, options) unless options[:email].blank?
387
383
 
388
- xml.tns(:horizontalOffset, options[:horizontal_offset]) unless options[:horizontal_offest].blank?
389
- xml.tns(:verticalOffset, options[:vertical_offset]) unless options[:vertical_offest].blank?
390
- xml.tns(:printDensity, options[:print_density]) unless options[:print_density].blank?
391
- xml.tns(:rotationDegrees, options[:rotation]) unless options[:rotation].blank?
392
- xml.tns(:printMemo, options[:print_memo]) unless options[:print_memo].blank?
393
- xml.tns(:printInstructions, options[:print_instructions]) unless options[:print_instructions].blank?
394
- xml.tns(:ReturnImageData, options[:return_image_data]) unless options[:return_image_data].blank?
395
- xml.tns(:InternalTransactionNumber, options[:internal_transaction_number]) unless options[:internal_transaction_number].blank?
396
- xml.tns(:PaperSize, options[:paper_size]) unless options[:paper_size].blank?
384
+ xml['tns'].horizontalOffset( options[:horizontal_offset]) unless options[:horizontal_offest].blank?
385
+ xml['tns'].verticalOffset( options[:vertical_offset]) unless options[:vertical_offest].blank?
386
+ xml['tns'].printDensity( options[:print_density]) unless options[:print_density].blank?
387
+ xml['tns'].rotationDegrees( options[:rotation]) unless options[:rotation].blank?
388
+ xml['tns'].printMemo( options[:print_memo]) unless options[:print_memo].blank?
389
+ xml['tns'].printInstructions( options[:print_instructions]) unless options[:print_instructions].blank?
390
+ xml['tns'].ReturnImageData( options[:return_image_data]) unless options[:return_image_data].blank?
391
+ xml['tns'].InternalTransactionNumber(options[:internal_transaction_number]) unless options[:internal_transaction_number].blank?
392
+ xml['tns'].PaperSize( options[:paper_size]) unless options[:paper_size].blank?
397
393
 
398
394
  add_label_recipient_info(xml, options) unless options[:label_email_address].blank?
399
395
  end
@@ -401,32 +397,32 @@ module ActiveShipping
401
397
  end
402
398
 
403
399
  def add_shipment_notification(xml, options)
404
- xml.tns :ShipmentNotification do
405
- xml.tns(:Email, options[:email])
406
- xml.tns(:CCToAccountHolder, options[:cc_to_account_holder]) unless options[:cc_to_account_holder].blank?
407
- xml.tns(:UseCompanyNameInFromLine, options[:use_company_name_in_from_name]) unless options[:use_company_name_in_from_line].blank?
408
- xml.tns(:UseCompanyNameInSubject, options[:use_company_name_in_subject]) unless options[:use_company_name_in_subject].blank?
400
+ xml['tns'].ShipmentNotification do
401
+ xml['tns'].Email( options[:email])
402
+ xml['tns'].CCToAccountHolder( options[:cc_to_account_holder]) unless options[:cc_to_account_holder].blank?
403
+ xml['tns'].UseCompanyNameInFromLine(options[:use_company_name_in_from_name]) unless options[:use_company_name_in_from_line].blank?
404
+ xml['tns'].UseCompanyNameInSubject( options[:use_company_name_in_subject]) unless options[:use_company_name_in_subject].blank?
409
405
  end
410
406
  end
411
407
 
412
408
  def add_customs(xml, line_items, options)
413
- xml.tns :Customs do
414
- xml.tns(:ContentType, options[:content_type])
415
- xml.tns(:Comments, options[:comments]) unless options[:comments].blank?
416
- xml.tns(:LicenseNumber, options[:license_number]) unless options[:license_number].blank?
417
- xml.tns(:CertificateNumber, options[:certificate_number]) unless options[:certificate_number].blank?
418
- xml.tns(:InvoiceNumber, options[:invoice_number]) unless options[:invoice_number].blank?
419
- xml.tns(:OtherDescribe, options[:other_describe]) unless options[:other_describe].blank?
420
-
421
- xml.tns :CustomsLines do
409
+ xml['tns'].Customs do
410
+ xml['tns'].ContentType( options[:content_type])
411
+ xml['tns'].Comments( options[:comments]) unless options[:comments].blank?
412
+ xml['tns'].LicenseNumber( options[:license_number]) unless options[:license_number].blank?
413
+ xml['tns'].CertificateNumber(options[:certificate_number]) unless options[:certificate_number].blank?
414
+ xml['tns'].InvoiceNumber( options[:invoice_number]) unless options[:invoice_number].blank?
415
+ xml['tns'].OtherDescribe( options[:other_describe]) unless options[:other_describe].blank?
416
+
417
+ xml['tns'].CustomsLines do
422
418
  line_items.each do |customs_line|
423
- xml.tns :CustomsLine do
424
- xml.tns(:Description, customs_line.name)
425
- xml.tns(:Quantity, customs_line.quantity)
426
- xml.tns(:Value, '%.2f' % (customs_line.value.to_f / 100))
427
- xml.tns(:WeightOz, customs_line.ounces) unless customs_line.ounces.blank?
428
- xml.tns(:HSTariffNumber, customs_line.hs_code.tr('.', '')[0..5]) unless customs_line.hs_code.blank?
429
- xml.tns(:CountryOfOrigin, customs_line.options[:country]) unless customs_line.options[:country].blank?
419
+ xml['tns'].CustomsLine do
420
+ xml['tns'].Description( customs_line.name)
421
+ xml['tns'].Quantity( customs_line.quantity)
422
+ xml['tns'].Value( '%.2f' % (customs_line.value.to_f / 100))
423
+ xml['tns'].WeightOz( customs_line.ounces) unless customs_line.ounces.blank?
424
+ xml['tns'].HSTariffNumber( customs_line.hs_code.tr('.', '')[0..5]) unless customs_line.hs_code.blank?
425
+ xml['tns'].CountryOfOrigin(customs_line.options[:country]) unless customs_line.options[:country].blank?
430
426
  end
431
427
  end
432
428
  end
@@ -434,19 +430,19 @@ module ActiveShipping
434
430
  end
435
431
 
436
432
  def add_label_recipient_info(xml, options)
437
- xml.tns :LabelRecipientInfo do
438
- xml.tns(:EmailAddress, options[:label_email_address])
439
- xml.tns(:Name, options[:name]) unless options[:name].blank?
440
- xml.tns(:Note, options[:note]) unless options[:note].blank?
441
- xml.tns(:CopyToOriginator, options[:copy_to_originator]) unless options[:copy_to_originator].blank?
433
+ xml['tns'].LabelRecipientInfo do
434
+ xml['tns'].EmailAddress( options[:label_email_address])
435
+ xml['tns'].Name( options[:name]) unless options[:name].blank?
436
+ xml['tns'].Note( options[:note]) unless options[:note].blank?
437
+ xml['tns'].CopyToOriginator(options[:copy_to_originator]) unless options[:copy_to_originator].blank?
442
438
  end
443
439
  end
444
440
 
445
441
  def build_track_shipment_request(shipment_id, options)
446
442
  build_header do |xml|
447
- xml.tns :TrackShipment do
448
- xml.tns(:Authenticator, authenticator)
449
- xml.tns(options[:stamps_tx_id] ? :StampsTxID : :TrackingNumber, shipment_id)
443
+ xml['tns'].TrackShipment do
444
+ xml['tns'].Authenticator(authenticator)
445
+ xml['tns'].public_send(options[:stamps_tx_id] ? :StampsTxID : :TrackingNumber, shipment_id)
450
446
  end
451
447
  end
452
448
  end
@@ -473,10 +469,11 @@ module ActiveShipping
473
469
  response_options[:request] = last_request
474
470
  response_options[:test] = test_mode?
475
471
 
476
- document = REXML::Document.new(xml)
477
- child_element = document.get_elements('//soap:Body/*').first
472
+ document = Nokogiri.XML(xml)
473
+ child_element = document.at_xpath('/soap:Envelope/soap:Body/*')
478
474
  parse_method = 'parse_' + child_element.name.underscore
479
475
  if respond_to?(parse_method, true)
476
+ child_element.document.remove_namespaces!
480
477
  send(parse_method, child_element, response_options)
481
478
  else
482
479
  Response.new(false, "Unknown response object #{child_element.name}", response_options)
@@ -484,22 +481,22 @@ module ActiveShipping
484
481
  end
485
482
 
486
483
  def parse_fault(fault, response_options)
487
- @authenticator = fault.get_text('detail/authenticator').value if fault.get_text('detail/authenticator')
484
+ @authenticator = fault.at('detail/authenticator').text if fault.at('detail/authenticator')
488
485
 
489
- error_code = if fault.elements['detail/stamps_exception']
490
- fault.elements['detail/stamps_exception'].attributes['code']
491
- elsif fault.elements['detail/sdcerror']
492
- fault.elements['detail/sdcerror'].attributes['code']
486
+ error_code = if fault.at('detail/stamps_exception')
487
+ fault.at('detail/stamps_exception')['code']
488
+ elsif fault.at('detail/sdcerror')
489
+ fault.at('detail/sdcerror')['code']
493
490
  else
494
491
  nil
495
492
  end
496
493
 
497
494
  # Renew the Authenticator if it has expired and retry the request
498
- if error_code and error_code.downcase == '002b0202'
495
+ if error_code && error_code.downcase == '002b0202'
499
496
  request = renew_authenticator(last_request)
500
497
  commit(last_swsim_method, request)
501
498
  else
502
- raise ResponseError.new(fault.get_text('faultstring').to_s)
499
+ raise ResponseError.new(fault.at('faultstring').text)
503
500
  end
504
501
  end
505
502
 
@@ -508,37 +505,37 @@ module ActiveShipping
508
505
  end
509
506
 
510
507
  def parse_authenticator(response)
511
- @authenticator = response.get_text('Authenticator').value
508
+ @authenticator = response.at_xpath('Authenticator').text
512
509
  end
513
510
 
514
511
  def parse_get_account_info_response(account_info_response, response_options)
515
512
  parse_authenticator(account_info_response)
516
513
 
517
- account_info = account_info_response.elements['AccountInfo']
518
- response_options[:customer_id] = account_info.get_text('CustomerID').to_s
519
- response_options[:meter_number] = account_info.get_text('MeterNumber').to_s
520
- response_options[:user_id] = account_info.get_text('UserID').to_s
521
- response_options[:max_postage_balance] = account_info.get_text('MaxPostageBalance').to_s
522
- response_options[:lpo_city] = account_info.get_text('LPOCity').to_s
523
- response_options[:lpo_state] = account_info.get_text('LPOState').to_s
524
- response_options[:lpo_zip] = account_info.get_text('LPOZip').to_s
525
-
526
- postage_balance_node = account_info.elements['PostageBalance']
527
- response_options[:available_postage] = postage_balance_node.get_text('AvailablePostage').to_s
528
- response_options[:control_total] = postage_balance_node.get_text('ControlTotal').to_s
529
-
530
- capabilities_node = account_info.elements['Capabilities']
531
- response_options[:can_print_shipping] = capabilities_node.get_text('CanPrintShipping').to_s == 'true'
532
- response_options[:can_use_cost_codes] = capabilities_node.get_text('CanUseCostCodes').to_s == 'true'
533
- response_options[:can_use_hidden_postage] = capabilities_node.get_text('CanUseHiddenPostage').to_s == 'true'
534
- response_options[:can_purchase_sdc_insurance] = capabilities_node.get_text('CanPurchaseSDCInsurance').to_s == 'true'
535
- response_options[:can_print_memo] = capabilities_node.get_text('CanPrintMemoOnShippingLabel').to_s == 'true'
536
- response_options[:can_print_international] = capabilities_node.get_text('CanPrintInternational').to_s == 'true'
537
- response_options[:can_purchase_postage] = capabilities_node.get_text('CanPurchasePostage').to_s == 'true'
538
- response_options[:can_edit_cost_codes] = capabilities_node.get_text('CanEditCostCodes').to_s == 'true'
539
- response_options[:must_use_cost_codes] = capabilities_node.get_text('MustUseCostCodes').to_s == 'true'
540
- response_options[:can_view_online_reports] = capabilities_node.get_text('CanViewOnlineReports').to_s == 'true'
541
- response_options[:per_print_limit] = capabilities_node.get_text('PerPrintLimit').to_s
514
+ account_info = account_info_response.at('AccountInfo')
515
+ response_options[:customer_id] = account_info.at('CustomerID').text
516
+ response_options[:meter_number] = account_info.at('MeterNumber').text
517
+ response_options[:user_id] = account_info.at('UserID').text
518
+ response_options[:max_postage_balance] = account_info.at('MaxPostageBalance').text
519
+ response_options[:lpo_city] = account_info.at('LPOCity').text
520
+ response_options[:lpo_state] = account_info.at('LPOState').text
521
+ response_options[:lpo_zip] = account_info.at('LPOZip').text
522
+
523
+ postage_balance_node = account_info.at('PostageBalance')
524
+ response_options[:available_postage] = postage_balance_node.at('AvailablePostage').text
525
+ response_options[:control_total] = postage_balance_node.at('ControlTotal').text
526
+
527
+ capabilities_node = account_info.at('Capabilities')
528
+ response_options[:can_print_shipping] = capabilities_node.at('CanPrintShipping').text == 'true'
529
+ response_options[:can_use_cost_codes] = capabilities_node.at('CanUseCostCodes').text == 'true'
530
+ response_options[:can_use_hidden_postage] = capabilities_node.at('CanUseHiddenPostage').text == 'true'
531
+ response_options[:can_purchase_sdc_insurance] = capabilities_node.at('CanPurchaseSDCInsurance').text == 'true'
532
+ response_options[:can_print_memo] = capabilities_node.at('CanPrintMemoOnShippingLabel').text == 'true'
533
+ response_options[:can_print_international] = capabilities_node.at('CanPrintInternational').text == 'true'
534
+ response_options[:can_purchase_postage] = capabilities_node.at('CanPurchasePostage').text == 'true'
535
+ response_options[:can_edit_cost_codes] = capabilities_node.at('CanEditCostCodes').text == 'true'
536
+ response_options[:must_use_cost_codes] = capabilities_node.at('MustUseCostCodes').text == 'true'
537
+ response_options[:can_view_online_reports] = capabilities_node.at('CanViewOnlineReports').text == 'true'
538
+ response_options[:per_print_limit] = capabilities_node.at('PerPrintLimit').text
542
539
 
543
540
  StampsAccountInfoResponse.new(true, '', {}, response_options)
544
541
  end
@@ -546,13 +543,13 @@ module ActiveShipping
546
543
  def parse_purchase_postage_response(postage, response_options)
547
544
  parse_authenticator(postage)
548
545
 
549
- response_options[:purchase_status] = postage.get_text('PurchaseStatus').to_s
550
- response_options[:rejection_reason] = postage.get_text('RejectionReason').to_s if postage.get_text('RejectionReason')
551
- response_options[:transaction_id] = postage.get_text('TransactionID').to_s if postage.get_text('TransactionID')
546
+ response_options[:purchase_status] = postage.at('PurchaseStatus').text
547
+ response_options[:rejection_reason] = parse_content(postage, 'RejectionReason')
548
+ response_options[:transaction_id] = parse_content(postage, 'TransactionID')
552
549
 
553
- balance = postage.elements['PostageBalance']
554
- response_options[:available_postage] = balance.get_text('AvailablePostage').to_s
555
- response_options[:control_total] = balance.get_text('ControlTotal').to_s if balance.get_text('ControlTotal')
550
+ balance = postage.at('PostageBalance')
551
+ response_options[:available_postage] = balance.at('AvailablePostage').text
552
+ response_options[:control_total] = parse_content(balance, 'ControlTotal')
556
553
 
557
554
  StampsPurchasePostageResponse.new(true, '', {}, response_options)
558
555
  end
@@ -561,19 +558,18 @@ module ActiveShipping
561
558
  def parse_cleanse_address_response(cleanse_address, response_options)
562
559
  parse_authenticator(cleanse_address)
563
560
 
564
- response_options[:address_match] = cleanse_address.get_text('AddressMatch').to_s == 'true'
565
- response_options[:city_state_zip_ok] = cleanse_address.get_text('CityStateZipOK').to_s == 'true'
561
+ response_options[:address_match] = cleanse_address.at('AddressMatch').text == 'true'
562
+ response_options[:city_state_zip_ok] = cleanse_address.at('CityStateZipOK').text == 'true'
566
563
 
567
- address = cleanse_address.elements['Address']
568
- response_options[:cleanse_hash] = address.get_text('CleanseHash').to_s if address.get_text('CleanseHash')
569
- response_options[:override_hash] = address.get_text('OverrideHash').to_s if address.get_text('OverrideHash')
564
+ address = cleanse_address.at('Address')
565
+ response_options[:cleanse_hash] = parse_content(address, 'CleanseHash')
566
+ response_options[:override_hash] = parse_content(address, 'OverrideHash')
570
567
 
571
- address_node = cleanse_address.elements['Address']
572
- indicator_node = cleanse_address.get_text('ResidentialDeliveryIndicatorType').to_s
573
- po_box_node = cleanse_address.get_text('IsPOBox').to_s
574
- response_options[:address] = parse_address(address_node, indicator_node, po_box_node)
568
+ indicator_node = cleanse_address.at('ResidentialDeliveryIndicatorType')
569
+ po_box_node = cleanse_address.at('IsPOBox')
570
+ response_options[:address] = parse_address(address, indicator_node, po_box_node)
575
571
 
576
- candidate_addresses = cleanse_address.get_elements('CandidateAddresses/Address')
572
+ candidate_addresses = cleanse_address.xpath('CandidateAddresses/Address')
577
573
  response_options[:candidate_addresses] = candidate_addresses.map do |candidate_address|
578
574
  parse_address(candidate_address)
579
575
  end
@@ -584,23 +580,23 @@ module ActiveShipping
584
580
  def parse_address(address_node, residential_indicator_node = nil, po_box_node = nil)
585
581
  address = {}
586
582
 
587
- address[:name] = address_node.get_text('FullName').to_s if address_node.get_text('FullName')
588
- address[:company] = address_node.get_text('Company').to_s if address_node.get_text('Company')
589
- address[:address1] = address_node.get_text('Address1').to_s if address_node.get_text('Address1')
590
- address[:address2] = address_node.get_text('Address2').to_s if address_node.get_text('Address2')
591
- address[:address3] = address_node.get_text('Address3').to_s if address_node.get_text('Address3')
592
- address[:city] = address_node.get_text('City').to_s if address_node.get_text('City')
593
- address[:country] = address_node.get_text('Country').to_s if address_node.get_text('Country')
594
- address[:phone] = address_node.get_text('PhoneNumber').to_s if address_node.get_text('PhoneNumber')
583
+ address[:name] = parse_content(address_node, 'FullName')
584
+ address[:company] = parse_content(address_node, 'Company')
585
+ address[:address1] = parse_content(address_node, 'Address1')
586
+ address[:address2] = parse_content(address_node, 'Address2')
587
+ address[:address3] = parse_content(address_node, 'Address3')
588
+ address[:city] = parse_content(address_node, 'City')
589
+ address[:country] = parse_content(address_node, 'Country')
590
+ address[:phone] = parse_content(address_node, 'PhoneNumber')
595
591
 
596
592
  if address[:country] == 'US' || address[:country].nil?
597
- address[:state] = address_node.get_text('State').to_s if address_node.get_text('State')
593
+ address[:state] = parse_content(address_node, 'State')
598
594
 
599
- address[:postal_code] = address_node.get_text('ZIPCode').to_s if address_node.get_text('ZIPCode')
600
- address[:postal_code] += '-' + address_node.get_text('ZIPCodeAddOn').to_s if address_node.get_text('ZIPCodeAddOn')
595
+ address[:postal_code] = parse_content(address_node, 'ZIPCode')
596
+ address[:postal_code] += '-' + parse_content(address_node, 'ZIPCodeAddOn')
601
597
  else
602
- address[:province] = address_node.get_text('Province').to_s if address_node.get_text('Province')
603
- address[:postal_code] = address_node.get_text('PostalCode').to_s if address_node.get_text('PostalCode')
598
+ address[:province] = parse_content(address_node, 'Province')
599
+ address[:postal_code] = parse_content(address_node, 'PostalCode')
604
600
  end
605
601
 
606
602
  address[:address_type] = if residential_indicator_node == 'Yes'
@@ -619,7 +615,7 @@ module ActiveShipping
619
615
  def parse_get_rates_response(get_rates, response_options)
620
616
  parse_authenticator(get_rates)
621
617
 
622
- response_options[:estimates] = get_rates.get_elements('Rates/Rate').map do |rate|
618
+ response_options[:estimates] = get_rates.xpath('Rates/Rate').map do |rate|
623
619
  parse_rate(rate)
624
620
  end
625
621
 
@@ -629,33 +625,33 @@ module ActiveShipping
629
625
  def parse_rate(rate)
630
626
  rate_options = {}
631
627
 
632
- origin = Location.new(zip: rate.get_text('FromZIPCode').to_s)
628
+ origin = Location.new(zip: rate.at('FromZIPCode').text)
633
629
 
634
630
  location_values = {}
635
- location_values[:zip] = rate.get_text('ToZIPCode').to_s if rate.get_text('ToZIPCode')
636
- location_values[:country] = rate.get_text('ToCountry').to_s if rate.get_text('ToCountry')
631
+ location_values[:zip] = parse_content(rate, 'ToZIPCode')
632
+ location_values[:country] = parse_content(rate, 'ToCountry')
637
633
  destination = Location.new(location_values)
638
634
 
639
- service_name = SERVICE_TYPES[rate.get_text('ServiceType').to_s]
635
+ service_name = SERVICE_TYPES[rate.at('ServiceType').text]
640
636
 
641
- rate_options[:service_code] = rate.get_text('ServiceType').to_s
637
+ rate_options[:service_code] = rate.at('ServiceType').text
642
638
  rate_options[:currency] = 'USD'
643
- rate_options[:shipping_date] = Date.parse(rate.get_text('ShipDate').to_s)
639
+ rate_options[:shipping_date] = Date.parse(rate.at('ShipDate').text)
644
640
 
645
- if delivery_days = rate.get_text('DeliverDays')
646
- delivery_days = delivery_days.to_s.split('-')
641
+ if delivery_days = rate.at('DeliverDays')
642
+ delivery_days = delivery_days.text.split('-')
647
643
  rate_options[:delivery_range] = delivery_days.map { |day| rate_options[:shipping_date] + day.to_i.days }
648
644
  end
649
645
 
650
- rate_options[:total_price] = rate.get_text('Amount').to_s
646
+ rate_options[:total_price] = rate.at('Amount').text
651
647
 
652
648
  rate_options[:add_ons] = parse_add_ons(rate)
653
649
  rate_options[:packages] = parse_package(rate)
654
650
 
655
651
  add_ons = rate_options[:add_ons]
656
- if add_ons['SC-A-INS'] and add_ons['SC-A-INS'][:amount]
652
+ if add_ons['SC-A-INS'] && add_ons['SC-A-INS'][:amount]
657
653
  rate_options[:insurance_price] = add_ons['SC-A-INS'][:amount]
658
- elsif add_ons['US-A-INS'] and add_ons['US-A-INS'][:amount]
654
+ elsif add_ons['US-A-INS'] && add_ons['US-A-INS'][:amount]
659
655
  rate_options[:insurance_price] = add_ons['US-A-INS'][:amount]
660
656
  end
661
657
 
@@ -664,14 +660,14 @@ module ActiveShipping
664
660
 
665
661
  def parse_add_ons(rate)
666
662
  add_ons = {}
667
- rate.get_elements('AddOns/AddOnV5').each do |add_on|
668
- add_on_type = add_on.get_text('AddOnType').to_s
663
+ rate.xpath('AddOns/AddOnV5').each do |add_on|
664
+ add_on_type = add_on.at('AddOnType').text
669
665
 
670
666
  add_on_details = {}
671
- add_on_details[:missing_data] = add_on.get_text('MissingData').to_s if add_on.get_text('MissingData')
672
- add_on_details[:amount] = add_on.get_text('Amount').to_s if add_on.get_text('Amount')
667
+ add_on_details[:missing_data] = parse_content(add_on, 'MissingData') if add_on.at('MissingData')
668
+ add_on_details[:amount] = parse_content(add_on, 'Amount') if add_on.at('Amount')
673
669
 
674
- prohibited_with = add_on.get_elements('ProhibitedWithAnyOf/AddOnTypeV5').map(&:text)
670
+ prohibited_with = add_on.xpath('ProhibitedWithAnyOf/AddOnTypeV5').map(&:text)
675
671
  add_on_details[:prohibited_with] = prohibited_with unless prohibited_with.empty?
676
672
 
677
673
  add_ons[add_on_type] = add_on_details
@@ -681,17 +677,17 @@ module ActiveShipping
681
677
  end
682
678
 
683
679
  def parse_package(rate)
684
- weight = rate.get_text('WeightOz').to_s.to_f
680
+ weight = rate.at('WeightOz').text.to_f
685
681
 
686
682
  dimensions = %w(Length Width Height).map do |dim|
687
- rate.get_text(dim) ? rate.get_text(dim).to_s.to_f : nil
683
+ rate.at(dim) ? rate.at(dim).text.to_f : nil
688
684
  end
689
685
  dimensions.compact!
690
686
 
691
687
  package_options = { units: :imperial }
692
688
 
693
- if value = rate.get_text('InsuredValue') || rate.get_text('DeclaredValue')
694
- package_options[:value] = value.to_s.to_f
689
+ if value = rate.at('InsuredValue') || rate.at('DeclaredValue')
690
+ package_options[:value] = value.text.to_f
695
691
  package_options[:currency] = 'USD'
696
692
  end
697
693
 
@@ -701,14 +697,14 @@ module ActiveShipping
701
697
  def parse_create_indicium_response(indicium, response_options)
702
698
  parse_authenticator(indicium)
703
699
 
704
- response_options[:shipping_id] = indicium.get_text('IntegratorTxID').to_s
705
- response_options[:tracking_number] = indicium.get_text('TrackingNumber').to_s if indicium.get_text('TrackingNumber')
706
- response_options[:stamps_tx_id] = indicium.get_text('StampsTxID').to_s
707
- response_options[:label_url] = indicium.get_text('URL').to_s if indicium.get_text('URL')
708
- response_options[:available_postage] = indicium.get_text('PostageBalance/AvailablePostage').to_s
709
- response_options[:control_total] = indicium.get_text('PostageBalance/ControlTotal').to_s
710
- response_options[:image_data] = Base64.decode64(indicium.get_text('ImageData/base64Binary').to_s) if indicium.get_text('ImageData/base64Binary')
711
- response_options[:rate] = parse_rate(indicium.elements['Rate'])
700
+ response_options[:shipping_id] = indicium.at('IntegratorTxID').text
701
+ response_options[:tracking_number] = parse_content(indicium, 'TrackingNumber')
702
+ response_options[:stamps_tx_id] = indicium.at('StampsTxID').text
703
+ response_options[:label_url] = parse_content(indicium, 'URL')
704
+ response_options[:available_postage] = indicium.at('PostageBalance/AvailablePostage').text
705
+ response_options[:control_total] = indicium.at('PostageBalance/ControlTotal').text
706
+ response_options[:image_data] = Base64.decode64(indicium.at('ImageData/base64Binary').text) if indicium.at('ImageData/base64Binary')
707
+ response_options[:rate] = parse_rate(indicium.at('Rate'))
712
708
 
713
709
  StampsShippingResponse.new(true, '', {}, response_options)
714
710
  end
@@ -718,27 +714,27 @@ module ActiveShipping
718
714
 
719
715
  response_options[:carrier] = @@name
720
716
 
721
- shipment_events = track_shipment.get_elements('TrackingEvents/TrackingEvent').map do |event|
717
+ shipment_events = track_shipment.xpath('TrackingEvents/TrackingEvent').map do |event|
722
718
  unless response_options[:status]
723
- response_options[:status_code] = event.get_text('TrackingEventType').to_s
719
+ response_options[:status_code] = event.at('TrackingEventType').text
724
720
  response_options[:status] = response_options[:status_code].underscore.to_sym
725
721
  end
726
722
 
727
- response_options[:delivery_signature] = event.get_text('SignedBy').to_s if event.get_text('SignedBy')
723
+ response_options[:delivery_signature] = parse_content(event, 'SignedBy')
728
724
 
729
- description = event.get_text('Event').to_s
725
+ description = event.at('Event').text
730
726
 
731
- timestamp = event.get_text('Timestamp').to_s
727
+ timestamp = event.at('Timestamp').text
732
728
  date, time = timestamp.split('T')
733
729
  year, month, day = date.split('-')
734
730
  hour, minute, second = time.split(':')
735
731
  zoneless_time = Time.utc(year, month, day, hour, minute, second)
736
732
 
737
733
  location = Location.new(
738
- city: event.get_text('City').to_s,
739
- state: event.get_text('State').to_s,
740
- zip: event.get_text('Zip').to_s,
741
- country: event.get_text('Country').to_s
734
+ city: event.at('City').text,
735
+ state: event.at('State').text,
736
+ zip: event.at('Zip').text,
737
+ country: event.at('Country').text
742
738
  )
743
739
 
744
740
  ShipmentEvent.new(description, zoneless_time, location)
@@ -749,6 +745,11 @@ module ActiveShipping
749
745
 
750
746
  TrackingResponse.new(true, '', {}, response_options)
751
747
  end
748
+
749
+ def parse_content(node, child)
750
+ return unless node.at(child) && node.at(child).text != ''
751
+ node.at(child).text
752
+ end
752
753
  end
753
754
 
754
755
  class StampsAccountInfoResponse < Response
@@ -840,8 +841,6 @@ module ActiveShipping
840
841
  class StampsShippingResponse < ShippingResponse
841
842
  include ActiveUtils::PostsData
842
843
 
843
- self.ssl_version = :SSLv3
844
-
845
844
  attr_reader :rate, :stamps_tx_id, :label_url, :available_postage, :control_total
846
845
 
847
846
  def initialize(success, message, params = {}, options = {})