ups 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8bbb686432266ab1abecf098744a453d0c1747eb51a40629335c6f7ce083475b
4
+ data.tar.gz: fbe3f140f3c29831f21cc340fe272916d26efc9365183f01cdd6dceb8c06ea58
5
+ SHA512:
6
+ metadata.gz: 791b16e26b54f1e9f0a02353d73d0a4b84c89f459461334d20292341e4ba707ab569a249e617cdc5eaa69f99964177b358ea2f13bd58295998f5627dec871292
7
+ data.tar.gz: 8483e06fa271c7a514b0d073a9763011f960b7df38d08843ff9d2760dc5fa457b9d5286b429718e39a082ca0002f232a0f63aa72ee0fbbadb7ca847a55c00460
data/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # UPS Shipping Ruby Gem
2
+
3
+ A Ruby wrapper for the UPS Shipping API that allows you to get shipping rates and create shipping labels.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'ups'
11
+ ```
12
+
13
+ ## Configuration
14
+
15
+ ```ruby
16
+ Ups.configure do |config|
17
+ config.client_id = Rails.application.credentials.ups.client_id
18
+ config.client_secret = Rails.application.credentials.ups.client_secret
19
+ config.account_number = Rails.application.credentials.ups.account
20
+ config.sandbox = false
21
+ end
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### Getting Shipping Rates
27
+
28
+ ```ruby
29
+ # Create addresses
30
+ shipper = Ups::Address.new(
31
+ company_name: "ACME Corp",
32
+ address_line_1: "123 Main St",
33
+ city: "Atlanta",
34
+ state: "GA",
35
+ postal_code: "30309",
36
+ country_code: "US",
37
+ phone: "1234567890"
38
+ )
39
+
40
+ ship_to = Ups::Address.new(
41
+ name: "John Doe",
42
+ address_line_1: "456 Oak Ave",
43
+ city: "New York",
44
+ state: "NY",
45
+ postal_code: "10001",
46
+ country_code: "US"
47
+ )
48
+
49
+ # Create package
50
+ package = Ups:Package.new(
51
+ length: 10,
52
+ width: 8,
53
+ height: 6,
54
+ weight: 5
55
+ )
56
+
57
+ # Create rate request
58
+ rate_request = Ups::RateRequest.new(
59
+ shipper: shipper,
60
+ ship_to: ship_to,
61
+ ship_from: shipper,
62
+ service_code: "03" # UPS Ground
63
+ )
64
+ rate_request.add_package(package)
65
+
66
+ # Get rates
67
+ rating_service = Ups::Rating.new(Ups.client)
68
+ rates = rating_service.get_rates(rate_request)
69
+
70
+ rates.each do |rate|
71
+ puts "#{rate[:service_name]}: $#{rate[:total_cost]} #{rate[:currency]}"
72
+ end
73
+ ```
74
+
75
+ ### Creating Shipping Labels
76
+
77
+ ```ruby
78
+ # Create ship request (using same addresses and packages from above)
79
+ ship_request = Ups::ShipRequest.new(
80
+ shipper: shipper,
81
+ ship_to: ship_to,
82
+ ship_from: shipper,
83
+ service_code: "03",
84
+ description: "Test shipment"
85
+ )
86
+ ship_request.add_package(package)
87
+
88
+ # Create shipment
89
+ shipping_service = Ups::Shipping.new(Ups.client)
90
+ result = shipping_service.create_shipment(ship_request)
91
+
92
+ puts "Tracking Number: #{result[:tracking_number]}"
93
+ puts "Total Cost: $#{result[:total_cost]} #{result[:currency]}"
94
+ puts "Label URL: #{result[:label_url]}"
95
+ ```
96
+
97
+ ## Service Codes
98
+
99
+ - 01: UPS Next Day Air
100
+ - 02: UPS 2nd Day Air
101
+ - 03: UPS Ground
102
+ - 07: UPS Worldwide Express
103
+ - 08: UPS Worldwide Expedited
104
+ - 11: UPS Standard
105
+ - 12: UPS 3 Day Select
106
+ - 13: UPS Next Day Air Saver
107
+ - 14: UPS Next Day Air Early AM
data/lib/ups/client.rb ADDED
@@ -0,0 +1,77 @@
1
+ module Ups
2
+ class Client
3
+ include HTTParty
4
+
5
+ attr_reader :config, :access_token
6
+
7
+ def initialize(config)
8
+ @config = config
9
+ @access_token = nil
10
+ authenticate
11
+ end
12
+
13
+ def get(endpoint, options = {})
14
+ request(:get, endpoint, options)
15
+ end
16
+
17
+ def post(endpoint, options = {})
18
+ request(:post, endpoint, options)
19
+ end
20
+
21
+ private
22
+
23
+ def auth_headers
24
+ {
25
+ 'Authorization' => "Basic #{auth_string}",
26
+ 'X-Merchant-Id' => @config.client_id,
27
+ 'Accept' => 'application/json',
28
+ 'Content-Type' => 'application/x-www-form-urlencoded'
29
+ }
30
+ end
31
+
32
+ def auth_string
33
+ @auth_string ||= Base64.strict_encode64("#{@config.client_id}:#{@config.client_secret}")
34
+ end
35
+
36
+ def authenticate
37
+ response = HTTParty.post(@config.oauth_url, { headers: auth_headers, body: 'grant_type=client_credentials' })
38
+
39
+ if response.success?
40
+ @access_token = response.parsed_response['access_token']
41
+ else
42
+ raise AuthenticationError, "Failed to authenticate: #{response.body}"
43
+ end
44
+ end
45
+
46
+ def request_headers
47
+ {
48
+ 'Authorization' => "Bearer #{@access_token}",
49
+ 'Content-Type' => 'application/json'
50
+ }
51
+ end
52
+
53
+ def request(method, endpoint, opts = {})
54
+ url = "#{@config.base_url}#{endpoint}"
55
+ response = HTTParty.send(method, url, { headers: request_headers.merge(opts[:headers] || {}), body: opts[:body]&.to_json })
56
+ handle_response(response)
57
+ end
58
+
59
+ def handle_response(response)
60
+ case response.code
61
+ when 200, 201
62
+ response.parsed_response
63
+ when 401
64
+ authenticate
65
+ raise AuthenticationError, "Authentication failed"
66
+ when 400
67
+ raise BadRequestError, response.body
68
+ when 404
69
+ raise NotFoundError, response.body
70
+ when 500
71
+ raise ServerError, response.body
72
+ else
73
+ raise APIError, "HTTP #{response.code}: #{response.body}"
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,17 @@
1
+ module Ups
2
+ class Configuration
3
+ attr_accessor :client_id, :client_secret, :account_number, :sandbox
4
+
5
+ def initialize
6
+ @sandbox = true
7
+ end
8
+
9
+ def base_url
10
+ @sandbox ? "https://wwwcie.ups.com" : "https://onlinetools.ups.com"
11
+ end
12
+
13
+ def oauth_url
14
+ @sandbox ? "https://wwwcie.ups.com/security/v1/oauth/token" : "https://onlinetools.ups.com/security/v1/oauth/token"
15
+ end
16
+ end
17
+ end
data/lib/ups/errors.rb ADDED
@@ -0,0 +1,10 @@
1
+ module Ups
2
+ class Error < StandardError; end
3
+ class AuthenticationError < Error; end
4
+ class ValidationError < Error; end
5
+ class BadRequestError < Error; end
6
+ class NotFoundError < Error; end
7
+ class ServerError < Error; end
8
+ class APIError < Error; end
9
+ end
10
+
@@ -0,0 +1,20 @@
1
+ module Ups
2
+ class Address
3
+ attr_accessor :name, :company_name, :attention_name, :address_line_1, :address_line_2, :city, :state, :postal_code, :country_code, :phone, :tax_id
4
+
5
+ def initialize(attrs = {})
6
+ attrs.map{|k, v| send("#{k}=", v) if respond_to?("#{k}=") }
7
+ end
8
+
9
+ def validate!
10
+ errors = []
11
+ errors << "Address line 1 is required" if address_line_1.nil? || address_line_1.strip.empty?
12
+ errors << "City is required" if city.nil? || city.strip.empty?
13
+ errors << "State is required" if state.nil? || state.strip.empty?
14
+ errors << "Postal code is required" if postal_code.nil? || postal_code.strip.empty?
15
+ errors << "Country code is required" if country_code.nil? || country_code.strip.empty?
16
+
17
+ raise ValidationError, errors.join(", ") unless errors.empty?
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ module Ups
2
+ class Package
3
+ attr_accessor :description, :packaging_type, :length, :width, :height, :weight, :dimension_unit, :weight_unit, :value
4
+
5
+ def initialize(attrs = {})
6
+ attrs.map{|k,v| send("#{k}=", v) if respond_to?("#{k}=") }
7
+
8
+ @dimension_unit ||= 'IN'
9
+ @weight_unit ||= 'LBS'
10
+ @packaging_type ||= '02' # Customer Supplied Package
11
+ end
12
+
13
+ def validate!
14
+ errors = []
15
+ errors << "Length is required" if length.nil? || length <= 0
16
+ errors << "Width is required" if width.nil? || width <= 0
17
+ errors << "Height is required" if height.nil? || height <= 0
18
+ errors << "Weight is required" if weight.nil? || weight <= 0
19
+
20
+ raise ValidationError, errors.join(", ") unless errors.empty?
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ module Ups
2
+ class RateRequest
3
+ attr_accessor :shipper, :ship_to, :ship_from, :packages, :service_code, :reference, :dcis_type
4
+
5
+ def initialize(attrs = {})
6
+ attrs.map{|k, v| send("#{k}=", v) if respond_to?("#{k}=") }
7
+ @packages = []
8
+ end
9
+
10
+ def add_package(package)
11
+ @packages << package
12
+ end
13
+
14
+ def validate!
15
+ errors = []
16
+ errors << "Shipper is required" if shipper.nil?
17
+ errors << "Ship to address is required" if ship_to.nil?
18
+ errors << "Ship from address is required" if ship_from.nil?
19
+ errors << "At least one package is required" if packages.empty?
20
+
21
+ begin
22
+ shipper&.validate!
23
+ ship_to&.validate!
24
+ ship_from&.validate!
25
+ packages.each(&:validate!)
26
+ rescue ValidationError => e
27
+ errors << e.message
28
+ end
29
+
30
+ raise ValidationError, errors.join(", ") unless errors.empty?
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ module Ups
2
+ class ShipRequest
3
+ attr_accessor :shipper, :ship_to, :ship_from, :packages, :service_code, :reference, :description, :label_format
4
+
5
+ def initialize(attrs = {})
6
+ attrs.map{|k, v| send("#{k}=", v) if respond_to?("#{k}=") }
7
+ @packages = []
8
+ @label_format ||= "GIF"
9
+ end
10
+
11
+ def add_package(package)
12
+ @packages << package
13
+ end
14
+
15
+ def validate!
16
+ errors = []
17
+ errors << "Shipper is required" if shipper.nil?
18
+ errors << "Ship to address is required" if ship_to.nil?
19
+ errors << "Ship from address is required" if ship_from.nil?
20
+ errors << "At least one package is required" if packages.empty?
21
+
22
+ begin
23
+ shipper&.validate!
24
+ ship_to&.validate!
25
+ ship_from&.validate!
26
+ packages.each(&:validate!)
27
+ rescue ValidationError => e
28
+ errors << e.message
29
+ end
30
+
31
+ raise ValidationError, errors.join(", ") unless errors.empty?
32
+ end
33
+ end
34
+ end
data/lib/ups/rating.rb ADDED
@@ -0,0 +1,146 @@
1
+ module Ups
2
+ class Rating
3
+ attr_reader :client, :account_number
4
+
5
+ def initialize(client, account_number: nil)
6
+ @client = client
7
+ @account_number = account_number || client.config.account_number
8
+ end
9
+
10
+ def get_rates(rate_request)
11
+ endpoint = "/api/rating/v2409/Shop"
12
+ payload = build_rate_payload(rate_request)
13
+ response = client.post(endpoint, body: payload)
14
+ parse_rate_response(response)
15
+ end
16
+
17
+ private
18
+
19
+ def build_rate_payload(rate_request)
20
+ {
21
+ RateRequest: {
22
+ Request: {
23
+ RequestOption: "Rate",
24
+ TransactionReference: {
25
+ CustomerContext: rate_request.reference || "Rating Request"
26
+ }
27
+ },
28
+ Shipment: {
29
+ ShipmentDate: Date.today.to_s,
30
+ Shipper: {
31
+ Name: rate_request.shipper.company_name,
32
+ ShipperNumber: client.config.account_number,
33
+ Address: address_hash(rate_request.shipper)
34
+ },
35
+ ShipTo: {
36
+ Name: rate_request.ship_to.company_name || rate_request.ship_to.name,
37
+ Address: address_hash(rate_request.ship_to)
38
+ },
39
+ ShipFrom: {
40
+ Name: rate_request.ship_from.company_name || rate_request.ship_from.name,
41
+ Address: address_hash(rate_request.ship_from)
42
+ },
43
+ PaymentInformation: {
44
+ ShipmentCharge: {
45
+ Type: "01",
46
+ BillShipper: {
47
+ AccountNumber: account_number
48
+ }
49
+ }
50
+ },
51
+ # ShipmentServiceOptions: {
52
+ # DeliveryConfirmation: {
53
+ # DCISType: rate_request.dcis_type || 0 # "2" = Signature Required
54
+ # }
55
+ # },
56
+ Package: rate_request.packages.map { |pkg| package_hash(pkg) },
57
+ Service: {
58
+ Code: rate_request.service_code || "03"
59
+ }
60
+ }
61
+ }
62
+ }
63
+ end
64
+
65
+ def address_hash(address)
66
+ {
67
+ AddressLine: [address.address_line_1, address.address_line_2].compact,
68
+ City: address.city,
69
+ StateProvinceCode: address.state,
70
+ PostalCode: address.postal_code,
71
+ CountryCode: address.country_code
72
+ }
73
+ end
74
+
75
+ def package_hash(package)
76
+ {
77
+ PackagingType: {
78
+ Code: package.packaging_type || "02"
79
+ },
80
+ Dimensions: {
81
+ UnitOfMeasurement: {
82
+ Code: package.dimension_unit || "IN"
83
+ },
84
+ Length: package.length.to_s,
85
+ Width: package.width.to_s,
86
+ Height: package.height.to_s
87
+ },
88
+ PackageWeight: {
89
+ UnitOfMeasurement: {
90
+ Code: package.weight_unit || "LBS"
91
+ },
92
+ Weight: package.weight.to_s
93
+ },
94
+ PackageServiceOptions: {
95
+ DeclaredValue: {
96
+ CurrencyCode: "USD",
97
+ MonetaryValue: package.value
98
+ }
99
+ }
100
+ }
101
+ end
102
+
103
+ def parse_rate_response(response)
104
+ rates = []
105
+
106
+ if response['RateResponse'] && response['RateResponse']['RatedShipment']
107
+ rated_shipments = response['RateResponse']['RatedShipment']
108
+ rated_shipments = [rated_shipments] unless rated_shipments.is_a?(Array)
109
+
110
+ rated_shipments.each do |shipment|
111
+ rates << {
112
+ service_code: shipment['Service']['Code'],
113
+ service_name: get_service_name(shipment['Service']['Code']),
114
+ total: shipment['TotalCharges']['MonetaryValue'].to_f,
115
+ currency: shipment['TotalCharges']['CurrencyCode'],
116
+ transit_time: shipment['GuaranteedDelivery'] ? shipment['GuaranteedDelivery']['BusinessDaysInTransit'] : nil
117
+ }
118
+ end
119
+ end
120
+
121
+ rates
122
+ end
123
+
124
+ def get_service_name(code)
125
+ service_names[code] || "Unknown Service (#{code})"
126
+ end
127
+
128
+ def service_names
129
+ @service_names ||= {
130
+ '01' => 'UPS Next Day Air',
131
+ '02' => 'UPS 2nd Day Air',
132
+ '03' => 'UPS Ground',
133
+ '07' => 'UPS Worldwide Express',
134
+ '08' => 'UPS Worldwide Expedited',
135
+ '11' => 'UPS Standard',
136
+ '12' => 'UPS 3 Day Select',
137
+ '13' => 'UPS Next Day Air Saver',
138
+ '14' => 'UPS Next Day Air Early AM',
139
+ '54' => 'UPS Worldwide Express Plus',
140
+ '59' => 'UPS 2nd Day Air AM',
141
+ '65' => 'UPS Saver'
142
+ }
143
+ end
144
+
145
+ end
146
+ end
@@ -0,0 +1,131 @@
1
+ module Ups
2
+ class Shipping
3
+ attr_reader :client, :account_number
4
+
5
+ def initialize(client, account_number: nil)
6
+ @client = client
7
+ @account_number = account_number || client.config.account_number
8
+ end
9
+
10
+ def create_shipment(ship_request)
11
+ endpoint = "/api/shipments/v2409/ship"
12
+ payload = build_ship_payload(ship_request)
13
+ response = client.post(endpoint, body: payload)
14
+ parse_ship_response(response)
15
+ end
16
+
17
+ private
18
+
19
+ def build_ship_payload(ship_request)
20
+ {
21
+ ShipmentRequest: {
22
+ Request: {
23
+ RequestOption: "nonvalidate",
24
+ TransactionReference: {
25
+ CustomerContext: ship_request.reference || "Shipping Request"
26
+ }
27
+ },
28
+ Shipment: {
29
+ ShipmentDate: Date.today.to_s,
30
+ Description: ship_request.description || "Package",
31
+ Shipper: {
32
+ Name: ship_request.shipper.company_name,
33
+ AttentionName: ship_request.shipper.attention_name,
34
+ TaxIdentificationNumber: ship_request.shipper.tax_id,
35
+ Phone: {
36
+ Number: ship_request.shipper.phone
37
+ },
38
+ ShipperNumber: client.config.account_number,
39
+ Address: address_hash(ship_request.shipper)
40
+ },
41
+ ShipTo: {
42
+ Name: ship_request.ship_to.company_name || ship_request.ship_to.name,
43
+ AttentionName: ship_request.ship_to.attention_name,
44
+ Phone: {
45
+ Number: ship_request.ship_to.phone
46
+ },
47
+ Address: address_hash(ship_request.ship_to)
48
+ },
49
+ ShipFrom: {
50
+ Name: ship_request.ship_from.company_name || ship_request.ship_from.name,
51
+ AttentionName: ship_request.ship_from.attention_name,
52
+ Phone: {
53
+ Number: ship_request.ship_from.phone
54
+ },
55
+ Address: address_hash(ship_request.ship_from)
56
+ },
57
+ PaymentInformation: {
58
+ ShipmentCharge: {
59
+ Type: "01",
60
+ BillShipper: {
61
+ AccountNumber: account_number
62
+ }
63
+ }
64
+ },
65
+ Service: {
66
+ Code: ship_request.service_code || "02",
67
+ Description: "Service Code"
68
+ },
69
+ Package: ship_request.packages.map { |pkg| package_hash(pkg) }
70
+ },
71
+ LabelSpecification: {
72
+ LabelImageFormat: {
73
+ Code: ship_request.label_format || "GIF"
74
+ },
75
+ HTTPUserAgent: "UPS Rate Fetch"
76
+ }
77
+ }
78
+ }
79
+ end
80
+
81
+ def address_hash(address)
82
+ {
83
+ AddressLine: [address.address_line_1, address.address_line_2].compact,
84
+ City: address.city,
85
+ StateProvinceCode: address.state,
86
+ PostalCode: address.postal_code,
87
+ CountryCode: address.country_code
88
+ }
89
+ end
90
+
91
+ def package_hash(package)
92
+ {
93
+ Description: package.description || "Package",
94
+ Packaging: {
95
+ Code: package.packaging_type || "02"
96
+ },
97
+ Dimensions: {
98
+ UnitOfMeasurement: {
99
+ Code: package.dimension_unit || "IN"
100
+ },
101
+ Length: package.length.to_s,
102
+ Width: package.width.to_s,
103
+ Height: package.height.to_s
104
+ },
105
+ PackageWeight: {
106
+ UnitOfMeasurement: {
107
+ Code: package.weight_unit || "LBS"
108
+ },
109
+ Weight: package.weight.to_s
110
+ }
111
+ }
112
+ end
113
+
114
+ def parse_ship_response(response)
115
+ if response['ShipmentResponse'] && response['ShipmentResponse']['ShipmentResults']
116
+ results = response['ShipmentResponse']['ShipmentResults']
117
+ {
118
+ tracking_number: results['ShipmentIdentificationNumber'],
119
+ label: results['PackageResults'][0]['ShippingLabel']['GraphicImage'],
120
+ extension: results['PackageResults'][0]['ShippingLabel']['ImageFormat']['Code'],
121
+ cost: results['ShipmentCharges']['TotalCharges']['MonetaryValue'].to_f,
122
+ currency: results['ShipmentCharges']['TotalCharges']['CurrencyCode'],
123
+ data: response.as_json(except: ["GraphicImage", "HTMLImage"]),
124
+ alerts: response['ShipmentResponse']['Response']['Alert']&.map{|s| s['Description'] }
125
+ }
126
+ else
127
+ raise APIError, "Invalid shipment response: #{response}"
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,4 @@
1
+ module Ups
2
+ VERSION = "0.0.1"
3
+ end
4
+
data/lib/ups.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'httparty'
2
+ require 'json'
3
+ require 'base64'
4
+
5
+ require_relative 'ups/version'
6
+ require_relative 'ups/configuration'
7
+ require_relative 'ups/client'
8
+ require_relative 'ups/rating'
9
+ require_relative 'ups/shipping'
10
+ require_relative 'ups/models/address'
11
+ require_relative 'ups/models/package'
12
+ require_relative 'ups/models/rate_request'
13
+ require_relative 'ups/models/ship_request'
14
+ require_relative 'ups/errors'
15
+
16
+ module Ups
17
+ class << self
18
+ def configuration
19
+ @configuration ||= Configuration.new
20
+ end
21
+
22
+ def configure
23
+ yield(configuration)
24
+ end
25
+
26
+ def client
27
+ @client ||= Client.new(configuration)
28
+ end
29
+ end
30
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ups
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - JD Warren
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-06-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httparty
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.21'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.21'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: webmock
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: vcr
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '6.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '6.0'
83
+ description: A Ruby gem that provides easy access to UPS shipping rates and label
84
+ creation using the new OAuth authentication
85
+ email:
86
+ - johndavid400@gmail.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - README.md
92
+ - lib/ups.rb
93
+ - lib/ups/client.rb
94
+ - lib/ups/configuration.rb
95
+ - lib/ups/errors.rb
96
+ - lib/ups/models/address.rb
97
+ - lib/ups/models/package.rb
98
+ - lib/ups/models/rate_request.rb
99
+ - lib/ups/models/ship_request.rb
100
+ - lib/ups/rating.rb
101
+ - lib/ups/shipping.rb
102
+ - lib/ups/version.rb
103
+ homepage: https://github.com/johndavid400/ups
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubygems_version: 3.4.19
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: Ruby wrapper for UPS Shipping API
126
+ test_files: []