friendly_shipping 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +4 -0
  3. data/.rubocop-relaxed.yml +170 -0
  4. data/.rubocop.yml +8 -0
  5. data/Gemfile +7 -4
  6. data/Rakefile +3 -1
  7. data/bin/console +1 -0
  8. data/friendly_shipping.gemspec +6 -4
  9. data/lib/friendly_shipping.rb +5 -1
  10. data/lib/friendly_shipping/bad_request.rb +3 -1
  11. data/lib/friendly_shipping/carrier.rb +6 -0
  12. data/lib/friendly_shipping/label.rb +14 -12
  13. data/lib/friendly_shipping/rate.rb +3 -0
  14. data/lib/friendly_shipping/request.rb +2 -0
  15. data/lib/friendly_shipping/response.rb +2 -0
  16. data/lib/friendly_shipping/services/ship_engine.rb +6 -5
  17. data/lib/friendly_shipping/services/ship_engine/client.rb +12 -10
  18. data/lib/friendly_shipping/services/ship_engine/parse_carrier_response.rb +10 -8
  19. data/lib/friendly_shipping/services/ship_engine/parse_label_response.rb +2 -0
  20. data/lib/friendly_shipping/services/ship_engine/parse_rate_estimate_response.rb +4 -0
  21. data/lib/friendly_shipping/services/ship_engine/parse_void_response.rb +2 -0
  22. data/lib/friendly_shipping/services/ship_engine/serialize_label_shipment.rb +12 -12
  23. data/lib/friendly_shipping/services/ship_engine/serialize_rate_estimate_request.rb +2 -0
  24. data/lib/friendly_shipping/services/ups.rb +67 -0
  25. data/lib/friendly_shipping/services/ups/client.rb +38 -0
  26. data/lib/friendly_shipping/services/ups/parse_rate_response.rb +44 -0
  27. data/lib/friendly_shipping/services/ups/parse_xml_response.rb +40 -0
  28. data/lib/friendly_shipping/services/ups/serialize_access_request.rb +22 -0
  29. data/lib/friendly_shipping/services/ups/serialize_address_snippet.rb +52 -0
  30. data/lib/friendly_shipping/services/ups/serialize_package_node.rb +49 -0
  31. data/lib/friendly_shipping/services/ups/serialize_rating_service_selection_request.rb +87 -0
  32. data/lib/friendly_shipping/services/ups/shipping_methods.rb +107 -0
  33. data/lib/friendly_shipping/shipping_method.rb +13 -2
  34. data/lib/friendly_shipping/version.rb +3 -1
  35. metadata +46 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2ecad34500f6b87c3aa381a2fe93a9f96d5203f1
4
- data.tar.gz: 5c6483ad49ba4ff5cd705dbeb9d44efa3ec865e6
3
+ metadata.gz: c8c1f8749c1f3c1199a3ff41ec10d3a1d92f78dd
4
+ data.tar.gz: f22645471497c2e0350659b4d1dd9cb35e799f22
5
5
  SHA512:
6
- metadata.gz: aeb1401d8bdbda932c92e5b79a9fb7d231e67eb3635210c64c53e90805fffd7a7e98056968289b44197874350543a658327b62d4e69a1d69974d385a6352cf6b
7
- data.tar.gz: 52d31ae21c4878b3e95770f59bad92a7f51619b6fb10ebe03d3295a666b9497f79c2ceb1450d366ce3e2e2e45d03cb7c43e122928e7c34f79d702d7d7003e073
6
+ metadata.gz: fc5b536db417d7d087ae26a3121f61438b510b14b4fc8d94b13d3e25397f7e8f1e1b4c2cdaa18f13a4265c9aad1f6cdd1dcd6a3424bd6822d2ef8dc72a78cb7b
7
+ data.tar.gz: f65dd002fe51c318074e6d4ae841f56d80637f7997d1becfe935e4d75f0ccc9da0d7df8746abbc38d6851068490b21d8182e67d26b912f3a5cc0995855f2fde9
data/.circleci/config.yml CHANGED
@@ -37,6 +37,10 @@ jobs:
37
37
  - ./vendor/bundle
38
38
  key: v1-dependencies-{{ checksum "Gemfile.lock" }}
39
39
 
40
+ - run:
41
+ name: run Rubocop
42
+ command: bundle exec rubocop
43
+
40
44
  # run tests!
41
45
  - run:
42
46
  name: run tests
@@ -0,0 +1,170 @@
1
+ # Relaxed.Ruby.Style
2
+ ## Version 2.2
3
+
4
+ Style/Alias:
5
+ Enabled: false
6
+ StyleGuide: https://relaxed.ruby.style/#stylealias
7
+
8
+ Style/AsciiComments:
9
+ Enabled: false
10
+ StyleGuide: https://relaxed.ruby.style/#styleasciicomments
11
+
12
+ Style/BeginBlock:
13
+ Enabled: false
14
+ StyleGuide: https://relaxed.ruby.style/#stylebeginblock
15
+
16
+ Style/BlockDelimiters:
17
+ Enabled: false
18
+ StyleGuide: https://relaxed.ruby.style/#styleblockdelimiters
19
+
20
+ Style/CommentAnnotation:
21
+ Enabled: false
22
+ StyleGuide: https://relaxed.ruby.style/#stylecommentannotation
23
+
24
+ Style/Documentation:
25
+ Enabled: false
26
+ StyleGuide: https://relaxed.ruby.style/#styledocumentation
27
+
28
+ Layout/DotPosition:
29
+ Enabled: false
30
+ StyleGuide: https://relaxed.ruby.style/#layoutdotposition
31
+
32
+ Style/DoubleNegation:
33
+ Enabled: false
34
+ StyleGuide: https://relaxed.ruby.style/#styledoublenegation
35
+
36
+ Style/EndBlock:
37
+ Enabled: false
38
+ StyleGuide: https://relaxed.ruby.style/#styleendblock
39
+
40
+ Style/FormatString:
41
+ Enabled: false
42
+ StyleGuide: https://relaxed.ruby.style/#styleformatstring
43
+
44
+ Style/IfUnlessModifier:
45
+ Enabled: false
46
+ StyleGuide: https://relaxed.ruby.style/#styleifunlessmodifier
47
+
48
+ Style/Lambda:
49
+ Enabled: false
50
+ StyleGuide: https://relaxed.ruby.style/#stylelambda
51
+
52
+ Style/ModuleFunction:
53
+ Enabled: false
54
+ StyleGuide: https://relaxed.ruby.style/#stylemodulefunction
55
+
56
+ Style/MultilineBlockChain:
57
+ Enabled: false
58
+ StyleGuide: https://relaxed.ruby.style/#stylemultilineblockchain
59
+
60
+ Style/NegatedIf:
61
+ Enabled: false
62
+ StyleGuide: https://relaxed.ruby.style/#stylenegatedif
63
+
64
+ Style/NegatedWhile:
65
+ Enabled: false
66
+ StyleGuide: https://relaxed.ruby.style/#stylenegatedwhile
67
+
68
+ Style/ParallelAssignment:
69
+ Enabled: false
70
+ StyleGuide: https://relaxed.ruby.style/#styleparallelassignment
71
+
72
+ Style/PercentLiteralDelimiters:
73
+ Enabled: false
74
+ StyleGuide: https://relaxed.ruby.style/#stylepercentliteraldelimiters
75
+
76
+ Style/PerlBackrefs:
77
+ Enabled: false
78
+ StyleGuide: https://relaxed.ruby.style/#styleperlbackrefs
79
+
80
+ Style/Semicolon:
81
+ Enabled: false
82
+ StyleGuide: https://relaxed.ruby.style/#stylesemicolon
83
+
84
+ Style/SignalException:
85
+ Enabled: false
86
+ StyleGuide: https://relaxed.ruby.style/#stylesignalexception
87
+
88
+ Style/SingleLineBlockParams:
89
+ Enabled: false
90
+ StyleGuide: https://relaxed.ruby.style/#stylesinglelineblockparams
91
+
92
+ Style/SingleLineMethods:
93
+ Enabled: false
94
+ StyleGuide: https://relaxed.ruby.style/#stylesinglelinemethods
95
+
96
+ Layout/SpaceBeforeBlockBraces:
97
+ Enabled: false
98
+ StyleGuide: https://relaxed.ruby.style/#layoutspacebeforeblockbraces
99
+
100
+ Layout/SpaceInsideParens:
101
+ Enabled: false
102
+ StyleGuide: https://relaxed.ruby.style/#layoutspaceinsideparens
103
+
104
+ Style/SpecialGlobalVars:
105
+ Enabled: false
106
+ StyleGuide: https://relaxed.ruby.style/#stylespecialglobalvars
107
+
108
+ Style/StringLiterals:
109
+ Enabled: false
110
+ StyleGuide: https://relaxed.ruby.style/#stylestringliterals
111
+
112
+ Style/TrailingCommaInArguments:
113
+ Enabled: false
114
+ StyleGuide: https://relaxed.ruby.style/#styletrailingcommainarguments
115
+
116
+ Style/TrailingCommaInArrayLiteral:
117
+ Enabled: false
118
+ StyleGuide: https://relaxed.ruby.style/#styletrailingcommainarrayliteral
119
+
120
+ Style/TrailingCommaInHashLiteral:
121
+ Enabled: false
122
+ StyleGuide: https://relaxed.ruby.style/#styletrailingcommainhashliteral
123
+
124
+ Style/SymbolArray:
125
+ Enabled: false
126
+ StyleGuide: http://relaxed.ruby.style/#stylesymbolarray
127
+
128
+ Style/WhileUntilModifier:
129
+ Enabled: false
130
+ StyleGuide: https://relaxed.ruby.style/#stylewhileuntilmodifier
131
+
132
+ Style/WordArray:
133
+ Enabled: false
134
+ StyleGuide: https://relaxed.ruby.style/#stylewordarray
135
+
136
+ Lint/AmbiguousRegexpLiteral:
137
+ Enabled: false
138
+ StyleGuide: https://relaxed.ruby.style/#lintambiguousregexpliteral
139
+
140
+ Lint/AssignmentInCondition:
141
+ Enabled: false
142
+ StyleGuide: https://relaxed.ruby.style/#lintassignmentincondition
143
+
144
+ Metrics/AbcSize:
145
+ Enabled: false
146
+
147
+ Metrics/BlockNesting:
148
+ Enabled: false
149
+
150
+ Metrics/ClassLength:
151
+ Enabled: false
152
+
153
+ Metrics/ModuleLength:
154
+ Enabled: false
155
+
156
+ Metrics/CyclomaticComplexity:
157
+ Enabled: false
158
+
159
+ Metrics/LineLength:
160
+ Enabled: false
161
+
162
+ Metrics/MethodLength:
163
+ Enabled: false
164
+
165
+ Metrics/ParameterLists:
166
+ Enabled: false
167
+
168
+ Metrics/PerceivedComplexity:
169
+ Enabled: false
170
+
data/.rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+
4
+ inherit_from:
5
+ - .rubocop-relaxed.yml
6
+
7
+ Metrics/BlockLength:
8
+ Enabled: false
data/Gemfile CHANGED
@@ -1,13 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
6
 
5
7
  # Specify your gem's dependencies in friendly_shipping.gemspec
6
8
  gemspec
7
9
 
8
- gem 'vcr'
9
- gem 'webmock'
10
- gem 'pry'
11
10
  gem 'dotenv'
12
11
  gem 'factory_bot'
12
+ gem 'pry'
13
13
  gem 'rspec_junit_formatter'
14
+ gem 'rubocop'
15
+ gem 'vcr'
16
+ gem 'webmock'
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rspec/core/rake_task"
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
8
+ task default: :spec
data/bin/console CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "bundler/setup"
4
5
  require "friendly_shipping"
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
1
2
 
2
- lib = File.expand_path("../lib", __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require "friendly_shipping/version"
5
6
 
@@ -21,11 +22,12 @@ Gem::Specification.new do |spec|
21
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
23
  spec.require_paths = ["lib"]
23
24
 
24
- spec.add_runtime_dependency "physical", "~> 0.3.0"
25
- spec.add_runtime_dependency "rest-client", "~> 2.0"
26
- spec.add_runtime_dependency "dry-monads", "~> 1.0"
27
25
  spec.add_runtime_dependency "data_uri", "~> 0.0.3"
26
+ spec.add_runtime_dependency "dry-monads", "~> 1.0"
28
27
  spec.add_runtime_dependency "money", ">= 6.0.0"
28
+ spec.add_runtime_dependency "nokogiri", ">= 1.6"
29
+ spec.add_runtime_dependency "physical", "~> 0.3.0"
30
+ spec.add_runtime_dependency "rest-client", "~> 2.0"
29
31
  spec.required_ruby_version = '>= 2.4'
30
32
 
31
33
  spec.add_development_dependency "bundler"
@@ -1,13 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "physical"
2
4
 
3
5
  require "friendly_shipping/version"
4
6
  require "friendly_shipping/request"
5
7
  require "friendly_shipping/response"
6
8
  require "friendly_shipping/carrier"
7
- require "friendly_shipping/services/ship_engine"
8
9
  require "friendly_shipping/shipping_method"
9
10
  require "friendly_shipping/label"
10
11
  require "friendly_shipping/rate"
11
12
 
13
+ require "friendly_shipping/services/ship_engine"
14
+ require "friendly_shipping/services/ups"
15
+
12
16
  module FriendlyShipping
13
17
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
 
3
5
  module FriendlyShipping
@@ -16,7 +18,7 @@ module FriendlyShipping
16
18
  parsed_body = JSON.parse(response.body)
17
19
  messages = parsed_body.fetch('errors')&.map { |e| e.fetch('message') }
18
20
  messages&.join(', ')
19
- rescue JSON::ParserError, KeyError => _error
21
+ rescue JSON::ParserError, KeyError => _e
20
22
  nil
21
23
  end
22
24
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FriendlyShipping
2
4
  class Carrier
3
5
  attr_reader :id, :name, :code, :shipping_methods, :balance, :data
@@ -10,5 +12,9 @@ module FriendlyShipping
10
12
  @balance = balance
11
13
  @data = data
12
14
  end
15
+
16
+ def ==(other)
17
+ id == other.id
18
+ end
13
19
  end
14
20
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FriendlyShipping
2
4
  class Label
3
5
  attr_reader :id,
@@ -13,18 +15,18 @@ module FriendlyShipping
13
15
  :original_response
14
16
 
15
17
  def initialize(
16
- id: nil,
17
- shipment_id: nil,
18
- tracking_number: nil,
19
- service_code: nil,
20
- label_href: nil,
21
- label_format: nil,
22
- label_data: nil,
23
- shipment_cost: nil,
24
- data: {},
25
- original_request: nil,
26
- original_response: nil
27
- )
18
+ id: nil,
19
+ shipment_id: nil,
20
+ tracking_number: nil,
21
+ service_code: nil,
22
+ label_href: nil,
23
+ label_format: nil,
24
+ label_data: nil,
25
+ shipment_cost: nil,
26
+ data: {},
27
+ original_request: nil,
28
+ original_response: nil
29
+ )
28
30
  @id = id
29
31
  @shipment_id = shipment_id
30
32
  @tracking_number = tracking_number
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FriendlyShipping
2
4
  class Rate
3
5
  class NoAmountsGiven < StandardError; end
@@ -35,6 +37,7 @@ module FriendlyShipping
35
37
 
36
38
  def total_amount
37
39
  raise NoAmountsGiven if amounts.empty?
40
+
38
41
  amounts.map { |_name, amount| amount }.sum
39
42
  end
40
43
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FriendlyShipping
2
4
  class Request
3
5
  attr_reader :url, :body, :headers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FriendlyShipping
2
4
  class Response
3
5
  attr_reader :status, :body, :headers
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require 'dry/monads/result'
3
4
  require 'friendly_shipping/services/ship_engine/client'
@@ -15,9 +16,9 @@ module FriendlyShipping
15
16
  API_PATHS = {
16
17
  carriers: "carriers",
17
18
  labels: "labels"
18
- }
19
+ }.freeze
19
20
 
20
- def initialize(token:, test: true, client: self.class::Client)
21
+ def initialize(token:, test: true, client: Client)
21
22
  @token = token
22
23
  @test = test
23
24
  @client = client
@@ -36,11 +37,11 @@ module FriendlyShipping
36
37
  def rate_estimates(shipment, carriers)
37
38
  request = FriendlyShipping::Request.new(
38
39
  url: API_BASE + 'rates/estimate',
39
- body: SerializeRateEstimateRequest.(shipment: shipment, carriers: carriers).to_json,
40
+ body: SerializeRateEstimateRequest.call(shipment: shipment, carriers: carriers).to_json,
40
41
  headers: request_headers
41
42
  )
42
43
  client.post(request).fmap do |response|
43
- ParseRateEstimateResponse.(response: response, request: request, carriers: carriers)
44
+ ParseRateEstimateResponse.call(response: response, request: request, carriers: carriers)
44
45
  end
45
46
  end
46
47
 
@@ -51,7 +52,7 @@ module FriendlyShipping
51
52
  headers: request_headers
52
53
  )
53
54
  client.post(request).fmap do |response|
54
- ParseLabelResponse.(request: request, response: response)
55
+ ParseLabelResponse.call(request: request, response: response)
55
56
  end
56
57
  end
57
58
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/monads/result'
2
4
  require 'friendly_shipping/bad_request'
3
5
  require 'rest-client'
@@ -14,8 +16,8 @@ module FriendlyShipping
14
16
  )
15
17
 
16
18
  Success(convert_to_friendly_response(http_response))
17
- rescue ::RestClient::Exception => error
18
- Failure(error)
19
+ rescue ::RestClient::Exception => e
20
+ Failure(e)
19
21
  end
20
22
 
21
23
  def post(friendly_shipping_request)
@@ -26,11 +28,11 @@ module FriendlyShipping
26
28
  )
27
29
 
28
30
  Success(convert_to_friendly_response(http_response))
29
- rescue ::RestClient::Exception => error
30
- if error.http_code == 400
31
- Failure(BadRequest.new(error))
31
+ rescue ::RestClient::Exception => e
32
+ if e.http_code == 400
33
+ Failure(BadRequest.new(e))
32
34
  else
33
- Failure(error)
35
+ Failure(e)
34
36
  end
35
37
  end
36
38
 
@@ -42,11 +44,11 @@ module FriendlyShipping
42
44
  )
43
45
 
44
46
  Success(convert_to_friendly_response(http_response))
45
- rescue ::RestClient::Exception => error
46
- if error.http_code == 400
47
- Failure(BadRequest.new(error))
47
+ rescue ::RestClient::Exception => e
48
+ if e.http_code == 400
49
+ Failure(BadRequest.new(e))
48
50
  else
49
- Failure(error)
51
+ Failure(e)
50
52
  end
51
53
  end
52
54
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
 
3
5
  module FriendlyShipping
@@ -31,14 +33,14 @@ module FriendlyShipping
31
33
  private
32
34
 
33
35
  def parse_shipping_method(carrier, shipping_method_data)
34
- FriendlyShipping::ShippingMethod.new(
35
- carrier: carrier,
36
- name: shipping_method_data["name"],
37
- service_code: shipping_method_data["service_code"],
38
- domestic: shipping_method_data["domestic"],
39
- international: shipping_method_data["international"],
40
- multi_package: shipping_method_data["is_multi_package_supported"]
41
- )
36
+ FriendlyShipping::ShippingMethod.new(
37
+ carrier: carrier,
38
+ name: shipping_method_data["name"],
39
+ service_code: shipping_method_data["service_code"],
40
+ domestic: shipping_method_data["domestic"],
41
+ international: shipping_method_data["international"],
42
+ multi_package: shipping_method_data["is_multi_package_supported"]
43
+ )
42
44
  end
43
45
  end
44
46
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
  require 'data_uri'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
  require 'money'
3
5
 
@@ -11,8 +13,10 @@ module FriendlyShipping
11
13
  parsed_json.map do |rate|
12
14
  carrier = carriers.detect { |c| c.id == rate['carrier_id'] }
13
15
  next unless carrier
16
+
14
17
  shipping_method = carrier.shipping_methods.detect { |sm| sm.service_code == rate['service_code'] }
15
18
  next unless shipping_method
19
+
16
20
  amounts = get_amounts(rate)
17
21
  FriendlyShipping::Rate.new(
18
22
  shipping_method: shipping_method,
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'friendly_shipping/bad_request'
2
4
 
3
5
  module FriendlyShipping
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FriendlyShipping
2
4
  module Services
3
5
  class ShipEngine
@@ -20,7 +22,7 @@ module FriendlyShipping
20
22
  }
21
23
  }
22
24
  if shipment.options[:carrier_id]
23
- shipment_hash[:shipment].merge!(carrier_id: shipment.options[:carrier_id])
25
+ shipment_hash[:shipment][:carrier_id] = shipment.options[:carrier_id]
24
26
  end
25
27
  shipment_hash
26
28
  end
@@ -46,25 +48,23 @@ module FriendlyShipping
46
48
  packages.map do |package|
47
49
  package_hash = serialize_weight(package.weight)
48
50
  if package.container.properties[:usps_label_messages]
49
- package_hash.merge!(label_messages: package.container.properties[:usps_label_messages])
51
+ package_hash[:label_messages] = package.container.properties[:usps_label_messages]
50
52
  end
51
53
  package_code = package.container.properties[:usps_package_code]
52
54
  if package_code
53
- package_hash.merge!(package_code: package_code)
55
+ package_hash[:package_code] = package_code
54
56
  else
55
- package_hash.merge!(
56
- dimensions: {
57
- unit: 'inch',
58
- width: package.container.width.convert_to(:inches).value.to_f.round(2),
59
- length: package.container.length.convert_to(:inches).value.to_f.round(2),
60
- height: package.container.height.convert_to(:inches).value.to_f.round(2)
61
- }
62
- )
57
+ package_hash[:dimensions] = {
58
+ unit: 'inch',
59
+ width: package.container.width.convert_to(:inches).value.to_f.round(2),
60
+ length: package.container.length.convert_to(:inches).value.to_f.round(2),
61
+ height: package.container.height.convert_to(:inches).value.to_f.round(2)
62
+ }
63
63
  end
64
64
  package_hash
65
65
  end
66
66
  end
67
-
67
+
68
68
  def serialize_weight(weight)
69
69
  ounces = weight.convert_to(:ounce).value.to_f
70
70
  {
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FriendlyShipping
2
4
  module Services
3
5
  class ShipEngine
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/monads/result'
4
+ require 'friendly_shipping/services/ups/client'
5
+ require 'friendly_shipping/services/ups/serialize_access_request'
6
+ require 'friendly_shipping/services/ups/serialize_rating_service_selection_request'
7
+ require 'friendly_shipping/services/ups/parse_rate_response'
8
+ require 'friendly_shipping/services/ups/shipping_methods'
9
+
10
+ module FriendlyShipping
11
+ module Services
12
+ class Ups
13
+ include Dry::Monads::Result::Mixin
14
+
15
+ attr_reader :test, :key, :login, :password, :client
16
+
17
+ CARRIER = FriendlyShipping::Carrier.new(
18
+ id: 'ups',
19
+ name: 'United Parcel Service',
20
+ code: 'ups',
21
+ shipping_methods: SHIPPING_METHODS
22
+ )
23
+
24
+ TEST_URL = 'https://wwwcie.ups.com'
25
+ LIVE_URL = 'https://onlinetools.ups.com'
26
+
27
+ RESOURCES = {
28
+ rates: '/ups.app/xml/Rate'
29
+ }.freeze
30
+
31
+ def initialize(key:, login:, password:, test: true, client: Client)
32
+ @key = key
33
+ @login = login
34
+ @password = password
35
+ @test = test
36
+ @client = client
37
+ end
38
+
39
+ def carriers
40
+ Success([CARRIER])
41
+ end
42
+
43
+ def rate_estimates(shipment, _carriers)
44
+ rate_request_xml = SerializeRatingServiceSelectionRequest.call(shipment: shipment)
45
+ url = base_url + RESOURCES[:rates]
46
+ request = FriendlyShipping::Request.new(
47
+ url: url,
48
+ body: access_request_xml + rate_request_xml
49
+ )
50
+
51
+ client.post(request).bind do |response|
52
+ ParseRateResponse.call(response: response, request: request, shipment: shipment)
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def access_request_xml
59
+ SerializeAccessRequest.call(key: key, login: login, password: password)
60
+ end
61
+
62
+ def base_url
63
+ test ? TEST_URL : LIVE_URL
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/monads/result'
4
+ require 'friendly_shipping/bad_request'
5
+ require 'rest-client'
6
+
7
+ module FriendlyShipping
8
+ module Services
9
+ class Ups
10
+ class Client
11
+ extend Dry::Monads::Result::Mixin
12
+ class << self
13
+ def post(friendly_shipping_request)
14
+ http_response = ::RestClient.post(
15
+ friendly_shipping_request.url,
16
+ friendly_shipping_request.body,
17
+ friendly_shipping_request.headers
18
+ )
19
+
20
+ Success(convert_to_friendly_response(http_response))
21
+ rescue ::RestClient::Exception => e
22
+ Failure(e)
23
+ end
24
+
25
+ private
26
+
27
+ def convert_to_friendly_response(http_response)
28
+ FriendlyShipping::Response.new(
29
+ status: http_response.code,
30
+ body: http_response.body,
31
+ headers: http_response.headers
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'friendly_shipping/services/ups/parse_xml_response'
4
+
5
+ module FriendlyShipping
6
+ module Services
7
+ class Ups
8
+ class ParseRateResponse
9
+ def self.call(request:, response:, shipment:)
10
+ parsing_result = ParseXMLResponse.call(response.body, 'RatingServiceSelectionResponse')
11
+ parsing_result.fmap do |xml|
12
+ xml.root.css('> RatedShipment').map do |rated_shipment|
13
+ service_code = rated_shipment.at('Service/Code').text
14
+ shipping_method = CARRIER.shipping_methods.detect do |sm|
15
+ sm.service_code == service_code && shipment.origin.country.in?(sm.origin_countries)
16
+ end
17
+ days_to_delivery = rated_shipment.at('GuaranteedDaysToDelivery').text.to_i
18
+ currency = Money::Currency.new(rated_shipment.at('TotalCharges/CurrencyCode').text)
19
+ total_cents = rated_shipment.at('TotalCharges/MonetaryValue').text.to_d * currency.subunit_to_unit
20
+ insurance_price = rated_shipment.at('ServiceOptionsCharges/MonetaryValue').text.to_f
21
+ negotiated_rate = rated_shipment.at(
22
+ 'NegotiatedRates/NetSummaryCharges/GrandTotal/MonetaryValue'
23
+ )&.text.to_f
24
+
25
+ FriendlyShipping::Rate.new(
26
+ shipping_method: shipping_method,
27
+ amounts: { total: Money.new(total_cents, currency) },
28
+ warnings: [rated_shipment.at("RatedShipmentWarning")&.text].compact,
29
+ errors: [],
30
+ data: {
31
+ insurance_price: insurance_price,
32
+ negotiated_rate: negotiated_rate,
33
+ days_to_delivery: days_to_delivery
34
+ },
35
+ original_request: request,
36
+ original_response: response
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class Ups
6
+ class ParseXMLResponse
7
+ extend Dry::Monads::Result::Mixin
8
+ SUCCESSFUL_RESPONSE_STATUS_CODE = '1'
9
+
10
+ class << self
11
+ def call(response_body, expected_root_tag)
12
+ xml = Nokogiri.XML(response_body)
13
+ if xml.root.nil? || xml.root.name != expected_root_tag
14
+ Failure('Invalid document')
15
+ end
16
+ if request_successful?(xml)
17
+ Success(xml)
18
+ else
19
+ Failure(error_message(xml))
20
+ end
21
+ rescue Nokogiri::XML::SyntaxError => e
22
+ Failure(e)
23
+ end
24
+
25
+ private
26
+
27
+ def request_successful?(xml)
28
+ xml.root.at('Response/ResponseStatusCode').text == SUCCESSFUL_RESPONSE_STATUS_CODE
29
+ end
30
+
31
+ def error_message(xml)
32
+ status = xml.root.at_xpath('Response/ResponseStatusDescription')&.text
33
+ desc = xml.root.at_xpath('Response/Error/ErrorDescription')&.text
34
+ [status, desc].compact.join(": ").presence || 'UPS could not process the request.'
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nokogiri'
4
+
5
+ module FriendlyShipping
6
+ module Services
7
+ class Ups
8
+ class SerializeAccessRequest
9
+ def self.call(login:, password:, key:)
10
+ xml_builder = Nokogiri::XML::Builder.new do |xml|
11
+ xml.AccessRequest do
12
+ xml.AccessLicenseNumber(key)
13
+ xml.UserId(login)
14
+ xml.Password(password)
15
+ end
16
+ end
17
+ xml_builder.to_xml
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class Ups
6
+ class SerializeAddressSnippet
7
+ class << self
8
+ def call(xml:, location:)
9
+ if location.company_name # Is this a business address?
10
+ name = location.company_name[0..34]
11
+ attention_name = location.name
12
+ else
13
+ name = location.name
14
+ attention_name = nil
15
+ end
16
+
17
+ # UPS wants a different main Name tag when it's the shipper
18
+ if xml.parent.name == "Shipper"
19
+ xml.Name(name)
20
+ else
21
+ xml.CompanyName(name)
22
+ end
23
+
24
+ if attention_name
25
+ xml.AttentionName(attention_name)
26
+ end
27
+
28
+ xml.PhoneNumber(location.phone) if location.phone
29
+
30
+ xml.Address do
31
+ xml.AddressLine1(location.address1) if location.address1
32
+ xml.AddressLine2(location.address2) if location.address2
33
+
34
+ xml.City(location.city) if location.city
35
+ xml.PostalCode(location.zip) if location.zip
36
+
37
+ # StateProvinceCode required for negotiated rates but not otherwise, for some reason
38
+ xml.StateProvinceCode(location.region.code) if location.region
39
+ xml.CountryCode(location.country.code) if location.country
40
+
41
+ # Quote residential rates by default. If UPS doesn't know if the address is residential or
42
+ # commercial, it will quote a residential rate by default. Even with this flag being set,
43
+ # if UPS knows the address is commercial it will quote a commercial rate.
44
+ #
45
+ xml.ResidentialAddressIndicator
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class Ups
6
+ class SerializePackageNode
7
+ def self.call(xml:, package:)
8
+ xml.Package do
9
+ xml.PackagingType do
10
+ xml.Code('02')
11
+ end
12
+
13
+ if package.dimensions.all? { |dim| !dim.value.zero? && !dim.value.infinite? }
14
+ xml.Dimensions do
15
+ xml.UnitOfMeasurement do
16
+ xml.Code('IN')
17
+ end
18
+ xml.Length(package.length.convert_to(:inches).value.to_f.round(3))
19
+ xml.Width(package.width.convert_to(:inches).value.to_f.round(3))
20
+ xml.Height(package.height.convert_to(:inches).value.to_f.round(3))
21
+ end
22
+ end
23
+
24
+ xml.PackageWeight do
25
+ xml.UnitOfMeasurement do
26
+ xml.Code('LBS')
27
+ end
28
+
29
+ xml.Weight([package.weight.convert_to(:pounds).value.to_f.round(2).ceil, 1].max)
30
+ end
31
+
32
+ if package.properties[:shipper_release]
33
+ xml.PackageServiceOptions do
34
+ xml.ShipperReleaseIndicator
35
+ end
36
+ end
37
+
38
+ Array.wrap(package.properties[:reference_numbers]).each do |reference_number_info|
39
+ xml.ReferenceNumber do
40
+ xml.Code(reference_number_info[:code] || "")
41
+ xml.Value(reference_number_info[:value])
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nokogiri'
4
+ require 'friendly_shipping/services/ups/serialize_address_snippet'
5
+ require 'friendly_shipping/services/ups/serialize_package_node'
6
+
7
+ module FriendlyShipping
8
+ module Services
9
+ class Ups
10
+ class SerializeRatingServiceSelectionRequest
11
+ PICKUP_CODES = HashWithIndifferentAccess.new(
12
+ daily_pickup: "01",
13
+ customer_counter: "03",
14
+ one_time_pickup: "06",
15
+ on_call_air: "07",
16
+ suggested_retail_rates: "11",
17
+ letter_center: "19",
18
+ air_service_center: "20"
19
+ )
20
+
21
+ CUSTOMER_CLASSIFICATIONS = HashWithIndifferentAccess.new(
22
+ shipper_number: "00",
23
+ daily_rates: "01",
24
+ retail_rates: "04",
25
+ regional_rates: "05",
26
+ general_rates: "06",
27
+ standard_rates: "53"
28
+ )
29
+
30
+ def self.call(shipment:)
31
+ shipper = shipment.options[:shipper] || shipment.origin
32
+ pickup_type = PICKUP_CODES[shipment.options[:pickup_type] || :daily_pickup]
33
+ customer_classification = CUSTOMER_CLASSIFICATIONS[
34
+ shipment.options[:customer_classification] || :daily_rates
35
+ ]
36
+ origin_account = shipment.options[:origin_account]
37
+ destination_account = shipment.options[:destination_account]
38
+
39
+ xml_builder = Nokogiri::XML::Builder.new do |xml|
40
+ xml.RatingServiceSelectionRequest do
41
+ xml.Request do
42
+ xml.RequestAction('Rate')
43
+ xml.RequestOption('Shop')
44
+ xml.SubVersion('1707')
45
+ end
46
+
47
+ xml.PickupType do
48
+ xml.Code(pickup_type)
49
+ end
50
+ xml.CustomerClassification do
51
+ xml.Code(customer_classification)
52
+ end
53
+
54
+ xml.Shipment do
55
+ # not implemented: Shipment/Description element
56
+ xml.Shipper do
57
+ SerializeAddressSnippet.call(xml: xml, location: shipper)
58
+
59
+ xml.ShipperNumber(origin_account)
60
+ end
61
+
62
+ xml.ShipTo do
63
+ SerializeAddressSnippet.call(xml: xml, location: shipment.destination)
64
+
65
+ if destination_account
66
+ xml.ShipperAssignedIdentificationNumber(destination_account)
67
+ end
68
+ end
69
+
70
+ if shipper != shipment.origin
71
+ xml.ShipFrom do
72
+ SerializeAddressSnippet.call(xml: xml, location: shipment.origin)
73
+ end
74
+ end
75
+
76
+ shipment.packages.each do |package|
77
+ SerializePackageNode.call(xml: xml, package: package)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ xml_builder.to_xml
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class Ups
6
+ EU_COUNTRIES = %w(
7
+ AT BE BG CY CZ DE DK EE ES FI FR GB GR HR HU IE IT LT LU LV MT NL PO PT RO SE SI SK
8
+ ).map { |country_code| Carmen::Country.coded(country_code) }
9
+
10
+ class << self
11
+ private
12
+
13
+ def countries_by_code(code)
14
+ all_countries = Carmen::Country.all
15
+ covered_countries = EU_COUNTRIES + %w(US PR CA PL MX).map do |country_code|
16
+ Carmen::Country.coded(country_code)
17
+ end
18
+ other_countries = Carmen::Country.all - covered_countries
19
+ case code
20
+ when 'EU' then EU_COUNTRIES
21
+ when 'OTHER' then other_countries
22
+ when 'ALL' then all_countries
23
+ else
24
+ [Carmen::Country.coded(code)]
25
+ end
26
+ end
27
+ end
28
+
29
+ SHIPPING_METHODS = [
30
+ ['US', 'international', 'UPS Standard', '11'],
31
+ ['US', 'international', 'UPS Worldwide Express', '07'],
32
+ ['US', 'international', 'UPS Worldwide Expedited', '08'],
33
+ ['US', 'international', 'UPS Worldwide Express Plus', '54'],
34
+ ['US', 'international', 'UPS Worldwide Saver', '65'],
35
+ ['US', 'domestic', 'UPS 2nd Day Air', '02'],
36
+ ['US', 'domestic', 'UPS 2nd Day Air A.M.', '59'],
37
+ ['US', 'domestic', 'UPS 3 Day Select', '12'],
38
+ ['US', 'domestic', 'UPS Ground', '03'],
39
+ ['US', 'domestic', 'UPS Next Day Air', '01'],
40
+ ['US', 'domestic', 'UPS Next Day Air Early', '14'],
41
+ ['US', 'domestic', 'UPS Next Day Air Saver', '13'],
42
+ ['CA', 'domestic', 'UPS Expedited Canadian domestic shipments', '02'],
43
+ ['CA', 'domestic', 'UPS Express Saver Canadian domestic shipments', '13'],
44
+ ['CA', 'domestic', 'UPS 3 Day Select Shipments originating in Canada to CA and US 48', '12'],
45
+ ['CA', 'international', 'UPS 3 Day Select Shipments originating in Canada to CA and US 48', '12'],
46
+ ['CA', 'domestic', 'UPS Access Point Economy Canadian domestic shipments', '70'],
47
+ ['CA', 'domestic', 'UPS Express Canadian domestic shipments', '01'],
48
+ ['CA', 'domestic', 'UPS Express Early Canadian domestic shipments', '14'],
49
+ ['CA', 'international', 'UPS Express Saver International shipments originating in Canada', '65'],
50
+ ['CA', 'international', 'UPS Standard Shipments originating in Canada (Domestic and Int’l)', '11'],
51
+ ['CA', 'domestic', 'UPS Standard Shipments originating in Canada (Domestic and Int’l)', '11'],
52
+ ['CA', 'international', 'UPS Worldwide Expedited International shipments originating in Canada', '08'],
53
+ ['CA', 'international', 'UPS Worldwide Express International shipments originating in Canada', '07'],
54
+ ['CA', 'international', 'UPS Worldwide Express Plus International shipments originating in Canada', '54'],
55
+ ['CA', 'international', 'UPS Express Early Shipments originating in Canada to CA and US 48', '54'],
56
+ ['CA', 'domestic', 'UPS Express Early Shipments originating in Canada to CA and US 48', '54'],
57
+ ['EU', 'domestic', 'UPS Access Point Economy Shipments within the European Union', '70'],
58
+ ['EU', 'international', 'UPS Expedited Shipments originating in the European Union', '08'],
59
+ ['EU', 'international', 'UPS Express Shipments originating in the European Union', '07'],
60
+ ['EU', 'international', 'UPS Standard Shipments originating in the European Union', '11'],
61
+ ['EU', 'international', 'UPS Worldwide Express Plus Shipments originating in the European Union', '54'],
62
+ ['EU', 'international', 'UPS Worldwide Saver Shipments originating in the European Union', '65'],
63
+ ['MX', 'domestic', 'UPS Access Point Economy Shipments within Mexico', '70'],
64
+ ['MX', 'international', 'UPS Expedited Shipments originating in Mexico', '08'],
65
+ ['MX', 'international', 'UPS Express Shipments originating in Mexico', '07'],
66
+ ['MX', 'international', 'UPS Standard Shipments originating in Mexico', '11'],
67
+ ['MX', 'international', 'UPS Worldwide Express Plus Shipments originating in Mexico', '54'],
68
+ ['MX', 'international', 'UPS Worldwide Saver Shipments originating in Mexico', '65'],
69
+ ['PL', 'domestic', 'UPS Access Point Economy Polish domestic shipments', '70'],
70
+ ['PL', 'domestic', 'UPS Today Dedicated Courier Polish domestic shipments', '83'],
71
+ ['PL', 'domestic', 'UPS Today Express Polish domestic shipments', '85'],
72
+ ['PL', 'domestic', 'UPS Today Express Saver Polish domestic shipments', '86'],
73
+ ['PL', 'domestic', 'UPS Today Standard Polish domestic shipments', '82'],
74
+ ['PL', 'international', 'UPS Expedited Shipments originating in Poland', '08'],
75
+ ['PL', 'international', 'UPS Express Shipments originating in Poland', '07'],
76
+ ['PL', 'international', 'UPS Express Plus Shipments originating in Poland', '54'],
77
+ ['PL', 'international', 'UPS Express Saver Shipments originating in Poland', '65'],
78
+ ['PL', 'international', 'UPS Standard Shipments originating in Poland', '11'],
79
+ ['PR', 'domestic', 'UPS 2nd Day Air', '02'],
80
+ ['PR', 'domestic', 'UPS Ground', '03'],
81
+ ['PR', 'domestic', 'UPS Next Day Air', '01'],
82
+ ['PR', 'domestic', 'UPS Next Day Air Early', '14'],
83
+ ['PR', 'international', 'UPS Worldwide Expedited', '08'],
84
+ ['PR', 'international', 'UPS Worldwide Express', '07'],
85
+ ['PR', 'international', 'UPS Worldwide Express Plus', '54'],
86
+ ['PR', 'international', 'UPS Worldwide Saver', '65'],
87
+ ['DE', 'domestic', 'UPS Express 12:00 German domestic shipments', '74'],
88
+ ['OTHER', 'domestic', 'UPS Express', '07'],
89
+ ['OTHER', 'domestic', 'UPS Standard', '11'],
90
+ ['OTHER', 'international', 'UPS Worldwide Expedited', '08'],
91
+ ['OTHER', 'international', 'UPS Worldwide Express Plus', '54'],
92
+ ['OTHER', 'international', 'UPS Worldwide Saver', '65'],
93
+ ['ALL', 'international', 'UPS Worldwide Express Freight', '96'],
94
+ ['ALL', 'international', 'UPS Worldwide Express Freight Midday', '71']
95
+ ].freeze.map do |origin_country_code, dom_or_intl, name, code|
96
+ FriendlyShipping::ShippingMethod.new(
97
+ name: name,
98
+ service_code: code,
99
+ domestic: dom_or_intl == 'domestic',
100
+ international: dom_or_intl == 'international',
101
+ origin_countries: countries_by_code(origin_country_code),
102
+ multi_package: true
103
+ ).freeze
104
+ end
105
+ end
106
+ end
107
+ end
@@ -1,14 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FriendlyShipping
2
4
  class ShippingMethod
3
- attr_reader :name, :service_code, :carrier
5
+ attr_reader :name, :service_code, :carrier, :origin_countries
4
6
 
5
- def initialize(name: nil, service_code: nil, domestic: nil, international: nil, multi_package: nil, carrier: nil)
7
+ def initialize(
8
+ name: nil,
9
+ service_code: nil,
10
+ domestic: nil,
11
+ international: nil,
12
+ multi_package: nil,
13
+ carrier: nil,
14
+ origin_countries: []
15
+ )
6
16
  @name = name
7
17
  @service_code = service_code
8
18
  @domestic = domestic
9
19
  @international = international
10
20
  @multi_package = multi_package
11
21
  @carrier = carrier
22
+ @origin_countries = origin_countries
12
23
  end
13
24
 
14
25
  def domestic?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FriendlyShipping
2
- VERSION = "0.2.3"
4
+ VERSION = "0.2.4"
3
5
  end
metadata CHANGED
@@ -1,85 +1,99 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: friendly_shipping
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Meyerhoff
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-06-27 00:00:00.000000000 Z
11
+ date: 2019-07-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: physical
14
+ name: data_uri
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.3.0
19
+ version: 0.0.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.3.0
26
+ version: 0.0.3
27
27
  - !ruby/object:Gem::Dependency
28
- name: rest-client
28
+ name: dry-monads
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.0'
33
+ version: '1.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.0'
40
+ version: '1.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: dry-monads
42
+ name: money
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '1.0'
47
+ version: 6.0.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '1.0'
54
+ version: 6.0.0
55
55
  - !ruby/object:Gem::Dependency
56
- name: data_uri
56
+ name: nokogiri
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: physical
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - "~>"
60
74
  - !ruby/object:Gem::Version
61
- version: 0.0.3
75
+ version: 0.3.0
62
76
  type: :runtime
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
- version: 0.0.3
82
+ version: 0.3.0
69
83
  - !ruby/object:Gem::Dependency
70
- name: money
84
+ name: rest-client
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - ">="
87
+ - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: 6.0.0
89
+ version: '2.0'
76
90
  type: :runtime
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - ">="
94
+ - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: 6.0.0
96
+ version: '2.0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: bundler
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -132,6 +146,8 @@ files:
132
146
  - ".circleci/config.yml"
133
147
  - ".gitignore"
134
148
  - ".rspec"
149
+ - ".rubocop-relaxed.yml"
150
+ - ".rubocop.yml"
135
151
  - ".travis.yml"
136
152
  - CODE_OF_CONDUCT.md
137
153
  - Gemfile
@@ -156,6 +172,15 @@ files:
156
172
  - lib/friendly_shipping/services/ship_engine/parse_void_response.rb
157
173
  - lib/friendly_shipping/services/ship_engine/serialize_label_shipment.rb
158
174
  - lib/friendly_shipping/services/ship_engine/serialize_rate_estimate_request.rb
175
+ - lib/friendly_shipping/services/ups.rb
176
+ - lib/friendly_shipping/services/ups/client.rb
177
+ - lib/friendly_shipping/services/ups/parse_rate_response.rb
178
+ - lib/friendly_shipping/services/ups/parse_xml_response.rb
179
+ - lib/friendly_shipping/services/ups/serialize_access_request.rb
180
+ - lib/friendly_shipping/services/ups/serialize_address_snippet.rb
181
+ - lib/friendly_shipping/services/ups/serialize_package_node.rb
182
+ - lib/friendly_shipping/services/ups/serialize_rating_service_selection_request.rb
183
+ - lib/friendly_shipping/services/ups/shipping_methods.rb
159
184
  - lib/friendly_shipping/shipping_method.rb
160
185
  - lib/friendly_shipping/version.rb
161
186
  homepage: https://github.com/friendly_cart/friendly_shipping