friendly_shipping 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
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