ups_time_in_transit_plus 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OGMxNTA2ZGEwNWFlYWJjZGU4YWQxYmE5NmZmYzU3ZGRhZWNmYzI3OA==
5
+ data.tar.gz: !binary |-
6
+ ZjY5YTczZWUzYmMyOTdhZGJmYzhmNzc4NDk3NTYyNTFlNTAwNTlkZg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZjljNWFhNzJjODdhZTU1Y2U1OGNhZDQwYjUwNGNhZTBkM2MxYzUwMWRjMzJj
10
+ NmQxZGM3MWI3N2JjNjY2ZTgyY2I5N2VjZDhlN2M5M2UwNmJhNjNlOWFhMjE3
11
+ MjYyY2JjZWE5NjQwMmQwZTBmZmQ2MDdkYjgyZWZkNjc2NDNhZWQ=
12
+ data.tar.gz: !binary |-
13
+ MjNjYzE1ZjY3ZmNmNTU1Y2UyYWU5N2MzYTc3OTJkNGM2ZWVjMjcyYmQ3YTFk
14
+ NzA3MGNmZGY3N2UyYWIzNzFiY2RhODc0MWM0YWQ2OTE5YTZjN2U1MmVkOTQx
15
+ ZTI4OTMzZjZjYjNlMDYzNjYyMjJjZDVkMTQ3MGVhMzBhMDhhMDc=
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2010 Joseph Stelmach
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
data/Manifest ADDED
@@ -0,0 +1,6 @@
1
+ LICENSE
2
+ Manifest
3
+ README
4
+ Rakefile
5
+ lib/ups_time_in_transit.rb
6
+ ups_time_in_transit.gemspec
data/README ADDED
@@ -0,0 +1,58 @@
1
+ ups_time_in_transit is an easy to use interface to the UPS Time in Transit API.
2
+
3
+ == installation ==
4
+ gem install ups_time_in_transit
5
+
6
+ == usage ==
7
+
8
+ # define your access options. These are options that won't change
9
+ # between requests. Take care to specify the proper UPS url:
10
+ # 'https://wwwcie.ups.com/ups.app/xml/TimeInTransit' for testing and
11
+ # development, and 'https://onlinetools.ups.com/ups.app/xml/TimeInTransit'
12
+ # for production.
13
+ access_options = {
14
+ :url => 'https://wwwcie.ups.com/ups.app/xml/TimeInTransit',
15
+ :access_license_number => 'foo',
16
+ :user_id => 'bar',
17
+ :password => 'baz',
18
+ :order_cutoff_time => 17 ,
19
+ :sender_city => 'Hoboken',
20
+ :sender_state => 'NJ',
21
+ :sender_country_code => 'US',
22
+ :sender_zip => '07030'}
23
+
24
+ # It's best to store these these options in a yaml file and load them
25
+ # into a map when you need them:
26
+ yaml = YAML.load_file("#{RAILS_ROOT}/config/ups_time_in_transit.yml")
27
+ access_options = yaml[RAILS_ENV].inject({}){|h,(k, v)| h[k.to_sym] = v; h}
28
+
29
+ # create an api instance with your access options
30
+ time_in_transit_api = UPS::TimeInTransit.new(access_options)
31
+
32
+ # for each request, generate a map of request options describing
33
+ # the shipment and where it's going
34
+ request_options = {
35
+ :total_packages => 1,
36
+ :unit_of_measurement => 'LBS',
37
+ :weight => 10,
38
+ :city => 'Newark',
39
+ :state => 'DE',
40
+ :zip => '19711',
41
+ :country_code => 'US'}
42
+
43
+ # request the map of delivery types (overnight, ground, etc.) to the expected
44
+ # delivery date for that type. Be sure to rescue any errors.
45
+ begin
46
+ delivery_dates = time_in_transit_api.request(request_options)
47
+ rescue => error
48
+ puts error.inspect
49
+ end
50
+
51
+ == changelog ==
52
+
53
+ v.0.1.1
54
+ some erroneous documentation
55
+
56
+ v.0.1.0
57
+ initial release
58
+
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('ups_time_in_transit', '0.1.1') do |p|
6
+ p.description = "Provides an easy to use interface to the UPS time in transit API"
7
+ p.url = "http://github.com/joestelmach/ups_time_in_transit"
8
+ p.author = "Joe Stelmach"
9
+ p.email = "joestelmach @nospam@ gmail.com"
10
+ p.ignore_pattern = ["tmp/*", "script/*"]
11
+ p.runtime_dependencies = ['activesupport']
12
+ end
@@ -0,0 +1,257 @@
1
+ require 'date'
2
+ require 'rexml/document'
3
+ require 'net/http'
4
+ require 'net/https'
5
+ require 'rubygems'
6
+ require 'active_support'
7
+
8
+ module UPS
9
+ # Provides a simple api to to ups's time in transit service.
10
+ class TimeInTransit
11
+ XPCI_VERSION = '1.0002'
12
+ DEFAULT_CUTOFF_TIME = 18
13
+ DEFAULT_TIMEOUT = 30
14
+ DEFAULT_RETRY_COUNT = 3
15
+ DEFAULT_COUNTRY_CODE = 'US'
16
+ DEFAULT_UNIT_OF_MEASUREMENT = 'LBS'
17
+
18
+ # Creates a TimeInTransit instance based on the given hash of access
19
+ # options The following access options are available and are required
20
+ # unless a default value is specified:
21
+ #
22
+ # [<tt>:url</tt>]
23
+ # The ups api url to use
24
+ #
25
+ # [<tt>:access_license_number</tt>]
26
+ # Your ups license number
27
+ #
28
+ # [<tt>:user_id</tt>]
29
+ # Your ups user id
30
+ #
31
+ # [<tt>password</tt>]
32
+ # Your ups password
33
+ #
34
+ # [<tt>:order_cutoff_time</tt>]
35
+ # Your own arbitrary cutoff time that is some time before the actual ups cutoff
36
+ # time. Requests made after this time will use the following day as the send
37
+ # date (or the following monday if the request is made on a weekend or on a
38
+ # friday after this time.)
39
+ #
40
+ # [<tt>:sender_city</tt>]
41
+ # The city you are shipping from
42
+ #
43
+ # [<tt>:sender_state</tt>]
44
+ # The state you are shipping from
45
+ #
46
+ # [<tt>:sender_zip</tt>]
47
+ # The zip code you are shipping from
48
+ #
49
+ # [<tt>:sender_country_code</tt>]
50
+ # The country you are shipping from (defaults to 'US')
51
+ #
52
+ # [<tt>:retry_count</tt>]
53
+ # The number of times you would like to retry when a connection
54
+ # fails (defaults to 3)
55
+ #
56
+ # [<tt>:timeout</tt>]
57
+ # The number of seconds you would like to wait for a response before
58
+ # giving up (defaults to 30)
59
+ #
60
+ def initialize(access_options)
61
+ @order_cutoff_time = access_options[:order_cutoff_time] || DEFAULT_CUTOFF_TIME
62
+ @url = access_options[:url]
63
+ @timeout = access_options[:timeout] || DEFAULT_TIMEOUT
64
+ @retry_count = access_options[:retry_count] || DEFAULT_CUTOFF_TIME
65
+
66
+ @access_xml = generate_xml({
67
+ :AccessRequest => {
68
+ :AccessLicenseNumber => access_options[:access_license_number],
69
+ :UserId => access_options[:user_id],
70
+ :Password => access_options[:password]
71
+ }
72
+ })
73
+
74
+ @transit_from_attributes = {
75
+ :AddressArtifactFormat => {
76
+ :PoliticalDivision2 => access_options[:sender_city],
77
+ :PoliticalDivision1 => access_options[:sender_state],
78
+ :CountryCode => access_options[:sender_country_code] || DEFAULT_COUNTRY_CODE,
79
+ :PostcodePrimaryLow => access_options[:sender_zip]
80
+ }
81
+ }
82
+ end
83
+
84
+ # Requests time in transit information based on the given hash of options:
85
+ #
86
+ # [<tt>:total_packages</tt>]
87
+ # the number of packages in the shipment (defaults to 1)
88
+ #
89
+ # [<tt>:unit_of_measurement</tt>]
90
+ # the unit of measurement to use (defaults to 'LBS')
91
+ #
92
+ # [<tt>:weight</tt>]
93
+ # the weight of the shipment in the given units
94
+ #
95
+ # [<tt>:city</tt>]
96
+ # the city you are shipping to
97
+ #
98
+ # [<tt>:state</tt>]
99
+ # the state you are shipping to
100
+ #
101
+ # [<tt>:zip</tt>]
102
+ # the zip code you are shipping to
103
+ #
104
+ # [<tt>:country_code</tt>]
105
+ # the country you are shipping to (defaults to 'US')
106
+ #
107
+ # An error will be raised if the request is unsuccessful.
108
+ #
109
+ def request(options)
110
+
111
+ # build our request xml
112
+ pickup_date = calculate_pickup_date
113
+ options[:pickup_date] = pickup_date.strftime('%Y%m%d')
114
+ xml = @access_xml + generate_xml(build_transit_attributes(options))
115
+
116
+ # attempt the request in a timeout
117
+ delivery_dates = {}
118
+ attempts = 0
119
+ begin
120
+ Timeout.timeout(@timeout) do
121
+ response = send_request(@url, xml)
122
+ delivery_dates = response_to_map(response)
123
+ end
124
+
125
+ # We can only attempt to recover from Timeout errors, all other errors
126
+ # should be raised back to the user
127
+ rescue Timeout::Error => error
128
+ if(attempts < @retry_count)
129
+ attempts += 1
130
+ retry
131
+
132
+ else
133
+ raise error
134
+ end
135
+ end
136
+
137
+ delivery_dates
138
+ end
139
+
140
+ private
141
+
142
+ # calculates the next available pickup date based on the current time and the
143
+ # configured order cutoff time
144
+ def calculate_pickup_date
145
+ now = Time.now
146
+ day_of_week = now.strftime('%w').to_i
147
+ in_weekend = [6,0].include?(day_of_week)
148
+ in_friday_after_cutoff = day_of_week == 5 and now.hour > @order_cutoff_time
149
+ # Rails.logger.debug "Time now is: #{now}\nOrder cutoff time is: #{@order_cutoff_time}\n"
150
+
151
+ # If we're in a weekend (6 is Sat, 0 is Sun,) or we're in Friday after
152
+ # the cutoff time, then our ship date will move
153
+ if(in_weekend or in_friday_after_cutoff)
154
+ pickup_date = now.next_week
155
+
156
+ # if we're in another weekday but after the cutoff time, our ship date
157
+ # moves to tomorrow
158
+ elsif(now.hour > @order_cutoff_time)
159
+ # Rails.logger.debug "\nSetting the time in transit to tomorrow #{now.tomorrow}, since now.hour > @order_cutoff_time = #{now.hour > @order_cutoff_time}\n"
160
+ pickup_date = now.tomorrow
161
+ else
162
+ pickup_date = now
163
+ end
164
+ # Rails.logger.debug "UPS pickup_date is: #{pickup_date} and the time now is #{now}"
165
+ return pickup_date
166
+ end
167
+
168
+ # Builds a hash of transit request attributes based on the given values
169
+ def build_transit_attributes(options)
170
+ # set defaults if none given
171
+ options[:total_packages] = 1 unless options[:total_packages]
172
+
173
+ # convert all options to string values
174
+ options.each_value {|option| option = options.to_s}
175
+
176
+ transit_attributes = {
177
+ :TimeInTransitRequest => {
178
+ :Request => {
179
+ :RequestAction => 'TimeInTransit',
180
+ :TransactionReference => {
181
+ :XpciVersion => XPCI_VERSION
182
+ }
183
+ },
184
+ :TotalPackagesInShipment => options[:total_packages],
185
+ :ShipmentWeight => {
186
+ :UnitOfMeasurement => {
187
+ :Code => options[:unit_of_measurement] || DEFAULT_UNIT_OF_MEASUREMENT
188
+ },
189
+ :Weight => options[:weight],
190
+ },
191
+ :PickupDate => options[:pickup_date],
192
+ :TransitFrom => @transit_from_attributes,
193
+ :TransitTo => {
194
+ :AddressArtifactFormat => {
195
+ :PoliticalDivision2 => options[:city],
196
+ :PoliticalDivision1 => options[:state],
197
+ :CountryCode => options[:country_code] || DEFAULT_COUNTRY_CODE,
198
+ :PostcodePrimaryLow => options[:zip],
199
+ }
200
+ }
201
+ }
202
+ }
203
+ end
204
+
205
+ # generates an xml document for the given attributes
206
+ def generate_xml(attributes)
207
+ xml = REXML::Document.new
208
+ xml << REXML::XMLDecl.new
209
+ emit(attributes, xml)
210
+ xml.root.add_attribute("xml:lang", "en-US")
211
+ xml.to_s
212
+ end
213
+
214
+ # recursively emits xml nodes under the given node for values in the given hash
215
+ def emit(attributes, node)
216
+ attributes.each do |k,v|
217
+ child_node = REXML::Element.new(k.to_s, node)
218
+ (v.respond_to? 'each_key') ? emit(v, child_node) : child_node.add_text(v.to_s)
219
+ end
220
+ end
221
+
222
+ # Posts the given data to the given url, returning the raw response
223
+ def send_request(url, data)
224
+ # Rails.logger.debug "Sending the following data in a request to UPS time in transit API:\n#{data}\n"
225
+ uri = URI.parse(url)
226
+ http = Net::HTTP.new(uri.host, uri.port)
227
+ if uri.port == 443
228
+ http.use_ssl = true
229
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
230
+ end
231
+ response = http.post(uri.path, data)
232
+ response.code == '200' ? response.body : response.error!
233
+ end
234
+
235
+ # converts the given raw xml response to a map of local service codes
236
+ # to estimated delivery dates
237
+ def response_to_map(response)
238
+ # Rails.logger.debug "Received the following data in a response from UPS time in transit API:\n#{response}\n"
239
+ response_doc = REXML::Document.new(response)
240
+ response_code = response_doc.elements['//ResponseStatusCode'].text.to_i
241
+ raise "Invalid response from ups:\n#{response_doc.to_s}" if(!response_code || response_code != 1)
242
+
243
+ service_codes_to_delivery_dates = {}
244
+ response_code = response_doc.elements.each('//ServiceSummary') do |service_element|
245
+ service_code = service_element.elements['Service/Code'].text
246
+ if(service_code)
247
+ date_string = service_element.elements['EstimatedArrival/Date'].text
248
+ time_string = service_element.elements['EstimatedArrival/Time'].text
249
+ days_in_transit = service_element.elements['EstimatedArrival/BusinessTransitDays'].text.to_i
250
+ delivery_date = Time.parse("#{date_string} #{time_string}")
251
+ service_codes_to_delivery_dates[service_code] = {:delivery_date => delivery_date, :days_in_transit => days_in_transit}
252
+ end
253
+ end
254
+ service_codes_to_delivery_dates
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{ups_time_in_transit_plus}
5
+ s.version = "0.1.3"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Joe Stelmach, Benjamin Crudo"]
9
+ # s.cert_chain = ["/Users/joe/workspace/personal/gem-public_cert.pem"]
10
+ s.date = %q{2013-10-07}
11
+ s.description = %q{Provides an easy to use interface to the UPS time in transit API}
12
+ s.email = %q{gems @nospam@ gmail.com}
13
+ s.extra_rdoc_files = ["LICENSE", "README", "lib/ups_time_in_transit.rb"]
14
+ s.files = ["LICENSE", "Manifest", "README", "Rakefile", "lib/ups_time_in_transit.rb", "ups_time_in_transit_plus.gemspec"]
15
+ s.homepage = %q{http://github.com/BENGMN/ups_time_in_transit}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Ups_time_in_transit", "--main", "README"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{ups_time_in_transit_plus}
19
+ s.rubygems_version = %q{1.3.7}
20
+ # s.signing_key = %q{/Users/joe/workspace/personal/gem-private_key.pem}
21
+ s.summary = %q{Provides an easy to use interface to the UPS time in transit API}
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 3
26
+
27
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
28
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
29
+ else
30
+ s.add_dependency(%q<activesupport>, [">= 0"])
31
+ end
32
+ else
33
+ s.add_dependency(%q<activesupport>, [">= 0"])
34
+ end
35
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ups_time_in_transit_plus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - Joe Stelmach, Benjamin Crudo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Provides an easy to use interface to the UPS time in transit API
28
+ email: gems @nospam@ gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README
34
+ - lib/ups_time_in_transit.rb
35
+ files:
36
+ - LICENSE
37
+ - Manifest
38
+ - README
39
+ - Rakefile
40
+ - lib/ups_time_in_transit.rb
41
+ - ups_time_in_transit_plus.gemspec
42
+ homepage: http://github.com/BENGMN/ups_time_in_transit
43
+ licenses: []
44
+ metadata: {}
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --line-numbers
48
+ - --inline-source
49
+ - --title
50
+ - Ups_time_in_transit
51
+ - --main
52
+ - README
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '1.2'
65
+ requirements: []
66
+ rubyforge_project: ups_time_in_transit_plus
67
+ rubygems_version: 2.1.10
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Provides an easy to use interface to the UPS time in transit API
71
+ test_files: []