pactrac 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +26 -0
- data/README.md +148 -0
- data/bin/pactrac +107 -0
- data/lib/pactrac.rb +2 -0
- data/lib/pactrac/carrier.rb +20 -0
- data/lib/pactrac/carrier/dhl.rb +81 -0
- data/lib/pactrac/carrier/ems.rb +87 -0
- data/lib/pactrac/err.rb +3 -0
- data/lib/pactrac/http.rb +22 -0
- data/lib/pactrac/http/cookie.rb +58 -0
- data/lib/pactrac/http/session.rb +19 -0
- data/lib/pactrac/response.rb +3 -0
- data/lib/pactrac/verify/file.rb +17 -0
- metadata +140 -0
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copyright (c) 2012, Michael Alexander
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
|
24
|
+
The views and conclusions contained in the software and documentation are those
|
25
|
+
of the authors and should not be interpreted as representing official policies,
|
26
|
+
either expressed or implied, of Miniand.
|
data/README.md
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
PacTrac - International package tracking for Ruby
|
2
|
+
=================================================
|
3
|
+
|
4
|
+
PacTrac is a library which can be used via API and CLI to fetch raw tracking
|
5
|
+
data. In the initial alpha, only DHL is supported, but a number of other
|
6
|
+
methods are in the process of being added.
|
7
|
+
|
8
|
+
Installation
|
9
|
+
============
|
10
|
+
|
11
|
+
```bash
|
12
|
+
gem install pactrac
|
13
|
+
```
|
14
|
+
|
15
|
+
CLI
|
16
|
+
===
|
17
|
+
|
18
|
+
The binary `pactrac` is included with the gem, usable from the command line.
|
19
|
+
|
20
|
+
### Basic usage
|
21
|
+
|
22
|
+
If not specified, the carrier company is guessed.
|
23
|
+
|
24
|
+
```bash
|
25
|
+
$ pactrac track 1234567890
|
26
|
+
```
|
27
|
+
|
28
|
+
### Specifying carrier
|
29
|
+
|
30
|
+
```bash
|
31
|
+
$ pactrac track 1234567890 --carrier Dhl
|
32
|
+
```
|
33
|
+
|
34
|
+
### Verifying the request
|
35
|
+
|
36
|
+
Some shipping carriers, such as EMS, require CAPTCHA style verifications. The
|
37
|
+
CLI allows for this by downloading the CAPTCHA image to a temporary directory,
|
38
|
+
and allowing you to run the command again adding your verification input.
|
39
|
+
|
40
|
+
```bash
|
41
|
+
$ be pactrac track EE123456789CN
|
42
|
+
EMS requires verification, please view the image at
|
43
|
+
file:///tmp/pactrac_1345961387_ems.jpg and run pactrac again using the following
|
44
|
+
command:
|
45
|
+
|
46
|
+
pactrac track EE123456789CN --carrier Ems --cookie JSESSIONID\=WrTlQ59KBnfpq1pWp
|
47
|
+
hgySgYPTSln1p6rrhDd2pSvFyJt2LJGQ9dr\!-1493181672\;\ TS79e94e\=4f22237fc7dc1c7bf7
|
48
|
+
176dda6b8992a70d84d223c37efee05039bc0060ac0ec5c32dd2e9
|
49
|
+
--verify YOUR_VERIFICATION_HERE
|
50
|
+
```
|
51
|
+
|
52
|
+
API
|
53
|
+
===
|
54
|
+
|
55
|
+
To include PacTrac, add `require 'pactrac'` to your script.
|
56
|
+
|
57
|
+
### Returning error status as part of a pair
|
58
|
+
|
59
|
+
A number of functions which expect failure return a pair of values instead of
|
60
|
+
just one, to avoid returning different types and to avoid exceptions as control
|
61
|
+
flow. The first value is an error struct, with offsets `valid` and `msg`. If
|
62
|
+
`valid` is true then the function call was successful, if it is false then `msg`
|
63
|
+
will be populated with an error message.
|
64
|
+
|
65
|
+
### Discover a carrier based on a tracking number
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
PacTrac::Carrier.for_tracking_number('EE123456789')
|
69
|
+
```
|
70
|
+
|
71
|
+
Returns a pair of values, the first an Err struct, the second is the
|
72
|
+
corresponding carrier module.
|
73
|
+
|
74
|
+
### Request tracking information
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
require 'pactrac'
|
78
|
+
|
79
|
+
carrier = PacTrac::Carrier.for_tracking_number('1234567890')
|
80
|
+
session = carrier.start_session # Start an HTTP session, used in requests
|
81
|
+
err, response = carrier.tracking_request('1234567890', session)
|
82
|
+
raise 'Error getting tracking information' unless err.valid
|
83
|
+
raise 'Verification needed' if response.requires_verification
|
84
|
+
err, tracking_data = carrier.parse_tracking_data(response)
|
85
|
+
raise 'Error getting response' unless err.valid
|
86
|
+
puts "Delivery via #{carrier.title} from #{tracking_data[:origin]} to
|
87
|
+
#{tracking_data[:destination]}"
|
88
|
+
# Output updates ordered by latest
|
89
|
+
tracking_data[:updates].sort_by { |u| u[:at] }.reverse.each do |u|
|
90
|
+
puts "Update time #{u[:at]}: package at #{u[:location]} with message
|
91
|
+
#{u[:message]}"
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
### Sending verification data
|
96
|
+
|
97
|
+
If you have made a tracking request and it requires verification, the
|
98
|
+
`requires_verification` value in the tracking data struct will be true, and the
|
99
|
+
`verification_image` will be set as a URI for the location of the image.
|
100
|
+
Usually the image is downloaded to the local filesystem (file://) for checking.
|
101
|
+
|
102
|
+
After manually checking the verification image, a second request is made to the
|
103
|
+
server. The cookies from the previous request need to be sent to the new
|
104
|
+
request.
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
require 'pactrac'
|
108
|
+
require 'pactrac/http/cookie'
|
109
|
+
|
110
|
+
carrier = PacTrac::Carrier.for_tracking_number('EE123456789CN')
|
111
|
+
session = carrier.start_session # Start an HTTP session, used in requests
|
112
|
+
err, response = carrier.tracking_request('EE123456789CN', session)
|
113
|
+
raise "Error getting tracking information, #{err.msg}" unless err.valid
|
114
|
+
if response.requires_verification
|
115
|
+
session.cookies = PacTrac::Http::Cookie.from_response(response)
|
116
|
+
err, response = carrier.verify('EE123456789CN', 'JHRPDS', session)
|
117
|
+
raise "Error verifying, #{err.msg}" unless err.valid
|
118
|
+
end
|
119
|
+
err, tracking_data = carrier.parse_tracking_data(response)
|
120
|
+
raise "Error getting response, #{err.msg}" unless err.valid
|
121
|
+
puts "Delivery via #{carrier.title} from #{tracking_data[:origin]} to
|
122
|
+
#{tracking_data[:destination]}"
|
123
|
+
# Output updates ordered by latest
|
124
|
+
tracking_data[:updates].sort_by { |u| u[:at] }.reverse.each do |u|
|
125
|
+
puts "Update time #{u[:at]}: package at #{u[:location]} with message
|
126
|
+
#{u[:message]}"
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
130
|
+
Supported carriers
|
131
|
+
==================
|
132
|
+
|
133
|
+
* EMS - PacTrac::Carrier::Ems
|
134
|
+
* DHL - PacTrac::Carrier::Dhl
|
135
|
+
|
136
|
+
Testing
|
137
|
+
=======
|
138
|
+
|
139
|
+
Automated testing is done through rspec, and can be run from rake using
|
140
|
+
`rake spec`.
|
141
|
+
|
142
|
+
Style
|
143
|
+
=====
|
144
|
+
|
145
|
+
The gem is written in a functional style, avoiding side effects of functions and
|
146
|
+
aiming to keep them pure. The gem is also written to avoid returning different
|
147
|
+
types, so many functions return a pair of values, one for exit status / message,
|
148
|
+
and the other for the actual return value.
|
data/bin/pactrac
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
require 'pactrac'
|
3
|
+
require 'pactrac/http/cookie'
|
4
|
+
require 'terminal-table'
|
5
|
+
require 'commander/import'
|
6
|
+
require 'shellwords'
|
7
|
+
require 'colorize'
|
8
|
+
|
9
|
+
program :name, 'PacTrac'
|
10
|
+
program :version, '0.0.1'
|
11
|
+
program :description, 'Internation package tracker in Ruby'
|
12
|
+
|
13
|
+
command :track do |c|
|
14
|
+
c.syntax = 'pactrac track <tracking_number> [options]'
|
15
|
+
c.description = 'Track a package by tracking number'
|
16
|
+
c.option '--carrier STRING', String,
|
17
|
+
'Specify the carrier, otherwise carrier is detected'
|
18
|
+
c.option '--cookie STRING', String,
|
19
|
+
'Specify cookie data to set when verifying'
|
20
|
+
c.option '--verify STRING', String,
|
21
|
+
'Specify the verification string'
|
22
|
+
c.action do |args, options|
|
23
|
+
tracking_number = args.first
|
24
|
+
if tracking_number.nil?
|
25
|
+
$stderr.puts("Please specify a tracking number".red)
|
26
|
+
exit(1)
|
27
|
+
end
|
28
|
+
carrier = nil
|
29
|
+
if options.carrier.nil?
|
30
|
+
err, carrier = PacTrac::Carrier.for_tracking_number(
|
31
|
+
tracking_number)
|
32
|
+
unless err.valid
|
33
|
+
$stderr.puts(
|
34
|
+
"Could not match to a carrier, please specify with --carrier".red)
|
35
|
+
exit(2)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
begin
|
39
|
+
carrier = PacTrac::Carrier.const_get(options.carrier)
|
40
|
+
rescue
|
41
|
+
$stderr.puts("No carrier with the name #{options.carrier}".red)
|
42
|
+
exit(3)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
session = carrier.start_session
|
46
|
+
if options.verify.nil?
|
47
|
+
err, resp = carrier.tracking_request(tracking_number, session)
|
48
|
+
else
|
49
|
+
unless options.cookie.nil?
|
50
|
+
session.cookies = PacTrac::Http::Cookie.from_request_header_value(
|
51
|
+
options.cookie)
|
52
|
+
end
|
53
|
+
err, resp = carrier.verify(tracking_number, options.verify, session)
|
54
|
+
end
|
55
|
+
unless err.valid
|
56
|
+
$stderr.puts("Error requesting tracking data: #{err.msg}".red)
|
57
|
+
exit(4)
|
58
|
+
end
|
59
|
+
if resp.requires_verification
|
60
|
+
$stdout.puts("#{carrier.title} requires verification, please view the " +
|
61
|
+
"image at #{resp.verification_image.green} and run pactrac again " +
|
62
|
+
"using the following command:")
|
63
|
+
$stdout.puts
|
64
|
+
cookies = PacTrac::Http::Cookie.from_response(resp)
|
65
|
+
cookies_string = cookies.length == 0 ? '' : ' --cookie ' +
|
66
|
+
Shellwords.escape(PacTrac::Http::Cookie.to_request_header_value(
|
67
|
+
cookies))
|
68
|
+
$stdout.puts("pactrac track #{Shellwords.escape(tracking_number)
|
69
|
+
} --carrier #{carrier.name.split('::').last}#{cookies_string
|
70
|
+
} --verify " + "YOUR_VERIFICATION_HERE".green)
|
71
|
+
exit(5)
|
72
|
+
else
|
73
|
+
err, tracking_data = carrier.parse_tracking_data(resp)
|
74
|
+
unless err.valid
|
75
|
+
$stderr.puts("Error parsing tracking data: #{err.msg}".red)
|
76
|
+
exit(6)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Discovers headings from updates
|
81
|
+
headings = [ :at, :location, :message ]
|
82
|
+
tracking_data[:updates].each do |u|
|
83
|
+
u.keys.each do |key|
|
84
|
+
headings << key unless headings.include?(key)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
rows = tracking_data[:updates].sort_by { |u| u[:at] }.reverse.map do |u|
|
88
|
+
headings.map { |heading| u[heading] }
|
89
|
+
end
|
90
|
+
|
91
|
+
PacTrac::Http::Session.finish(session)
|
92
|
+
$stdout.puts("Tracking number: #{tracking_number}")
|
93
|
+
$stdout.puts("Carrier: #{carrier.title}")
|
94
|
+
unless tracking_data[:origin].nil?
|
95
|
+
$stdout.puts("Origin: #{tracking_data[:origin]}")
|
96
|
+
end
|
97
|
+
unless tracking_data[:destination].nil?
|
98
|
+
$stdout.puts("Destination: #{tracking_data[:destination]}")
|
99
|
+
end
|
100
|
+
$stdout.puts
|
101
|
+
|
102
|
+
puts Terminal::Table.new(:headings => headings.map { |h|
|
103
|
+
h.to_s.capitalize }, :rows => rows)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
default_command :help
|
data/lib/pactrac.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'pactrac/carrier/dhl'
|
2
|
+
require 'pactrac/carrier/ems'
|
3
|
+
require 'pactrac/err'
|
4
|
+
|
5
|
+
module PacTrac
|
6
|
+
module Carrier
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def all
|
10
|
+
[Carrier::Dhl, Carrier::Ems]
|
11
|
+
end
|
12
|
+
|
13
|
+
def for_tracking_number(tracking_number)
|
14
|
+
all.each do |c|
|
15
|
+
return Err.new(true), c if c.tracking_number_relevant?(tracking_number)
|
16
|
+
end
|
17
|
+
return Err.new(false, 'unable to match tracking number to carrier')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'pactrac/err'
|
3
|
+
require 'pactrac/response'
|
4
|
+
require 'pactrac/http'
|
5
|
+
require 'pactrac/http/session'
|
6
|
+
require 'net/http'
|
7
|
+
require 'date'
|
8
|
+
|
9
|
+
module PacTrac
|
10
|
+
module Carrier
|
11
|
+
module Dhl
|
12
|
+
module_function
|
13
|
+
|
14
|
+
def title
|
15
|
+
'DHL'
|
16
|
+
end
|
17
|
+
|
18
|
+
def tracking_number_relevant?(tracking_number)
|
19
|
+
tracking_number.strip.match(/^\d{10}$/)
|
20
|
+
end
|
21
|
+
|
22
|
+
def tracking_request(tracking_number, session)
|
23
|
+
err, raw = Http.request(Net::HTTP::Get.new(
|
24
|
+
"/content/g0/en/express/tracking.shtml" +
|
25
|
+
"?brand=DHL&AWB=#{tracking_number}%0D%0A", 'User-Agent' =>
|
26
|
+
'Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.1 (KHTML, like' +
|
27
|
+
' Gecko) Chrome/21.0.1180.75 Safari/537.1'), session)
|
28
|
+
unless err.valid
|
29
|
+
return Err.new(false,
|
30
|
+
"there was a problem connecting to the DHL server, #{err.msg}")
|
31
|
+
end
|
32
|
+
return Err.new(true), Response.new(raw, false)
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse_tracking_data(response)
|
36
|
+
doc = Nokogiri::HTML(response.raw.body)
|
37
|
+
table = doc.css('.clpt_tracking_results table').first
|
38
|
+
if table.nil?
|
39
|
+
return Err.new(false, 'unable to find tracking data table')
|
40
|
+
end
|
41
|
+
tracking_data = { :updates => [] }
|
42
|
+
origin_node = table.css('#orginURL4').first
|
43
|
+
if origin_node.nil?
|
44
|
+
return Err.new(false, 'unable to find origin')
|
45
|
+
end
|
46
|
+
tracking_data[:origin] = origin_node.content
|
47
|
+
destination_node = table.css('#destinationURL4').first
|
48
|
+
if destination_node.nil?
|
49
|
+
return Err.new(false, 'unable to find destination')
|
50
|
+
end
|
51
|
+
tracking_data[:destination] = destination_node.content
|
52
|
+
current_date = nil
|
53
|
+
table.children.each do |section|
|
54
|
+
next unless ['thead', 'tbody'].include?(section.name.to_s)
|
55
|
+
next if section.attribute('class').to_s == 'tophead'
|
56
|
+
section.css('tr').each do |row|
|
57
|
+
cells = row.css('td, th')
|
58
|
+
first_cell = cells.first
|
59
|
+
case
|
60
|
+
when first_cell.name == 'th' # Date cell
|
61
|
+
current_date = Date.parse(first_cell.content)
|
62
|
+
when first_cell.attribute('class').to_s != 'emptyRow'
|
63
|
+
next if current_date.nil?
|
64
|
+
tracking_data[:updates] << {
|
65
|
+
:message => cells[1].content.strip,
|
66
|
+
:location => cells[2].content.strip,
|
67
|
+
:at => DateTime.parse(
|
68
|
+
"#{current_date.to_s}T#{cells[3].content.to_s}"),
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
return Err.new(true), tracking_data
|
74
|
+
end
|
75
|
+
|
76
|
+
def start_session
|
77
|
+
Http::Session.start('www.dhl.com', 80)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'pactrac/err'
|
3
|
+
require 'pactrac/response'
|
4
|
+
require 'pactrac/http'
|
5
|
+
require 'pactrac/http/session'
|
6
|
+
require 'pactrac/http/cookie'
|
7
|
+
require 'pactrac/verify/file'
|
8
|
+
require 'net/http'
|
9
|
+
require 'date'
|
10
|
+
|
11
|
+
module PacTrac
|
12
|
+
module Carrier
|
13
|
+
module Ems
|
14
|
+
module_function
|
15
|
+
|
16
|
+
def title
|
17
|
+
'EMS'
|
18
|
+
end
|
19
|
+
|
20
|
+
def tracking_number_relevant?(tracking_number)
|
21
|
+
tracking_number.strip.match(/^EE\d+CN$/)
|
22
|
+
end
|
23
|
+
|
24
|
+
def tracking_request(tracking_number, session)
|
25
|
+
err, raw = Http.request(Net::HTTP::Get.new('/english.html',
|
26
|
+
'User-Agent' => user_agent), session)
|
27
|
+
return err unless err.valid
|
28
|
+
# Get validation image
|
29
|
+
err, raw = Http.request(Net::HTTP::Get.new('/ems/rand',
|
30
|
+
'User-Agent' => user_agent), session)
|
31
|
+
unless err.valid
|
32
|
+
return Err.new(false,
|
33
|
+
"there was a problem connecting to the EMS server, #{err.msg}")
|
34
|
+
end
|
35
|
+
f = Verify::File.create('ems.jpg', raw.body)
|
36
|
+
return Err.new(true), Response.new(raw, true, "file://#{f}")
|
37
|
+
end
|
38
|
+
|
39
|
+
def verify(tracking_number, verification, session)
|
40
|
+
req = Net::HTTP::Post.new('/ems/order/singleQuery_e',
|
41
|
+
'User-Agent' => user_agent)
|
42
|
+
req.set_form_data({ :mailNum => tracking_number, :checkCode =>
|
43
|
+
verification })
|
44
|
+
if session.cookies
|
45
|
+
req['Cookie'] = Http::Cookie.to_request_header_value(session.cookies)
|
46
|
+
end
|
47
|
+
err, raw = Http.request(req, session)
|
48
|
+
unless err.valid
|
49
|
+
return Err.new(false,
|
50
|
+
"there was a problem connecting to the EMS server, #{err.msg}")
|
51
|
+
end
|
52
|
+
if raw.body.match(/failure/i)
|
53
|
+
return Err.new(false, 'failure to verify using given code')
|
54
|
+
end
|
55
|
+
return Err.new(true), Response.new(raw, false)
|
56
|
+
end
|
57
|
+
|
58
|
+
def parse_tracking_data(response)
|
59
|
+
doc = Nokogiri::HTML(response.raw.body)
|
60
|
+
table = doc.css('.mailnum_result_box table')
|
61
|
+
if table.nil?
|
62
|
+
return Err.new(false, 'unable to find tracking data table')
|
63
|
+
end
|
64
|
+
tracking_data = { :updates => [] }
|
65
|
+
table.css('tr').each do |row|
|
66
|
+
cells = row.css('td')
|
67
|
+
next if cells.nil? or cells.length < 2
|
68
|
+
tracking_data[:updates] << {
|
69
|
+
:at => DateTime.parse(cells[0].content.to_s),
|
70
|
+
:location => cells[1].content.to_s.strip,
|
71
|
+
:message => cells[2].content.to_s.strip,
|
72
|
+
}
|
73
|
+
end
|
74
|
+
return Err.new(true), tracking_data
|
75
|
+
end
|
76
|
+
|
77
|
+
def start_session
|
78
|
+
Http::Session.start('www.ems.com.cn', 80)
|
79
|
+
end
|
80
|
+
|
81
|
+
def user_agent
|
82
|
+
'Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.1 (KHTML, like' +
|
83
|
+
' Gecko) Chrome/21.0.1180.75 Safari/537.1'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/pactrac/err.rb
ADDED
data/lib/pactrac/http.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module PacTrac
|
5
|
+
module Http
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def request(req, session)
|
9
|
+
raw = nil
|
10
|
+
begin
|
11
|
+
Timeout::timeout(10) do
|
12
|
+
raw = session.session.request(req)
|
13
|
+
end
|
14
|
+
rescue Timeout::Error => e
|
15
|
+
return Err.new(false, 'tracking request took too long to respond')
|
16
|
+
rescue Net::HTTPError => e
|
17
|
+
return Err.new(false, 'error making tracking request')
|
18
|
+
end
|
19
|
+
return Err.new(true), raw
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module PacTrac
|
4
|
+
module Http
|
5
|
+
module Cookie
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def from_response(response)
|
9
|
+
from_raw(response.raw)
|
10
|
+
end
|
11
|
+
|
12
|
+
def from_raw(raw)
|
13
|
+
cookies = {}
|
14
|
+
raw.get_fields('Set-Cookie').each do |c|
|
15
|
+
key, value = from_response_header_value(c)
|
16
|
+
cookies[key] = value
|
17
|
+
end
|
18
|
+
cookies
|
19
|
+
end
|
20
|
+
|
21
|
+
def from_response_header_value(header_value)
|
22
|
+
header_value.split(/\s*;\s*/)[0].split(/\s*=\s*/)[0,2].map {|t|
|
23
|
+
URI.unescape(t) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def update_from_response(response, cookies)
|
27
|
+
update_from_raw(response.raw, cookies)
|
28
|
+
end
|
29
|
+
|
30
|
+
def update_from_raw(raw, cookies)
|
31
|
+
c = cookies.clone
|
32
|
+
from_raw(raw).each do |key, value|
|
33
|
+
c[key] = value
|
34
|
+
end
|
35
|
+
c
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_request_header_value(cookies)
|
39
|
+
cookies.map { |key, value|
|
40
|
+
"#{URI.escape(key, ',;=')}=#{URI.escape(value, ',;=')}"
|
41
|
+
}.join('; ')
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_request_header(cookies)
|
45
|
+
"Cookie: #{to_request_header_value(cookies)}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def from_request_header_value(header_value)
|
49
|
+
header_value.split(/\s*;\s*/).reject{ |v| v.strip == '' }.map { |p|
|
50
|
+
p.split(/\s*=\s*/)[0,2].map {|t| URI.unescape(t) }
|
51
|
+
}.inject({}) { |hash, (key, value)|
|
52
|
+
hash[key] = value
|
53
|
+
hash
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module PacTrac
|
4
|
+
module Http
|
5
|
+
module Session
|
6
|
+
module_function
|
7
|
+
|
8
|
+
Store = Struct.new(:session, :cookies)
|
9
|
+
|
10
|
+
def start(address, port)
|
11
|
+
Store.new(Net::HTTP.start(address, port), {})
|
12
|
+
end
|
13
|
+
|
14
|
+
def finish(session)
|
15
|
+
session.session.finish
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
|
3
|
+
module PacTrac
|
4
|
+
module Verify
|
5
|
+
module File
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def create(name, data)
|
9
|
+
filename = ::File.join(Dir.tmpdir, "pactrac_#{Time.now.to_i}_#{name}")
|
10
|
+
::File.open(filename, 'w') do |f|
|
11
|
+
f.write(data)
|
12
|
+
end
|
13
|
+
filename
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pactrac
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Michael Alexander
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-08-23 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: nokogiri
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 9
|
29
|
+
segments:
|
30
|
+
- 1
|
31
|
+
- 5
|
32
|
+
- 5
|
33
|
+
version: 1.5.5
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: commander
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 63
|
45
|
+
segments:
|
46
|
+
- 4
|
47
|
+
- 1
|
48
|
+
- 2
|
49
|
+
version: 4.1.2
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: terminal-table
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 13
|
61
|
+
segments:
|
62
|
+
- 1
|
63
|
+
- 4
|
64
|
+
- 5
|
65
|
+
version: 1.4.5
|
66
|
+
type: :runtime
|
67
|
+
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: colorize
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 27
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
- 5
|
80
|
+
- 8
|
81
|
+
version: 0.5.8
|
82
|
+
type: :runtime
|
83
|
+
version_requirements: *id004
|
84
|
+
description: International package tracking for Ruby
|
85
|
+
email: beefsack@gmail.com
|
86
|
+
executables:
|
87
|
+
- pactrac
|
88
|
+
extensions: []
|
89
|
+
|
90
|
+
extra_rdoc_files: []
|
91
|
+
|
92
|
+
files:
|
93
|
+
- lib/pactrac/carrier/dhl.rb
|
94
|
+
- lib/pactrac/carrier/ems.rb
|
95
|
+
- lib/pactrac/err.rb
|
96
|
+
- lib/pactrac/http.rb
|
97
|
+
- lib/pactrac/http/session.rb
|
98
|
+
- lib/pactrac/http/cookie.rb
|
99
|
+
- lib/pactrac/carrier.rb
|
100
|
+
- lib/pactrac/verify/file.rb
|
101
|
+
- lib/pactrac/response.rb
|
102
|
+
- lib/pactrac.rb
|
103
|
+
- bin/pactrac
|
104
|
+
- LICENSE
|
105
|
+
- README.md
|
106
|
+
homepage: https://github.com/Miniand/pactrac
|
107
|
+
licenses: []
|
108
|
+
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
hash: 3
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
version: "0"
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
hash: 3
|
129
|
+
segments:
|
130
|
+
- 0
|
131
|
+
version: "0"
|
132
|
+
requirements: []
|
133
|
+
|
134
|
+
rubyforge_project:
|
135
|
+
rubygems_version: 1.8.24
|
136
|
+
signing_key:
|
137
|
+
specification_version: 3
|
138
|
+
summary: pactrac
|
139
|
+
test_files: []
|
140
|
+
|