friendly_shipping 0.5.3 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b2b1c8a12304b7f798f0df9210f6e52027bd588b9d612f5f05bf5d102e8db0e8
4
- data.tar.gz: 7813b10233d7364d836d5cd50bb281b9ef43c37d936009f8f400a164d4a30fb1
3
+ metadata.gz: 107fa27ec95b49342549dbf24c53cc483cd05bdfbb64871940036aaac6567eb6
4
+ data.tar.gz: 7dd9c281178e513482ae0cb93787eb551b09e5f697f10d4db50dc2aef21dd4a8
5
5
  SHA512:
6
- metadata.gz: dc7eb250872e158e3ba105168bda09f47328bd2077f758a5e7e746416aa5af071686b992609c1b62d50d399085164999970119ba2d801cbe7d5f99803d675132
7
- data.tar.gz: 4ccb36047bfc9f612af55014b35fb3d72e629277776a65d4c7b4363c056d28d7de16d77564bf371a17ef01002cf04322047b93526f7aa35cc648b6acbfc33567
6
+ metadata.gz: 30c37297f77dc6be490cf9719211780b81b0d91cdc49311b349fc3844a6cbcb5755265e663b80e24a67cda9847e19d328808cec786cf7172d6986ccb75bbf6eb
7
+ data.tar.gz: 022c69680c07d9fed0b2b9afa8093e40b20bb7b441b332acd743078f74db70f3de03227953952de8b08048aa40eb902895529c781b6332036cf4e38ba653804e
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "bundler"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "daily"
data/.gitignore CHANGED
@@ -4,6 +4,7 @@
4
4
  /coverage/
5
5
  /doc/
6
6
  /.idea/
7
+ /.vscode/
7
8
  /pkg/
8
9
  /spec/reports/
9
10
  /tmp/
@@ -4,9 +4,43 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.6.4] - 2021-01-27
8
+
9
+ ### Added
10
+
11
+ - UPS Service: Include negotiated charges for UPS (#119)
12
+ - UPS Service: Include shipment-level itemized charges (#117)
13
+
14
+ ## [0.6.3] - 2020-10-30
15
+
16
+ ### Added
17
+
18
+ - USPS Service: Append HFP (Hold For Pickup) to service code when necessary (#110)
19
+ - USPS Service: Add Priority Cubic shipping method (#113)
20
+
21
+ ### Changed
22
+
23
+ - USPS Service: Refactor to use explicit service codes (#111)
24
+ - USPS Service: Match Priority Express by CLASSID instead of service name (#112)
25
+ - UPS Service: Rename peak surcharge keys to match UPS docs (#114)
26
+
27
+ ## [0.6.2] - 2020-08-12
28
+
29
+ - UPS Service: Be more resilient when UPS does not send a PickupTime element
30
+
31
+ ## [0.6.1] - 2020-03-11
32
+
33
+ - Add Content-Type header to UPS Freight API requests, fixing "Name too long" 500 error responses
34
+
35
+ ## [0.6.0] - 2020-03-11
36
+
37
+ - Changelog additions missed in previous release
38
+
7
39
  ## [0.5.3] - 2020-03-11
8
40
 
9
- - UPS Service: Add support for shipping labels / bills of lading
41
+ - UPS Service: Add support for shipping labels / bills of lading (#92)
42
+ - UPS/USPS Services: Return ApiFailure instead of a string for failed API responses (#95)
43
+ - UPS/USPS Services: Refactor ApiFailure to subclass ApiResponse (#96)
10
44
 
11
45
  ## [0.5.2] - 2020-01-31
12
46
 
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
 
13
13
  spec.summary = "An integration layer for shipping services"
14
14
  spec.description = "Allows you to quote or ship a Physical::Shipment object"
15
- spec.homepage = "https://github.com/friendly_cart/friendly_shipping"
15
+ spec.homepage = "https://github.com/friendlycart/friendly_shipping"
16
16
  spec.license = "MIT"
17
17
 
18
18
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
34
34
  spec.add_development_dependency "dotenv", "~> 2.7"
35
35
  spec.add_development_dependency "factory_bot", "~> 5.0"
36
36
  spec.add_development_dependency "pry", "~> 0.12"
37
- spec.add_development_dependency "rake", "~> 10.0"
37
+ spec.add_development_dependency "rake", ">= 12.3.3"
38
38
  spec.add_development_dependency "rspec", "~> 3.0"
39
39
  spec.add_development_dependency "rspec_junit_formatter", "~> 0.4"
40
40
  spec.add_development_dependency "rubocop", ">= 0.80", "< 1"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'dry/monads/result'
4
+ require 'friendly_shipping/api_failure'
4
5
  require 'rest-client'
5
6
 
6
7
  module FriendlyShipping
@@ -21,19 +22,19 @@ module FriendlyShipping
21
22
 
22
23
  Success(convert_to_friendly_response(http_response))
23
24
  rescue ::RestClient::Exception => e
24
- error_handler.call(e)
25
+ error_handler.call(e, original_request: request, original_response: e.response)
25
26
  end
26
27
 
27
- def post(friendly_shipping_request)
28
+ def post(request)
28
29
  http_response = ::RestClient.post(
29
- friendly_shipping_request.url,
30
- friendly_shipping_request.body,
31
- friendly_shipping_request.headers
30
+ request.url,
31
+ request.body,
32
+ request.headers
32
33
  )
33
34
 
34
35
  Success(convert_to_friendly_response(http_response))
35
36
  rescue ::RestClient::Exception => e
36
- error_handler.call(e)
37
+ error_handler.call(e, original_request: request, original_response: e.response)
37
38
  end
38
39
 
39
40
  def put(request)
@@ -45,13 +46,19 @@ module FriendlyShipping
45
46
 
46
47
  Success(convert_to_friendly_response(http_response))
47
48
  rescue ::RestClient::Exception => e
48
- error_handler.call(e)
49
+ error_handler.call(e, original_request: request, original_response: e.response)
49
50
  end
50
51
 
51
52
  private
52
53
 
53
- def wrap_in_failure(error)
54
- Failure(error)
54
+ def wrap_in_failure(error, original_request: nil, original_response: nil)
55
+ Failure(
56
+ ApiFailure.new(
57
+ error,
58
+ original_request: original_request,
59
+ original_response: original_response
60
+ )
61
+ )
55
62
  end
56
63
 
57
64
  def convert_to_friendly_response(http_response)
@@ -8,11 +8,23 @@ module FriendlyShipping
8
8
  class BadRequestHandler
9
9
  extend Dry::Monads::Result::Mixin
10
10
 
11
- def self.call(error)
11
+ def self.call(error, original_request: nil, original_response: nil)
12
12
  if error.http_code == 400
13
- Failure(BadRequest.new(error))
13
+ Failure(
14
+ ApiFailure.new(
15
+ BadRequest.new(error),
16
+ original_request: original_request,
17
+ original_response: original_response
18
+ )
19
+ )
14
20
  else
15
- Failure(error)
21
+ Failure(
22
+ ApiFailure.new(
23
+ error,
24
+ original_request: original_request,
25
+ original_response: original_response
26
+ )
27
+ )
16
28
  end
17
29
  end
18
30
  end
@@ -75,8 +75,8 @@ module FriendlyShipping
75
75
  "407" => "EXTENDED AREA PICKUP",
76
76
  "410" => "RETURN OF DOCUMENT",
77
77
  "430" => "PEAK SEASON",
78
- "431" => "PEAK SEASON SURCHARGE - LARGE PACK",
79
- "432" => "PEAK SEASON SURCHARGE - ADDITIONAL HANDLING",
78
+ "431" => "LARGE PACKAGE SEASONAL SURCHARGE",
79
+ "432" => "ADDITIONAL HANDLING SEASONAL SURCHARGE",
80
80
  "440" => "SHIP LARGE PACKAGE",
81
81
  "441" => "CARBON NEUTRAL",
82
82
  "442" => "PKG QV IN TRANSIT NOTIFICATION",
@@ -36,6 +36,8 @@ module FriendlyShipping
36
36
  negotiated_rate = ParseMoneyElement.call(
37
37
  rated_shipment.at('NegotiatedRates/NetSummaryCharges/GrandTotal')
38
38
  )&.last
39
+ negotiated_charges = extract_charges(rated_shipment.xpath('NegotiatedRates/ItemizedCharges'))
40
+ itemized_charges = extract_charges(rated_shipment.xpath('ItemizedCharges'))
39
41
 
40
42
  rated_shipment_warnings = rated_shipment.css('RatedShipmentWarning').map { |e| e.text.strip }
41
43
  if rated_shipment_warnings.any? { |e| e.match?(/to Residential/) }
@@ -52,8 +54,10 @@ module FriendlyShipping
52
54
  data: {
53
55
  insurance_price: insurance_price,
54
56
  negotiated_rate: negotiated_rate,
57
+ negotiated_charges: negotiated_charges,
55
58
  days_to_delivery: days_to_delivery,
56
59
  new_address_type: new_address_type,
60
+ itemized_charges: itemized_charges,
57
61
  packages: build_packages(rated_shipment)
58
62
  }.compact
59
63
  )
@@ -64,20 +68,24 @@ module FriendlyShipping
64
68
 
65
69
  def build_packages(rated_shipment)
66
70
  rated_shipment.css('RatedPackage').map do |rated_package|
67
- itemized_charges = rated_package.xpath('ItemizedCharges').map do |element|
68
- ParseMoneyElement.call(element)
69
- end.compact.to_h
70
71
  {
71
72
  transportation_charges: ParseMoneyElement.call(rated_package.at('TransportationCharges')).last,
72
73
  base_service_charge: ParseMoneyElement.call(rated_package.at('BaseServiceCharge')).last,
73
74
  service_options_charges: ParseMoneyElement.call(rated_package.at('ServiceOptionsCharges'))&.last,
74
- itemized_charges: itemized_charges,
75
+ itemized_charges: extract_charges(rated_package.xpath('ItemizedCharges')),
75
76
  total_charges: ParseMoneyElement.call(rated_package.at('TotalCharges')).last,
77
+ negotiated_charges: extract_charges(rated_package.xpath('NegotiatedCharges/ItemizedCharges')),
76
78
  weight: BigDecimal(rated_package.at('Weight').text),
77
79
  billing_weight: BigDecimal(rated_package.at('BillingWeight/Weight').text)
78
80
  }.compact
79
81
  end
80
82
  end
83
+
84
+ def extract_charges(node)
85
+ node.map do |element|
86
+ ParseMoneyElement.call(element)
87
+ end.compact.to_h
88
+ end
81
89
  end
82
90
  end
83
91
  end
@@ -25,7 +25,7 @@ module FriendlyShipping
25
25
  delivery_time = service_summary.at('EstimatedArrival/Time').text
26
26
  delivery = Time.parse("#{delivery_date} #{delivery_time}")
27
27
  pickup_date = service_summary.at('EstimatedArrival/PickupDate').text
28
- pickup_time = service_summary.at('EstimatedArrival/PickupTime').text
28
+ pickup_time = service_summary.at('EstimatedArrival/PickupTime')&.text
29
29
  pickup = Time.parse("#{pickup_date} #{pickup_time}")
30
30
 
31
31
  # Some additional data
@@ -55,7 +55,7 @@ module FriendlyShipping
55
55
  end
56
56
 
57
57
  # Get rates for a shipment
58
- # @param [Physical::Shipment] location The shipment we want to get rates for
58
+ # @param [Physical::Shipment] shipment The shipment we want to get rates for
59
59
  # @param [FriendlyShipping::Services::UpsFreight::RatesOptions] options Options for obtaining rates for this shipment.
60
60
  # @return [Result<ApiResult<Array<Rate>>>] The rates returned from UPS encoded in a
61
61
  # `FriendlyShipping::ApiResult` object.
@@ -69,7 +69,7 @@ module FriendlyShipping
69
69
  end
70
70
 
71
71
  # Get labels for a shipment
72
- # @param [Physical::Shipment] location The shipment we want to get rates for
72
+ # @param [Physical::Shipment] shipment The shipment we want to get rates for
73
73
  # @param [FriendlyShipping::Services::UpsFreight::LabelOptions] options Options for shipping this shipment.
74
74
  # @return [Result<ApiResult<ShipmentInformation>] The information that you need for shipping this shipment.
75
75
  def labels(shipment, options:, debug: false)
@@ -89,6 +89,7 @@ module FriendlyShipping
89
89
  url: url,
90
90
  body: payload.to_json,
91
91
  headers: {
92
+ Content_Type: 'application/json',
92
93
  Accept: 'application/json',
93
94
  Username: login,
94
95
  Password: password,
@@ -6,7 +6,7 @@ module FriendlyShipping
6
6
  class RestfulApiErrorHandler
7
7
  extend Dry::Monads::Result::Mixin
8
8
 
9
- def self.call(error)
9
+ def self.call(error, original_request: nil, original_response: nil)
10
10
  parsed_json = JSON.parse(error.response.body)
11
11
  errors = parsed_json.dig('response', 'errors')
12
12
 
@@ -16,7 +16,13 @@ module FriendlyShipping
16
16
  [status, desc].compact.join(": ").presence || 'UPS could not process the request.'
17
17
  end.join("\n")
18
18
 
19
- Failure(failure_string)
19
+ Failure(
20
+ ApiFailure.new(
21
+ failure_string,
22
+ original_request: original_request,
23
+ original_response: original_response
24
+ )
25
+ )
20
26
  end
21
27
  end
22
28
  end
@@ -54,6 +54,7 @@ module FriendlyShipping
54
54
  SERVICE_NAME_TAG = 'MailService'
55
55
  RATE_TAG = 'Rate'
56
56
  COMMERCIAL_RATE_TAG = 'CommercialRate'
57
+ COMMERCIAL_PLUS_RATE_TAG = 'CommercialPlusRate'
57
58
  CURRENCY = Money::Currency.new('USD').freeze
58
59
 
59
60
  class << self
@@ -82,27 +83,36 @@ module FriendlyShipping
82
83
 
83
84
  # Some USPS services only offer commercial pricing. Unfortunately, USPS then returns a retail rate of 0.
84
85
  # In these cases, return the commercial rate instead of the normal rate.
86
+ #
85
87
  # Some rates are available in both commercial and retail pricing - if we want the commercial pricing here,
86
88
  # we need to specify the commercial_pricing property on the `Physical::Package`.
87
- rate_value = if (package_options.commercial_pricing || rate_node.at(RATE_TAG).text.to_d.zero?) && rate_node.at(COMMERCIAL_RATE_TAG)
88
- rate_node.at(COMMERCIAL_RATE_TAG).text.to_d
89
- else
90
- rate_node.at(RATE_TAG).text.to_d
91
- end
89
+ #
90
+ commercial_rate_requested_or_rate_is_zero = package_options.commercial_pricing || rate_node.at(RATE_TAG).text.to_d.zero?
91
+ commercial_rate_available = rate_node.at(COMMERCIAL_RATE_TAG) || rate_node.at(COMMERCIAL_PLUS_RATE_TAG)
92
+
93
+ rate_value =
94
+ if commercial_rate_requested_or_rate_is_zero && commercial_rate_available
95
+ rate_node.at(COMMERCIAL_RATE_TAG)&.text&.to_d || rate_node.at(COMMERCIAL_PLUS_RATE_TAG).text.to_d
96
+ else
97
+ rate_node.at(RATE_TAG).text.to_d
98
+ end
92
99
 
93
100
  # The rate expressed as a RubyMoney objext
94
101
  rate = Money.new(rate_value * CURRENCY.subunit_to_unit, CURRENCY)
95
102
 
96
- # Which shipping method does this rate belong to? This is trickier than it sounds, because we match
97
- # strings here, and we have a `Priority Mail` and `Priority Mail Express` shipping method.
98
- # If we have multiple matches, we take the longest matching shipping method name so `Express` rates
99
- # do not accidentally get marked as `Priority` only.
100
- possible_shipping_methods = SHIPPING_METHODS.select do |sm|
101
- service_name.tr('-', ' ').upcase.starts_with?(sm.service_code)
102
- end.sort_by do |shipping_method|
103
- shipping_method.name.length
104
- end
105
- shipping_method = possible_shipping_methods.last
103
+ # Which shipping method does this rate belong to? We first try to match a rate to a shipping method
104
+ # by class ID (the CLASSID attribute in the USPS API rate response). Not every shipping method
105
+ # has a class ID defined, and a shipping method can have multiple class IDs (for example, Priority
106
+ # Express has different class IDs for standard, hold for pickup, and Sunday/Holiday delivery).
107
+ #
108
+ # If we don't find a match for class ID, we next try to match a rate to a shipping method using the
109
+ # shipping method's service code. The USPS API rate response includes a name for each rate in the
110
+ # MailService element. We match to see if the name starts with the given value. For example:
111
+ # `Priority Mail Express 2-day™`
112
+ #
113
+ shipping_method =
114
+ SHIPPING_METHODS.detect { |sm| sm.data[:class_ids]&.include?(service_code) } ||
115
+ SHIPPING_METHODS.detect { |sm| service_name.tr('-', ' ').upcase.starts_with?(sm.service_code) }
106
116
 
107
117
  # We find out the box name using a bit of Regex magic using named captures. See the `BOX_REGEX`
108
118
  # constant above.
@@ -115,7 +115,7 @@ module FriendlyShipping
115
115
  # This will likely be somewhat more work in the future.
116
116
  MAIL_CLASSES = {
117
117
  '1' => 'Priority Mail Express',
118
- '2' => 'Priority',
118
+ '2' => 'Priority Mail',
119
119
  '3' => 'First-Class',
120
120
  '6' => 'Package Services'
121
121
  }.freeze
@@ -49,11 +49,13 @@ module FriendlyShipping
49
49
  def service_code
50
50
  return 'ALL' unless shipping_method
51
51
 
52
- if commercial_pricing
53
- "#{shipping_method.service_code} COMMERCIAL"
54
- else
55
- shipping_method.service_code
56
- end
52
+ # Cubic shipping methods don't have HFP or COMMERCIAL modifiers
53
+ return shipping_method.service_code if shipping_method.service_code =~ /CUBIC/
54
+
55
+ service_code = [shipping_method.service_code]
56
+ service_code << 'HFP' if hold_for_pickup
57
+ service_code << 'COMMERCIAL' if commercial_pricing
58
+ service_code.join(' ')
57
59
  end
58
60
  end
59
61
  end
@@ -29,22 +29,33 @@ module FriendlyShipping
29
29
  package_service_retail: 'PACKAGE SERVICE RETAIL'
30
30
  }.freeze
31
31
 
32
+ CLASS_IDS = {
33
+ priority_mail_express: {
34
+ standard: '3',
35
+ hold_for_pickup: '2',
36
+ sunday_holiday_delivery: '23'
37
+ },
38
+ priority_mail_cubic: '999'
39
+ }.freeze
40
+
32
41
  SHIPPING_METHODS = [
33
- 'First-Class',
34
- 'Package Services',
35
- 'Priority',
36
- 'Priority Mail Express',
37
- 'Standard Post',
38
- 'Retail Ground',
39
- 'Media Mail',
40
- 'Library Mail',
41
- ].map do |shipping_method_name|
42
+ ['FIRST CLASS', 'First-Class'],
43
+ ['PACKAGE SERVICES', 'Package Services'],
44
+ ['PRIORITY', 'Priority Mail'],
45
+ ['PRIORITY MAIL EXPRESS', 'Priority Mail Express', CLASS_IDS[:priority_mail_express].values],
46
+ ['PRIORITY MAIL CUBIC', 'Priority Mail Cubic', CLASS_IDS[:priority_mail_cubic]],
47
+ ['STANDARD POST', 'Standard Post'],
48
+ ['RETAIL GROUND', 'Retail Ground'],
49
+ ['MEDIA MAIL', 'Media Mail'],
50
+ ['LIBRARY MAIL', 'Library Mail'],
51
+ ].map do |code, name, class_ids|
42
52
  FriendlyShipping::ShippingMethod.new(
43
53
  origin_countries: [Carmen::Country.coded('US')],
44
- name: shipping_method_name,
45
- service_code: shipping_method_name.tr('-', ' ').upcase,
54
+ name: name,
55
+ service_code: code,
46
56
  domestic: true,
47
- international: false
57
+ international: false,
58
+ data: { class_ids: class_ids }
48
59
  )
49
60
  end.freeze
50
61
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module FriendlyShipping
4
4
  class ShippingMethod
5
- attr_reader :name, :service_code, :carrier, :origin_countries
5
+ attr_reader :name, :service_code, :carrier, :origin_countries, :data
6
6
 
7
7
  # @param [String] name The shipping method's name
8
8
  # @param [String] service_code The shipping method's service code
@@ -11,6 +11,7 @@ module FriendlyShipping
11
11
  # @param [Boolean] multi_package Whether this is a multi-package shipping method
12
12
  # @param [FriendlyShipping::Carrier] carrier This shipping method's carrier
13
13
  # @param [Array] origin_countries Countries this shipping method ships from
14
+ # @param [Hash] data Additional carrier-specific data for this shipping method
14
15
  def initialize(
15
16
  name: nil,
16
17
  service_code: nil,
@@ -18,7 +19,8 @@ module FriendlyShipping
18
19
  international: nil,
19
20
  multi_package: nil,
20
21
  carrier: nil,
21
- origin_countries: []
22
+ origin_countries: [],
23
+ data: {}
22
24
  )
23
25
  @name = name
24
26
  @service_code = service_code
@@ -27,6 +29,7 @@ module FriendlyShipping
27
29
  @multi_package = multi_package
28
30
  @carrier = carrier
29
31
  @origin_countries = origin_countries
32
+ @data = data
30
33
  end
31
34
 
32
35
  def domestic?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FriendlyShipping
4
- VERSION = "0.5.3"
4
+ VERSION = "0.6.4"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: friendly_shipping
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.6.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Meyerhoff
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-11 00:00:00.000000000 Z
11
+ date: 2021-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: data_uri
@@ -166,16 +166,16 @@ dependencies:
166
166
  name: rake
167
167
  requirement: !ruby/object:Gem::Requirement
168
168
  requirements:
169
- - - "~>"
169
+ - - ">="
170
170
  - !ruby/object:Gem::Version
171
- version: '10.0'
171
+ version: 12.3.3
172
172
  type: :development
173
173
  prerelease: false
174
174
  version_requirements: !ruby/object:Gem::Requirement
175
175
  requirements:
176
- - - "~>"
176
+ - - ">="
177
177
  - !ruby/object:Gem::Version
178
- version: '10.0'
178
+ version: 12.3.3
179
179
  - !ruby/object:Gem::Dependency
180
180
  name: rspec
181
181
  requirement: !ruby/object:Gem::Requirement
@@ -275,6 +275,7 @@ extra_rdoc_files: []
275
275
  files:
276
276
  - ".circleci/config.yml"
277
277
  - ".env.template"
278
+ - ".github/dependabot.yml"
278
279
  - ".gitignore"
279
280
  - ".rspec"
280
281
  - ".rubocop-relaxed.yml"
@@ -392,11 +393,11 @@ files:
392
393
  - lib/friendly_shipping/timing.rb
393
394
  - lib/friendly_shipping/types.rb
394
395
  - lib/friendly_shipping/version.rb
395
- homepage: https://github.com/friendly_cart/friendly_shipping
396
+ homepage: https://github.com/friendlycart/friendly_shipping
396
397
  licenses:
397
398
  - MIT
398
399
  metadata: {}
399
- post_install_message:
400
+ post_install_message:
400
401
  rdoc_options: []
401
402
  require_paths:
402
403
  - lib
@@ -412,7 +413,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
412
413
  version: '0'
413
414
  requirements: []
414
415
  rubygems_version: 3.0.3
415
- signing_key:
416
+ signing_key:
416
417
  specification_version: 4
417
418
  summary: An integration layer for shipping services
418
419
  test_files: []