pactrac 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.
- 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
|
+
|