active_shipping 0.11.1 → 0.11.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -289,7 +289,7 @@ module ActiveMerchant
289
289
  expected_date = root_node.get_text('expected-delivery-date').to_s
290
290
  dest_postal_code = root_node.get_text('destination-postal-id').to_s
291
291
  destination = Location.new(:postal_code => dest_postal_code)
292
- origin = Location.new({})
292
+ origin = Location.new(origin_hash_for(root_node))
293
293
  options = {
294
294
  :carrier => @@name,
295
295
  :service_name => root_node.get_text('service-name').to_s,
@@ -762,6 +762,24 @@ module ActiveMerchant
762
762
  return value == 0 ? 0.01 : value.round / 100.0
763
763
  end
764
764
 
765
+ def origin_hash_for(root_node)
766
+ occurrences = root_node.get_elements('significant-events').first.get_elements('occurrence')
767
+ earliest = occurrences.sort_by { |occurrence| time_of_occurrence occurrence }.first
768
+
769
+ {
770
+ city: earliest.get_text('event-site'),
771
+ province: earliest.get_text('event-province'),
772
+ address_1: earliest.get_text('event-retail-location-id'),
773
+ country: 'Canada'
774
+ }
775
+ end
776
+
777
+ def time_of_occurrence(occurrence)
778
+ time = occurrence.get_text('event_time')
779
+ date = occurrence.get_text('event-date')
780
+ time_zone = occurrence.get_text('event-date')
781
+ DateTime.parse "#{date} #{time} #{time_zone}"
782
+ end
765
783
  end
766
784
 
767
785
  module CPPWSErrorResponse
@@ -771,20 +789,21 @@ module ActiveMerchant
771
789
  end
772
790
  end
773
791
 
774
- class CPPWSRateResponse < RateResponse
792
+ class CPPWSRateResponse < RateResponse
775
793
  include CPPWSErrorResponse
776
-
794
+
777
795
  def initialize(success, message, params = {}, options = {})
778
796
  handle_error(message, options)
779
797
  super
780
798
  end
781
799
  end
782
-
800
+
783
801
  class CPPWSTrackingResponse < TrackingResponse
802
+ DELIVERED_EVENT_CODES = %w(1496 1498 1499 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438)
784
803
  include CPPWSErrorResponse
785
804
 
786
805
  attr_reader :service_name, :expected_date, :changed_date, :change_reason, :customer_number
787
-
806
+
788
807
  def initialize(success, message, params = {}, options = {})
789
808
  handle_error(message, options)
790
809
  super
@@ -794,6 +813,21 @@ module ActiveMerchant
794
813
  @change_reason = options[:change_reason]
795
814
  @customer_number = options[:customer_number]
796
815
  end
816
+
817
+ def delivered?
818
+ ! delivered_event.nil?
819
+ end
820
+
821
+ def actual_delivery_time
822
+ delivered_event.time if delivered?
823
+ end
824
+
825
+
826
+ private
827
+
828
+ def delivered_event
829
+ @delivered_event ||= @shipment_events.detect { |event| DELIVERED_EVENT_CODES.include? event.name }
830
+ end
797
831
  end
798
832
 
799
833
  class CPPWSShippingResponse < ShippingResponse
@@ -145,7 +145,8 @@ module ActiveMerchant
145
145
 
146
146
  rate_request = build_rate_request(origin, destination, packages, options)
147
147
 
148
- response = commit(save_request(rate_request), (options[:test] || false)).gsub(/<(\/)?.*?\:(.*?)>/, '<\1\2>')
148
+ xml = commit(save_request(rate_request), (options[:test] || false))
149
+ response = remove_version_prefix(xml)
149
150
 
150
151
  parse_rate_response(origin, destination, packages, response, options)
151
152
  end
@@ -154,7 +155,8 @@ module ActiveMerchant
154
155
  options = @options.update(options)
155
156
 
156
157
  tracking_request = build_tracking_request(tracking_number, options)
157
- response = commit(save_request(tracking_request), (options[:test] || false)).gsub(/<(\/)?.*?\:(.*?)>/, '<\1\2>')
158
+ xml = commit(save_request(tracking_request), (options[:test] || false))
159
+ response = remove_version_prefix(xml)
158
160
  parse_tracking_response(response, options)
159
161
  end
160
162
 
@@ -480,7 +482,7 @@ module ActiveMerchant
480
482
  :actual_delivery_date => actual_delivery_time,
481
483
  :delivery_signature => delivery_signature,
482
484
  :shipment_events => shipment_events,
483
- :shipper_address => shipper_address.unknown? ? nil : shipper_address,
485
+ :shipper_address => (shipper_address.nil? || shipper_address.unknown?) ? nil : shipper_address,
484
486
  :origin => origin,
485
487
  :destination => destination,
486
488
  :tracking_number => tracking_number
@@ -561,6 +563,14 @@ module ActiveMerchant
561
563
  end
562
564
  end
563
565
 
566
+ def remove_version_prefix(xml)
567
+ if xml =~ /xmlns:v[0-9]/
568
+ xml.gsub(/<(\/)?.*?\:(.*?)>/, '<\1\2>')
569
+ else
570
+ xml
571
+ end
572
+ end
573
+
564
574
  def build_document(xml)
565
575
  REXML::Document.new(xml)
566
576
  rescue REXML::ParseException => e
@@ -161,6 +161,8 @@ module ActiveMerchant
161
161
  end
162
162
 
163
163
  response
164
+ rescue NoMethodError => e
165
+ raise ActiveMerchant::Shipping::ResponseContentError.new(e, xml)
164
166
  end
165
167
 
166
168
  def parse_child_text(parent, name)
@@ -380,10 +380,9 @@ module ActiveMerchant
380
380
  # This adds an origin event to the shipment activity in such cases.
381
381
  if origin && !(shipment_events.count == 1 && status == :delivered)
382
382
  first_event = shipment_events[0]
383
- same_country = origin.country_code(:alpha2) == first_event.location.country_code(:alpha2)
384
- same_or_blank_city = first_event.location.city.blank? or first_event.location.city == origin.city
385
383
  origin_event = ShipmentEvent.new(first_event.name, first_event.time, origin)
386
- if same_country and same_or_blank_city
384
+
385
+ if within_same_area?(origin, first_event.location)
387
386
  shipment_events[0] = origin_event
388
387
  else
389
388
  shipment_events.unshift(origin_event)
@@ -456,6 +455,12 @@ module ActiveMerchant
456
455
  ssl_post("#{test ? TEST_URL : LIVE_URL}/#{RESOURCES[action]}", request)
457
456
  end
458
457
 
458
+ def within_same_area?(origin, location)
459
+ return false unless location
460
+ matching_country_codes = origin.country_code(:alpha2) == location.country_code(:alpha2)
461
+ matching_or_blank_city = location.city.blank? || location.city == origin.city
462
+ matching_country_codes && matching_or_blank_city
463
+ end
459
464
 
460
465
  def service_name_for(origin, code)
461
466
  origin = origin.country_code(:alpha2)
@@ -13,6 +13,11 @@ module ActiveMerchant
13
13
  # This will send a test request to the USPS test servers, which they ask you
14
14
  # to do before they put your API key in production mode.
15
15
  class USPS < Carrier
16
+ EventDetails = Struct.new(:description, :time, :zoneless_time, :location)
17
+ EVENT_MESSAGE_PATTERNS = [
18
+ /^(.*), (\w+ \d{1,2}, \d{4}, \d{1,2}:\d\d [ap]m), (.*), (\w\w) (\d{5})$/i,
19
+ /^Your item \w{2,3} (out for delivery|delivered) at (\d{1,2}:\d\d [ap]m on \w+ \d{1,2}, \d{4}) in (.*), (\w\w) (\d{5})\.$/i
20
+ ]
16
21
  self.retry_safe = true
17
22
 
18
23
  cattr_reader :name
@@ -199,6 +204,20 @@ module ActiveMerchant
199
204
  Mass.new(70, :pounds)
200
205
  end
201
206
 
207
+ def extract_event_details(message)
208
+ return EventDetails.new unless EVENT_MESSAGE_PATTERNS.any?{|pattern| message =~ pattern}
209
+ description = $1.upcase
210
+ timestamp = $2
211
+ city = $3
212
+ state = $4
213
+ zip_code = $5
214
+
215
+ time = Time.parse(timestamp)
216
+ zoneless_time = Time.utc(time.year, time.month, time.mday, time.hour, time.min, time.sec)
217
+ location = Location.new(city: city, state: state, postal_code: zip_code, country: 'USA')
218
+ EventDetails.new($1.upcase, time, zoneless_time, location)
219
+ end
220
+
202
221
  protected
203
222
 
204
223
  def build_tracking_request(tracking_number, options={})
@@ -492,23 +511,8 @@ module ActiveMerchant
492
511
  tracking_number = root_node.elements['TrackInfo'].attributes['ID'].to_s
493
512
 
494
513
  tracking_details.each do |event|
495
- location = nil
496
- timestamp = nil
497
- description = nil
498
- if event.get_text.to_s =~ /^(.*), (\w+ \d\d, \d{4}, \d{1,2}:\d\d [ap]m), (.*), (\w\w) (\d{5})$/i ||
499
- event.get_text.to_s =~ /^Your item \w{2,3} (out for delivery|delivered) at (\d{1,2}:\d\d [ap]m on \w+ \d\d, \d{4}) in (.*), (\w\w) (\d{5})\.$/i
500
- description = $1.upcase
501
- timestamp = $2
502
- city = $3
503
- state = $4
504
- zip_code = $5
505
- location = Location.new(:city => city, :state => state, :postal_code => zip_code, :country => 'USA')
506
- end
507
- if location
508
- time = Time.parse(timestamp)
509
- zoneless_time = Time.utc(time.year, time.month, time.mday, time.hour, time.min, time.sec)
510
- shipment_events << ShipmentEvent.new(description, zoneless_time, location)
511
- end
514
+ details = extract_event_details(event.get_text.to_s)
515
+ shipment_events << ShipmentEvent.new(details.description, details.zoneless_time, details.location) if details.location
512
516
  end
513
517
 
514
518
  shipment_events = shipment_events.sort_by(&:time)
@@ -1,3 +1,3 @@
1
1
  module ActiveShipping
2
- VERSION = "0.11.1"
2
+ VERSION = "0.11.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_shipping
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.1
4
+ version: 0.11.2
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - James MacAulay
@@ -11,81 +12,92 @@ authors:
11
12
  autorequire:
12
13
  bindir: bin
13
14
  cert_chain: []
14
- date: 2013-12-31 00:00:00.000000000 Z
15
+ date: 2014-01-20 00:00:00.000000000 Z
15
16
  dependencies:
16
17
  - !ruby/object:Gem::Dependency
17
18
  name: activesupport
18
19
  requirement: !ruby/object:Gem::Requirement
20
+ none: false
19
21
  requirements:
20
- - - '>='
22
+ - - ! '>='
21
23
  - !ruby/object:Gem::Version
22
24
  version: 2.3.5
23
25
  type: :runtime
24
26
  prerelease: false
25
27
  version_requirements: !ruby/object:Gem::Requirement
28
+ none: false
26
29
  requirements:
27
- - - '>='
30
+ - - ! '>='
28
31
  - !ruby/object:Gem::Version
29
32
  version: 2.3.5
30
33
  - !ruby/object:Gem::Dependency
31
34
  name: i18n
32
35
  requirement: !ruby/object:Gem::Requirement
36
+ none: false
33
37
  requirements:
34
- - - '>='
38
+ - - ! '>='
35
39
  - !ruby/object:Gem::Version
36
40
  version: '0'
37
41
  type: :runtime
38
42
  prerelease: false
39
43
  version_requirements: !ruby/object:Gem::Requirement
44
+ none: false
40
45
  requirements:
41
- - - '>='
46
+ - - ! '>='
42
47
  - !ruby/object:Gem::Version
43
48
  version: '0'
44
49
  - !ruby/object:Gem::Dependency
45
50
  name: active_utils
46
51
  requirement: !ruby/object:Gem::Requirement
52
+ none: false
47
53
  requirements:
48
- - - '>='
54
+ - - ! '>='
49
55
  - !ruby/object:Gem::Version
50
56
  version: 1.0.1
51
57
  type: :runtime
52
58
  prerelease: false
53
59
  version_requirements: !ruby/object:Gem::Requirement
60
+ none: false
54
61
  requirements:
55
- - - '>='
62
+ - - ! '>='
56
63
  - !ruby/object:Gem::Version
57
64
  version: 1.0.1
58
65
  - !ruby/object:Gem::Dependency
59
66
  name: builder
60
67
  requirement: !ruby/object:Gem::Requirement
68
+ none: false
61
69
  requirements:
62
- - - '>='
70
+ - - ! '>='
63
71
  - !ruby/object:Gem::Version
64
72
  version: '0'
65
73
  type: :runtime
66
74
  prerelease: false
67
75
  version_requirements: !ruby/object:Gem::Requirement
76
+ none: false
68
77
  requirements:
69
- - - '>='
78
+ - - ! '>='
70
79
  - !ruby/object:Gem::Version
71
80
  version: '0'
72
81
  - !ruby/object:Gem::Dependency
73
82
  name: json
74
83
  requirement: !ruby/object:Gem::Requirement
84
+ none: false
75
85
  requirements:
76
- - - '>='
86
+ - - ! '>='
77
87
  - !ruby/object:Gem::Version
78
88
  version: 1.5.1
79
89
  type: :runtime
80
90
  prerelease: false
81
91
  version_requirements: !ruby/object:Gem::Requirement
92
+ none: false
82
93
  requirements:
83
- - - '>='
94
+ - - ! '>='
84
95
  - !ruby/object:Gem::Version
85
96
  version: 1.5.1
86
97
  - !ruby/object:Gem::Dependency
87
98
  name: minitest
88
99
  requirement: !ruby/object:Gem::Requirement
100
+ none: false
89
101
  requirements:
90
102
  - - ~>
91
103
  - !ruby/object:Gem::Version
@@ -93,6 +105,7 @@ dependencies:
93
105
  type: :development
94
106
  prerelease: false
95
107
  version_requirements: !ruby/object:Gem::Requirement
108
+ none: false
96
109
  requirements:
97
110
  - - ~>
98
111
  - !ruby/object:Gem::Version
@@ -100,20 +113,23 @@ dependencies:
100
113
  - !ruby/object:Gem::Dependency
101
114
  name: rake
102
115
  requirement: !ruby/object:Gem::Requirement
116
+ none: false
103
117
  requirements:
104
- - - '>='
118
+ - - ! '>='
105
119
  - !ruby/object:Gem::Version
106
120
  version: '0'
107
121
  type: :development
108
122
  prerelease: false
109
123
  version_requirements: !ruby/object:Gem::Requirement
124
+ none: false
110
125
  requirements:
111
- - - '>='
126
+ - - ! '>='
112
127
  - !ruby/object:Gem::Version
113
128
  version: '0'
114
129
  - !ruby/object:Gem::Dependency
115
130
  name: mocha
116
131
  requirement: !ruby/object:Gem::Requirement
132
+ none: false
117
133
  requirements:
118
134
  - - ~>
119
135
  - !ruby/object:Gem::Version
@@ -121,6 +137,7 @@ dependencies:
121
137
  type: :development
122
138
  prerelease: false
123
139
  version_requirements: !ruby/object:Gem::Requirement
140
+ none: false
124
141
  requirements:
125
142
  - - ~>
126
143
  - !ruby/object:Gem::Version
@@ -128,29 +145,33 @@ dependencies:
128
145
  - !ruby/object:Gem::Dependency
129
146
  name: timecop
130
147
  requirement: !ruby/object:Gem::Requirement
148
+ none: false
131
149
  requirements:
132
- - - '>='
150
+ - - ! '>='
133
151
  - !ruby/object:Gem::Version
134
152
  version: '0'
135
153
  type: :development
136
154
  prerelease: false
137
155
  version_requirements: !ruby/object:Gem::Requirement
156
+ none: false
138
157
  requirements:
139
- - - '>='
158
+ - - ! '>='
140
159
  - !ruby/object:Gem::Version
141
160
  version: '0'
142
161
  - !ruby/object:Gem::Dependency
143
162
  name: nokogiri
144
163
  requirement: !ruby/object:Gem::Requirement
164
+ none: false
145
165
  requirements:
146
- - - '>='
166
+ - - ! '>='
147
167
  - !ruby/object:Gem::Version
148
168
  version: '0'
149
169
  type: :development
150
170
  prerelease: false
151
171
  version_requirements: !ruby/object:Gem::Requirement
172
+ none: false
152
173
  requirements:
153
- - - '>='
174
+ - - ! '>='
154
175
  - !ruby/object:Gem::Version
155
176
  version: '0'
156
177
  description: Get rates and tracking info from various shipping carriers.
@@ -212,25 +233,29 @@ files:
212
233
  - CHANGELOG
213
234
  homepage: http://github.com/shopify/active_shipping
214
235
  licenses: []
215
- metadata: {}
216
236
  post_install_message:
217
237
  rdoc_options: []
218
238
  require_paths:
219
239
  - lib
220
240
  required_ruby_version: !ruby/object:Gem::Requirement
241
+ none: false
221
242
  requirements:
222
- - - '>='
243
+ - - ! '>='
223
244
  - !ruby/object:Gem::Version
224
245
  version: '0'
246
+ segments:
247
+ - 0
248
+ hash: 3769244612129544255
225
249
  required_rubygems_version: !ruby/object:Gem::Requirement
250
+ none: false
226
251
  requirements:
227
- - - '>='
252
+ - - ! '>='
228
253
  - !ruby/object:Gem::Version
229
254
  version: 1.3.6
230
255
  requirements: []
231
256
  rubyforge_project: active_shipping
232
- rubygems_version: 2.0.3
257
+ rubygems_version: 1.8.23
233
258
  signing_key:
234
- specification_version: 4
259
+ specification_version: 3
235
260
  summary: Shipping API extension for Active Merchant
236
261
  test_files: []
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 4b36c0ee1b7b2ac743a5b950a23f40f006b5fcc9
4
- data.tar.gz: 4a89bf43c4fde9d06f3e34aeee258a9c1990171d
5
- SHA512:
6
- metadata.gz: 1fe9164056568d8913f2f9230fb8a26cdb9f7ff5237c2ab4b7902fcfc1dd0e60b1615a8ccb999754c1f3349b32152fe8bb6f0503afe024452f36944d455d1517
7
- data.tar.gz: 5a55bbce296ed30b42a172b4dc70519cefa409828b68d7018a537ad14ddb6cd7d5e21d8f39d494f789454a4f5049acc539f57b656ef4bcd47ae6c5956ec21bf7