active_shipping 0.9.14 → 0.9.15

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE CHANGED
@@ -13,8 +13,8 @@ included in all copies or substantial portions of the Software.
13
13
 
14
14
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
17
- NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
18
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
19
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown CHANGED
@@ -1,5 +1,7 @@
1
1
  # Active Shipping
2
2
 
3
+ ![Build Status](https://travis-ci.org/Shopify/active_shipping.png)
4
+
3
5
  This library interfaces with the web services of various shipping carriers. The goal is to abstract the features that are most frequently used into a pleasant and consistent Ruby API. Active Shipping is an extension of [Active Merchant][], and as such, it borrows heavily from conventions used in the latter.
4
6
 
5
7
  Active Shipping is currently being used and improved in a production environment for [Shopify][]. Development is being done by the Shopify integrations team (<integrations-team@shopify.com>). Discussion is welcome in the [Active Merchant Google Group][discuss].
@@ -65,6 +65,17 @@ module ActiveMerchant
65
65
  def save_request(r)
66
66
  @last_request = r
67
67
  end
68
+
69
+ def timestamp_from_business_day(days)
70
+ return unless days
71
+ date = DateTime.now.utc
72
+ days.times do
73
+ begin
74
+ date = date + 1
75
+ end until ![0,6].include?(date.wday)
76
+ end
77
+ date
78
+ end
68
79
  end
69
80
  end
70
81
  end
@@ -1,6 +1,7 @@
1
1
  # FedEx module by Jimmy Baker
2
2
  # http://github.com/jimmyebaker
3
3
 
4
+ require 'date'
4
5
  module ActiveMerchant
5
6
  module Shipping
6
7
 
@@ -85,6 +86,45 @@ module ActiveMerchant
85
86
  'express_mps_master' => 'EXPRESS_MPS_MASTER'
86
87
  }
87
88
 
89
+
90
+ TransitTimes = ["UNKNOWN","ONE_DAY","TWO_DAYS","THREE_DAYS","FOUR_DAYS","FIVE_DAYS","SIX_DAYS","SEVEN_DAYS","EIGHT_DAYS","NINE_DAYS","TEN_DAYS","ELEVEN_DAYS","TWELVE_DAYS","THIRTEEN_DAYS","FOURTEEN_DAYS","FIFTEEN_DAYS","SIXTEEN_DAYS","SEVENTEEN_DAYS","EIGHTEEN_DAYS"]
91
+
92
+ # FedEx tracking codes as described in the FedEx Tracking Service WSDL Guide
93
+ # All delays also have been marked as exceptions
94
+ TRACKING_STATUS_CODES = HashWithIndifferentAccess.new({
95
+ 'AA' => :at_airport,
96
+ 'AD' => :at_delivery,
97
+ 'AF' => :at_fedex_facility,
98
+ 'AR' => :at_fedex_facility,
99
+ 'AP' => :at_pickup,
100
+ 'CA' => :canceled,
101
+ 'CH' => :location_changed,
102
+ 'DE' => :exception,
103
+ 'DL' => :delivered,
104
+ 'DP' => :departed_fedex_location,
105
+ 'DR' => :vehicle_furnished_not_used,
106
+ 'DS' => :vehicle_dispatched,
107
+ 'DY' => :exception,
108
+ 'EA' => :exception,
109
+ 'ED' => :enroute_to_delivery,
110
+ 'EO' => :enroute_to_origin_airport,
111
+ 'EP' => :enroute_to_pickup,
112
+ 'FD' => :at_fedex_destination,
113
+ 'HL' => :held_at_location,
114
+ 'IT' => :in_transit,
115
+ 'LO' => :left_origin,
116
+ 'OC' => :order_created,
117
+ 'OD' => :out_for_delivery,
118
+ 'PF' => :plane_in_flight,
119
+ 'PL' => :plane_landed,
120
+ 'PU' => :picked_up,
121
+ 'RS' => :return_to_shipper,
122
+ 'SE' => :exception,
123
+ 'SF' => :at_sort_facility,
124
+ 'SP' => :split_status,
125
+ 'TR' => :transfer
126
+ })
127
+
88
128
  def self.service_name_for_code(service_code)
89
129
  ServiceTypes[service_code] || "FedEx #{service_code.titleize.sub(/Fedex /, '')}"
90
130
  end
@@ -100,7 +140,7 @@ module ActiveMerchant
100
140
  rate_request = build_rate_request(origin, destination, packages, options)
101
141
 
102
142
  response = commit(save_request(rate_request), (options[:test] || false)).gsub(/<(\/)?.*?\:(.*?)>/, '<\1\2>')
103
-
143
+
104
144
  parse_rate_response(origin, destination, packages, response, options)
105
145
  end
106
146
 
@@ -111,7 +151,7 @@ module ActiveMerchant
111
151
  response = commit(save_request(tracking_request), (options[:test] || false)).gsub(/<(\/)?.*?\:(.*?)>/, '<\1\2>')
112
152
  parse_tracking_response(response, options)
113
153
  end
114
-
154
+
115
155
  protected
116
156
  def build_rate_request(origin, destination, packages, options={})
117
157
  imperial = ['US','LR','MM'].include?(origin.country_code(:alpha2))
@@ -133,7 +173,7 @@ module ActiveMerchant
133
173
  root_node << XmlNode.new('VariableOptions', 'SATURDAY_DELIVERY')
134
174
 
135
175
  root_node << XmlNode.new('RequestedShipment') do |rs|
136
- rs << XmlNode.new('ShipTimestamp', Time.now)
176
+ rs << XmlNode.new('ShipTimestamp', ship_timestamp(options[:turn_around_time]))
137
177
  rs << XmlNode.new('DropoffType', options[:dropoff_type] || 'REGULAR_PICKUP')
138
178
  rs << XmlNode.new('PackagingType', options[:packaging_type] || 'YOUR_PACKAGING')
139
179
 
@@ -236,15 +276,22 @@ module ActiveMerchant
236
276
  is_saturday_delivery = rated_shipment.get_text('AppliedOptions').to_s == 'SATURDAY_DELIVERY'
237
277
  service_type = is_saturday_delivery ? "#{service_code}_SATURDAY_DELIVERY" : service_code
238
278
 
239
- currency = handle_uk_currency(rated_shipment.get_text('RatedShipmentDetails/ShipmentRateDetail/TotalNetCharge/Currency').to_s)
279
+ transit_time = rated_shipment.get_text('TransitTime').to_s if service_code == "FEDEX_GROUND"
280
+ max_transit_time = rated_shipment.get_text('MaximumTransitTime').to_s if service_code == "FEDEX_GROUND"
281
+
282
+ delivery_timestamp = rated_shipment.get_text('DeliveryTimestamp').to_s
283
+
284
+ delivery_range = delivery_range_from(transit_time, max_transit_time, delivery_timestamp, options)
285
+
286
+ currency = handle_incorrect_currency_codes(rated_shipment.get_text('RatedShipmentDetails/ShipmentRateDetail/TotalNetCharge/Currency').to_s)
240
287
  rate_estimates << RateEstimate.new(origin, destination, @@name,
241
288
  self.class.service_name_for_code(service_type),
242
289
  :service_code => service_code,
243
290
  :total_price => rated_shipment.get_text('RatedShipmentDetails/ShipmentRateDetail/TotalNetCharge/Amount').to_s.to_f,
244
291
  :currency => currency,
245
292
  :packages => packages,
246
- :delivery_range => [rated_shipment.get_text('DeliveryTimestamp').to_s] * 2)
247
- end
293
+ :delivery_range => delivery_range)
294
+ end
248
295
 
249
296
  if rate_estimates.empty?
250
297
  success = false
@@ -253,7 +300,35 @@ module ActiveMerchant
253
300
 
254
301
  RateResponse.new(success, message, Hash.from_xml(response), :rates => rate_estimates, :xml => response, :request => last_request, :log_xml => options[:log_xml])
255
302
  end
256
-
303
+
304
+ def delivery_range_from(transit_time, max_transit_time, delivery_timestamp, options)
305
+ delivery_range = [delivery_timestamp, delivery_timestamp]
306
+
307
+ #if there's no delivery timestamp but we do have a transit time, use it
308
+ if delivery_timestamp.blank? && transit_time.present?
309
+ transit_range = parse_transit_times([transit_time,max_transit_time.presence || transit_time])
310
+ delivery_range = transit_range.map{|days| business_days_from(ship_date(options[:turn_around_time]), days)}
311
+ end
312
+
313
+ delivery_range
314
+ end
315
+
316
+ def business_days_from(date, days)
317
+ future_date = date
318
+ count = 0
319
+
320
+ while count < days
321
+ future_date += 1.day
322
+ count += 1 if business_day?(future_date)
323
+ end
324
+
325
+ future_date
326
+ end
327
+
328
+ def business_day?(date)
329
+ (1..5).include?(date.wday)
330
+ end
331
+
257
332
  def parse_tracking_response(response, options)
258
333
  xml = REXML::Document.new(response)
259
334
  root_node = xml.elements['TrackReply']
@@ -262,13 +337,32 @@ module ActiveMerchant
262
337
  message = response_message(xml)
263
338
 
264
339
  if success
265
- tracking_number, origin, destination = nil
340
+ tracking_number, origin, destination, status, status_code, status_description = nil
266
341
  shipment_events = []
267
-
342
+
268
343
  tracking_details = root_node.elements['TrackDetails']
269
344
  tracking_number = tracking_details.get_text('TrackingNumber').to_s
270
345
 
346
+ status_code = tracking_details.get_text('StatusCode').to_s
347
+ status_description = tracking_details.get_text('StatusDescription').to_s
348
+ status = TRACKING_STATUS_CODES[status_code]
349
+
350
+ origin_node = tracking_details.elements['OriginLocationAddress']
351
+
352
+ if origin_node
353
+ origin = Location.new(
354
+ :country => origin_node.get_text('CountryCode').to_s,
355
+ :province => origin_node.get_text('StateOrProvinceCode').to_s,
356
+ :city => origin_node.get_text('City').to_s
357
+ )
358
+ end
359
+
271
360
  destination_node = tracking_details.elements['DestinationAddress']
361
+
362
+ if destination_node.nil?
363
+ destination_node = tracking_details.elements['ActualDeliveryAddress']
364
+ end
365
+
272
366
  destination = Location.new(
273
367
  :country => destination_node.get_text('CountryCode').to_s,
274
368
  :province => destination_node.get_text('StateOrProvinceCode').to_s,
@@ -294,17 +388,33 @@ module ActiveMerchant
294
388
  shipment_events << ShipmentEvent.new(description, zoneless_time, location)
295
389
  end
296
390
  shipment_events = shipment_events.sort_by(&:time)
391
+
297
392
  end
298
393
 
299
394
  TrackingResponse.new(success, message, Hash.from_xml(response),
395
+ :carrier => @@name,
300
396
  :xml => response,
301
397
  :request => last_request,
398
+ :status => status,
399
+ :status_code => status_code,
400
+ :status_description => status_description,
302
401
  :shipment_events => shipment_events,
402
+ :origin => origin,
303
403
  :destination => destination,
304
404
  :tracking_number => tracking_number
305
405
  )
306
406
  end
307
-
407
+
408
+ def ship_timestamp(delay_in_hours)
409
+ delay_in_hours ||= 0
410
+ Time.now + delay_in_hours.hours
411
+ end
412
+
413
+ def ship_date(delay_in_hours)
414
+ delay_in_hours ||= 0
415
+ (Time.now + delay_in_hours.hours).to_date
416
+ end
417
+
308
418
  def response_status_node(document)
309
419
  document.elements['/*/Notifications/']
310
420
  end
@@ -322,8 +432,21 @@ module ActiveMerchant
322
432
  ssl_post(test ? TEST_URL : LIVE_URL, request.gsub("\n",''))
323
433
  end
324
434
 
325
- def handle_uk_currency(currency)
326
- currency =~ /UKL/i ? 'GBP' : currency
435
+ def handle_incorrect_currency_codes(currency)
436
+ case currency
437
+ when /UKL/i then 'GBP'
438
+ when /SID/i then 'SGD'
439
+ else currency
440
+ end
441
+ end
442
+
443
+ def parse_transit_times(times)
444
+ results = []
445
+ times.each do |day_count|
446
+ days = TransitTimes.index(day_count.to_s.chomp)
447
+ results << days.to_i
448
+ end
449
+ results
327
450
  end
328
451
  end
329
452
  end
@@ -82,6 +82,7 @@ module ActiveMerchant
82
82
  xml.tag! 'Address1', destination.address1
83
83
  xml.tag! 'Address2', destination.address2 unless destination.address2.blank?
84
84
  xml.tag! 'Address3', destination.address3 unless destination.address3.blank?
85
+ xml.tag! 'Company', destination.company unless destination.company.blank?
85
86
  xml.tag! 'City', destination.city
86
87
  xml.tag! 'State', destination.state unless destination.state.blank?
87
88
  xml.tag! 'Country', destination.country_code
@@ -173,17 +174,6 @@ module ActiveMerchant
173
174
  element.attributes[attribute]
174
175
  end
175
176
  end
176
-
177
- def timestamp_from_business_day(days)
178
- return unless days
179
- date = DateTime.now
180
- days.times do
181
- begin
182
- date = date + 1
183
- end until ![0,6].include?(date.wday)
184
- end
185
- date
186
- end
187
177
  end
188
178
  end
189
179
  end
@@ -26,13 +26,13 @@ module ActiveMerchant
26
26
  :letter_center => "19",
27
27
  :air_service_center => "20"
28
28
  })
29
-
29
+
30
30
  CUSTOMER_CLASSIFICATIONS = HashWithIndifferentAccess.new({
31
31
  :wholesale => "01",
32
32
  :occasional => "03",
33
33
  :retail => "04"
34
34
  })
35
-
35
+
36
36
  # these are the defaults described in the UPS API docs,
37
37
  # but they don't seem to apply them under all circumstances,
38
38
  # so we need to take matters into our own hands
@@ -85,7 +85,15 @@ module ActiveMerchant
85
85
  OTHER_NON_US_ORIGIN_SERVICES = {
86
86
  "07" => "UPS Express"
87
87
  }
88
-
88
+
89
+ TRACKING_STATUS_CODES = HashWithIndifferentAccess.new({
90
+ 'I' => :in_transit,
91
+ 'D' => :delivered,
92
+ 'X' => :exception,
93
+ 'P' => :pickup,
94
+ 'M' => :manifest_pickup
95
+ })
96
+
89
97
  # From http://en.wikipedia.org/w/index.php?title=European_Union&oldid=174718707 (Current as of November 30, 2007)
90
98
  EU_COUNTRY_CODES = ["GB", "AT", "BE", "BG", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT", "RO", "SK", "SI", "ES", "SE"]
91
99
 
@@ -276,7 +284,7 @@ module ActiveMerchant
276
284
  xml.elements.each('/*/RatedShipment') do |rated_shipment|
277
285
  service_code = rated_shipment.get_text('Service/Code').to_s
278
286
  days_to_delivery = rated_shipment.get_text('GuaranteedDaysToDelivery').to_s.to_i
279
- delivery_date = days_to_delivery >= 1 ? days_to_delivery.days.from_now.strftime("%Y-%m-%d") : nil
287
+ days_to_delivery = nil if days_to_delivery == 0
280
288
 
281
289
  rate_estimates << RateEstimate.new(origin, destination, @@name,
282
290
  service_name_for(origin, service_code),
@@ -284,7 +292,7 @@ module ActiveMerchant
284
292
  :currency => rated_shipment.get_text('TotalCharges/CurrencyCode').to_s,
285
293
  :service_code => service_code,
286
294
  :packages => packages,
287
- :delivery_range => [delivery_date])
295
+ :delivery_range => [timestamp_from_business_day(days_to_delivery)])
288
296
  end
289
297
  end
290
298
  RateResponse.new(success, message, Hash.from_xml(response).values.first, :rates => rate_estimates, :xml => response, :request => last_request)
@@ -296,17 +304,39 @@ module ActiveMerchant
296
304
  message = response_message(xml)
297
305
 
298
306
  if success
299
- tracking_number, origin, destination = nil
307
+ tracking_number, origin, destination, status_code, status_description = nil
308
+ delivered, exception = false
309
+ exception_event = nil
300
310
  shipment_events = []
301
-
311
+ status = {}
312
+ scheduled_delivery_date = nil
313
+
302
314
  first_shipment = xml.elements['/*/Shipment']
303
315
  first_package = first_shipment.elements['Package']
304
316
  tracking_number = first_shipment.get_text('ShipmentIdentificationNumber | Package/TrackingNumber').to_s
305
317
 
318
+ # Build status hash
319
+ status_node = first_package.elements['Activity/Status/StatusType']
320
+ status_code = status_node.get_text('Code').to_s
321
+ status_description = status_node.get_text('Description').to_s
322
+ status = TRACKING_STATUS_CODES[status_code]
323
+
324
+ if status_description =~ /out.*delivery/i
325
+ status = :out_for_delivery
326
+ end
327
+
306
328
  origin, destination = %w{Shipper ShipTo}.map do |location|
307
329
  location_from_address_node(first_shipment.elements["#{location}/Address"])
308
330
  end
309
-
331
+
332
+ # Get scheduled delivery date
333
+ unless status == :delivered
334
+ scheduled_delivery_date = parse_ups_datetime({
335
+ :date => first_shipment.get_text('ScheduledDeliveryDate'),
336
+ :time => nil
337
+ })
338
+ end
339
+
310
340
  activities = first_package.get_elements('Activity')
311
341
  unless activities.empty?
312
342
  shipment_events = activities.map do |activity|
@@ -324,7 +354,10 @@ module ActiveMerchant
324
354
 
325
355
  shipment_events = shipment_events.sort_by(&:time)
326
356
 
327
- if origin
357
+ # UPS will sometimes archive a shipment, stripping all shipment activity except for the delivery
358
+ # event (see test/fixtures/xml/delivered_shipment_without_events_tracking_response.xml for an example).
359
+ # This adds an origin event to the shipment activity in such cases.
360
+ if origin && !(shipment_events.count == 1 && status == :delivered)
328
361
  first_event = shipment_events[0]
329
362
  same_country = origin.country_code(:alpha2) == first_event.location.country_code(:alpha2)
330
363
  same_or_blank_city = first_event.location.city.blank? or first_event.location.city == origin.city
@@ -335,16 +368,29 @@ module ActiveMerchant
335
368
  shipment_events.unshift(origin_event)
336
369
  end
337
370
  end
338
- if shipment_events.last.name.downcase == 'delivered'
371
+
372
+ # Has the shipment been delivered?
373
+ if status == :delivered
374
+ if !destination
375
+ destination = shipment_events[-1].location
376
+ end
339
377
  shipment_events[-1] = ShipmentEvent.new(shipment_events.last.name, shipment_events.last.time, destination)
340
378
  end
341
379
  end
342
380
 
343
381
  end
344
382
  TrackingResponse.new(success, message, Hash.from_xml(response).values.first,
383
+ :carrier => @@name,
345
384
  :xml => response,
346
385
  :request => last_request,
386
+ :status => status,
387
+ :status_code => status_code,
388
+ :status_description => status_description,
389
+ :scheduled_delivery_date => scheduled_delivery_date,
347
390
  :shipment_events => shipment_events,
391
+ :delivered => delivered,
392
+ :exception => exception,
393
+ :exception_event => exception_event,
348
394
  :origin => origin,
349
395
  :destination => destination,
350
396
  :tracking_number => tracking_number)
@@ -363,6 +409,18 @@ module ActiveMerchant
363
409
  )
364
410
  end
365
411
 
412
+ def parse_ups_datetime(options = {})
413
+ time, date = options[:time].to_s, options[:date].to_s
414
+ if time.nil?
415
+ hour, minute, second = 0
416
+ else
417
+ hour, minute, second = time.scan(/\d{2}/)
418
+ end
419
+ year, month, day = date[0..3], date[4..5], date[6..7]
420
+
421
+ Time.utc(year, month, day, hour, minute, second)
422
+ end
423
+
366
424
  def response_success?(xml)
367
425
  xml.get_text('/*/Response/ResponseStatusCode').to_s == '1'
368
426
  end
@@ -131,6 +131,18 @@ module ActiveMerchant #:nodoc:
131
131
  string << "\nFax: #{@fax}" unless @fax.blank?
132
132
  string
133
133
  end
134
+
135
+ # Returns the postal code as a properly formatted Zip+4 code, e.g. "77095-2233"
136
+ def zip_plus_4
137
+ if /(\d{5})(\d{4})/ =~ @postal_code
138
+ return "#{$1}-#{$2}"
139
+ elsif /\d{5}-\d{4}/ =~ @postal_code
140
+ return @postal_code
141
+ else
142
+ nil
143
+ end
144
+ end
145
+
134
146
  end
135
147
 
136
148
  end
@@ -2,20 +2,45 @@ module ActiveMerchant #:nodoc:
2
2
  module Shipping
3
3
 
4
4
  class TrackingResponse < Response
5
+ attr_reader :carrier # symbol
6
+ attr_reader :carrier_name # string
7
+ attr_reader :status # symbol
8
+ attr_reader :status_code # string
9
+ attr_reader :status_description #string
10
+ attr_reader :scheduled_delivery_date # time
5
11
  attr_reader :tracking_number # string
6
12
  attr_reader :shipment_events # array of ShipmentEvents in chronological order
7
13
  attr_reader :origin, :destination
8
14
 
9
15
  def initialize(success, message, params = {}, options = {})
16
+ @carrier = options[:carrier].parameterize.to_sym
17
+ @carrier_name = options[:carrier]
18
+ @status = options[:status]
19
+ @status_code = options[:status_code]
20
+ @status_description = options[:status_description]
21
+ @scheduled_delivery_date = options[:scheduled_delivery_date]
10
22
  @tracking_number = options[:tracking_number]
11
23
  @shipment_events = Array(options[:shipment_events])
12
24
  @origin, @destination = options[:origin], options[:destination]
13
25
  super
14
26
  end
15
-
27
+
16
28
  def latest_event
17
29
  @shipment_events.last
18
30
  end
31
+
32
+ def is_delivered?
33
+ @status == :delivered
34
+ end
35
+
36
+ def has_exception?
37
+ @status == :exception
38
+ end
39
+
40
+ alias_method(:exception_event, :latest_event)
41
+ alias_method(:delivered?, :is_delivered?)
42
+ alias_method(:exception?, :has_exception?)
43
+
19
44
  end
20
45
 
21
46
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveShipping
2
- VERSION = "0.9.14"
2
+ VERSION = "0.9.15"
3
3
  end
metadata CHANGED
@@ -1,15 +1,10 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: active_shipping
3
- version: !ruby/object:Gem::Version
4
- hash: 39
3
+ version: !ruby/object:Gem::Version
5
4
  prerelease:
6
- segments:
7
- - 0
8
- - 9
9
- - 14
10
- version: 0.9.14
5
+ version: 0.9.15
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - James MacAulay
14
9
  - Tobi Lutke
15
10
  - Cody Fauser
@@ -17,124 +12,143 @@ authors:
17
12
  autorequire:
18
13
  bindir: bin
19
14
  cert_chain: []
20
-
21
- date: 2012-05-02 00:00:00 -04:00
22
- default_executable:
23
- dependencies:
24
- - !ruby/object:Gem::Dependency
25
- name: activesupport
15
+ date: 2013-05-06 00:00:00.000000000 Z
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
26
18
  prerelease: false
27
- requirement: &id001 !ruby/object:Gem::Requirement
28
- none: false
29
- requirements:
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- hash: 9
33
- segments:
34
- - 2
35
- - 3
36
- - 5
37
- version: 2.3.5
19
+ name: activesupport
38
20
  type: :runtime
39
- version_requirements: *id001
40
- - !ruby/object:Gem::Dependency
41
- name: i18n
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ! '>='
24
+ - !ruby/object:Gem::Version
25
+ version: 2.3.5
26
+ none: false
27
+ requirement: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ! '>='
30
+ - !ruby/object:Gem::Version
31
+ version: 2.3.5
32
+ none: false
33
+ - !ruby/object:Gem::Dependency
42
34
  prerelease: false
43
- requirement: &id002 !ruby/object:Gem::Requirement
44
- none: false
45
- requirements:
46
- - - ">="
47
- - !ruby/object:Gem::Version
48
- hash: 3
49
- segments:
50
- - 0
51
- version: "0"
35
+ name: i18n
52
36
  type: :runtime
53
- version_requirements: *id002
54
- - !ruby/object:Gem::Dependency
55
- name: active_utils
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ none: false
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ none: false
49
+ - !ruby/object:Gem::Dependency
56
50
  prerelease: false
57
- requirement: &id003 !ruby/object:Gem::Requirement
58
- none: false
59
- requirements:
60
- - - ">="
61
- - !ruby/object:Gem::Version
62
- hash: 21
63
- segments:
64
- - 1
65
- - 0
66
- - 1
67
- version: 1.0.1
51
+ name: active_utils
68
52
  type: :runtime
69
- version_requirements: *id003
70
- - !ruby/object:Gem::Dependency
71
- name: builder
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: 1.0.1
58
+ none: false
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: 1.0.1
64
+ none: false
65
+ - !ruby/object:Gem::Dependency
72
66
  prerelease: false
73
- requirement: &id004 !ruby/object:Gem::Requirement
74
- none: false
75
- requirements:
76
- - - ">="
77
- - !ruby/object:Gem::Version
78
- hash: 3
79
- segments:
80
- - 0
81
- version: "0"
67
+ name: builder
82
68
  type: :runtime
83
- version_requirements: *id004
84
- - !ruby/object:Gem::Dependency
85
- name: json
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ none: false
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ none: false
81
+ - !ruby/object:Gem::Dependency
86
82
  prerelease: false
87
- requirement: &id005 !ruby/object:Gem::Requirement
88
- none: false
89
- requirements:
90
- - - ">="
91
- - !ruby/object:Gem::Version
92
- hash: 1
93
- segments:
94
- - 1
95
- - 5
96
- - 1
97
- version: 1.5.1
83
+ name: json
98
84
  type: :runtime
99
- version_requirements: *id005
100
- - !ruby/object:Gem::Dependency
101
- name: rake
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: 1.5.1
90
+ none: false
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: 1.5.1
96
+ none: false
97
+ - !ruby/object:Gem::Dependency
102
98
  prerelease: false
103
- requirement: &id006 !ruby/object:Gem::Requirement
104
- none: false
105
- requirements:
106
- - - ">="
107
- - !ruby/object:Gem::Version
108
- hash: 3
109
- segments:
110
- - 0
111
- version: "0"
99
+ name: rake
112
100
  type: :development
113
- version_requirements: *id006
114
- - !ruby/object:Gem::Dependency
101
+ version_requirements: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ none: false
107
+ requirement: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ none: false
113
+ - !ruby/object:Gem::Dependency
114
+ prerelease: false
115
115
  name: mocha
116
+ type: :development
117
+ version_requirements: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ none: false
123
+ requirement: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ! '>='
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ none: false
129
+ - !ruby/object:Gem::Dependency
116
130
  prerelease: false
117
- requirement: &id007 !ruby/object:Gem::Requirement
118
- none: false
119
- requirements:
120
- - - ">="
121
- - !ruby/object:Gem::Version
122
- hash: 3
123
- segments:
124
- - 0
125
- version: "0"
131
+ name: timecop
126
132
  type: :development
127
- version_requirements: *id007
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ! '>='
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ none: false
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ! '>='
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ none: false
128
145
  description: Get rates and tracking info from various shipping carriers.
129
- email:
146
+ email:
130
147
  - james@shopify.com
131
148
  executables: []
132
-
133
149
  extensions: []
134
-
135
150
  extra_rdoc_files: []
136
-
137
- files:
151
+ files:
138
152
  - lib/active_shipping/shipping/base.rb
139
153
  - lib/active_shipping/shipping/carrier.rb
140
154
  - lib/active_shipping/shipping/carriers/bogus_carrier.rb
@@ -179,41 +193,31 @@ files:
179
193
  - MIT-LICENSE
180
194
  - README.markdown
181
195
  - CHANGELOG
182
- has_rdoc: true
183
196
  homepage: http://github.com/shopify/active_shipping
184
197
  licenses: []
185
-
186
198
  post_install_message:
187
199
  rdoc_options: []
188
-
189
- require_paths:
200
+ require_paths:
190
201
  - lib
191
- required_ruby_version: !ruby/object:Gem::Requirement
192
- none: false
193
- requirements:
194
- - - ">="
195
- - !ruby/object:Gem::Version
196
- hash: 3
197
- segments:
202
+ required_ruby_version: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - ! '>='
205
+ - !ruby/object:Gem::Version
206
+ segments:
198
207
  - 0
199
- version: "0"
200
- required_rubygems_version: !ruby/object:Gem::Requirement
208
+ hash: 3014508717412085176
209
+ version: '0'
201
210
  none: false
202
- requirements:
203
- - - ">="
204
- - !ruby/object:Gem::Version
205
- hash: 23
206
- segments:
207
- - 1
208
- - 3
209
- - 6
211
+ required_rubygems_version: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ! '>='
214
+ - !ruby/object:Gem::Version
210
215
  version: 1.3.6
216
+ none: false
211
217
  requirements: []
212
-
213
218
  rubyforge_project: active_shipping
214
- rubygems_version: 1.6.2
219
+ rubygems_version: 1.8.23
215
220
  signing_key:
216
221
  specification_version: 3
217
222
  summary: Shipping API extension for Active Merchant
218
223
  test_files: []
219
-