trackerific 0.7.2 → 0.7.3
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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.rdoc +2 -1
- data/lib/dependencies.rb +9 -0
- data/lib/trackerific.rb +5 -19
- data/lib/trackerific/builders.rb +12 -0
- data/lib/trackerific/builders/base/soap.rb +14 -16
- data/lib/trackerific/builders/base/xml.rb +22 -28
- data/lib/trackerific/builders/fedex.rb +66 -44
- data/lib/trackerific/builders/ups.rb +28 -24
- data/lib/trackerific/builders/usps.rb +15 -13
- data/lib/trackerific/parsers.rb +8 -0
- data/lib/trackerific/parsers/base.rb +24 -28
- data/lib/trackerific/parsers/fedex.rb +43 -47
- data/lib/trackerific/parsers/ups.rb +49 -53
- data/lib/trackerific/parsers/usps.rb +44 -48
- data/lib/trackerific/services.rb +10 -0
- data/lib/trackerific/services/base.rb +82 -55
- data/lib/trackerific/services/concerns/soap.rb +10 -40
- data/lib/trackerific/services/concerns/xml.rb +15 -37
- data/lib/trackerific/services/fedex.rb +9 -19
- data/lib/trackerific/services/mock_service.rb +26 -37
- data/lib/trackerific/services/ups.rb +11 -24
- data/lib/trackerific/services/usps.rb +23 -35
- data/lib/trackerific/soap.rb +5 -0
- data/lib/trackerific/soap/wsdl.rb +8 -12
- data/lib/trackerific/version.rb +1 -1
- data/spec/lib/trackerific/services/base_spec.rb +21 -4
- data/spec/lib/trackerific/services/concerns/soap_spec.rb +6 -21
- data/spec/lib/trackerific/services/concerns/xml_spec.rb +5 -18
- data/spec/lib/trackerific/services/mock_service_spec.rb +12 -0
- data/spec/lib/trackerific/services/usps_spec.rb +4 -4
- data/spec/lib/trackerific/version_spec.rb +1 -1
- data/spec/support/test_concerns.rb +15 -0
- metadata +8 -2
@@ -1,59 +1,55 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
end
|
1
|
+
class Trackerific::Parsers::FedEx < Trackerific::Parsers::Base
|
2
|
+
protected
|
3
|
+
|
4
|
+
def response_error
|
5
|
+
@response_error ||= if highest_severity == 'ERROR'
|
6
|
+
Trackerific::Error.new(notifications[:message])
|
7
|
+
else
|
8
|
+
false
|
9
|
+
end
|
10
|
+
end
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
def summary
|
13
|
+
nil
|
14
|
+
end
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
def events
|
17
|
+
track_details.map do |detail|
|
18
|
+
Trackerific::Event.new(parse_date(detail), nil, location(detail))
|
19
|
+
end
|
20
|
+
end
|
23
21
|
|
24
|
-
|
22
|
+
private
|
25
23
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
def location(detail)
|
25
|
+
a = detail[:destination_address]
|
26
|
+
"#{a[:city]}, #{a[:state_or_province_code]} #{a[:country_code]}"
|
27
|
+
end
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
def parse_date(detail)
|
30
|
+
detail[:ship_timestamp]
|
31
|
+
end
|
34
32
|
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
def track_reply
|
34
|
+
@response.hash[:envelope][:body][:track_reply]
|
35
|
+
end
|
38
36
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
47
|
-
end
|
37
|
+
def track_details
|
38
|
+
@track_details ||= begin
|
39
|
+
details = track_reply[:completed_track_details][:track_details]
|
40
|
+
details.select do |d|
|
41
|
+
d[:ship_timestamp].present? && d[:destination_address].present? &&
|
42
|
+
d[:destination_address][:city].present? &&
|
43
|
+
d[:destination_address][:state_or_province_code].present?
|
48
44
|
end
|
45
|
+
end
|
46
|
+
end
|
49
47
|
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
def highest_severity
|
49
|
+
track_reply[:highest_severity]
|
50
|
+
end
|
53
51
|
|
54
|
-
|
55
|
-
|
56
|
-
end
|
57
|
-
end
|
52
|
+
def notifications
|
53
|
+
track_reply[:notifications]
|
58
54
|
end
|
59
55
|
end
|
@@ -1,66 +1,62 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
16
|
-
end
|
1
|
+
class Trackerific::Parsers::UPS < Trackerific::Parsers::Base
|
2
|
+
protected
|
3
|
+
|
4
|
+
def response_error
|
5
|
+
@response_error ||= if @response.code != 200
|
6
|
+
Trackerific::Error.new("HTTP returned status #{@response.code}")
|
7
|
+
elsif response_status_code == :error
|
8
|
+
Trackerific::Error.new(error_response)
|
9
|
+
elsif response_status_code == :success
|
10
|
+
false
|
11
|
+
else
|
12
|
+
Trackerific::Error.new("Unknown status code from server.")
|
13
|
+
end
|
14
|
+
end
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
def summary
|
17
|
+
description(activity.first)
|
18
|
+
end
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
20
|
+
def events
|
21
|
+
activity.map do |a|
|
22
|
+
date = parse_ups_date_time(a['Date'], a['Time'])
|
23
|
+
Trackerific::Event.new(date, description(a), location(a))
|
24
|
+
end
|
25
|
+
end
|
28
26
|
|
29
|
-
|
27
|
+
private
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
def track_response
|
30
|
+
@response['TrackResponse']
|
31
|
+
end
|
34
32
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
def response_status_code
|
34
|
+
{ "0" => :error,
|
35
|
+
"1" => :success
|
36
|
+
}[track_response['Response']['ResponseStatusCode']]
|
37
|
+
end
|
40
38
|
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
def error_response
|
40
|
+
track_response['Response']['Error']['ErrorDescription']
|
41
|
+
end
|
44
42
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
43
|
+
def parse_ups_date_time(date, time)
|
44
|
+
hours, minutes, seconds = time.scan(/.{2}/)
|
45
|
+
DateTime.parse("#{Date.parse(date)} #{hours}:#{minutes}:#{seconds}")
|
46
|
+
end
|
49
47
|
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
def description(a)
|
49
|
+
a['Status']['StatusType']['Description']
|
50
|
+
end
|
53
51
|
|
54
|
-
|
55
|
-
|
56
|
-
|
52
|
+
def location(a)
|
53
|
+
a['ActivityLocation']['Address'].map {|k,v| v}.join(" ")
|
54
|
+
end
|
57
55
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
63
|
-
end
|
56
|
+
def activity
|
57
|
+
@activity ||= begin
|
58
|
+
a = track_response['Shipment']['Package']['Activity']
|
59
|
+
a.is_a?(Array) ? a : [a]
|
64
60
|
end
|
65
61
|
end
|
66
62
|
end
|
@@ -1,51 +1,47 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
else
|
14
|
-
false
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def summary
|
19
|
-
tracking_info['TrackSummary']
|
20
|
-
end
|
21
|
-
|
22
|
-
def events
|
23
|
-
tracking_info.fetch('TrackDetail', []).map do |e|
|
24
|
-
Trackerific::Event.new(date(e), description(e), location(e))
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def tracking_info
|
31
|
-
@response['TrackResponse']['TrackInfo']
|
32
|
-
end
|
33
|
-
|
34
|
-
def date(event)
|
35
|
-
d = event.split(" ")
|
36
|
-
DateTime.parse(d[0..3].join(" "))
|
37
|
-
end
|
38
|
-
|
39
|
-
def description(event)
|
40
|
-
d = event.split(" ")
|
41
|
-
d[4..d.length-4].join(" ")
|
42
|
-
end
|
43
|
-
|
44
|
-
def location(event)
|
45
|
-
d = event.gsub(".", "").split(" ")
|
46
|
-
city, state, zip = d.last(3)
|
47
|
-
"#{city}, #{state} #{zip}"
|
48
|
-
end
|
1
|
+
class Trackerific::Parsers::USPS < Trackerific::Parsers::Base
|
2
|
+
protected
|
3
|
+
|
4
|
+
def response_error
|
5
|
+
@response_error ||= if @response.code != 200
|
6
|
+
Trackerific::Error.new("HTTP returned status #{@response.code}")
|
7
|
+
elsif @response['Error']
|
8
|
+
Trackerific::Error.new(@response['Error']['Description'])
|
9
|
+
elsif @response['TrackResponse'].nil? && @response['CityStateLookupResponse'].nil?
|
10
|
+
Trackerific::Error.new("Invalid response from server.")
|
11
|
+
else
|
12
|
+
false
|
49
13
|
end
|
50
14
|
end
|
15
|
+
|
16
|
+
def summary
|
17
|
+
tracking_info['TrackSummary']
|
18
|
+
end
|
19
|
+
|
20
|
+
def events
|
21
|
+
tracking_info.fetch('TrackDetail', []).map do |e|
|
22
|
+
Trackerific::Event.new(date(e), description(e), location(e))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def tracking_info
|
29
|
+
@response['TrackResponse']['TrackInfo']
|
30
|
+
end
|
31
|
+
|
32
|
+
def date(event)
|
33
|
+
d = event.split(" ")
|
34
|
+
DateTime.parse(d[0..3].join(" "))
|
35
|
+
end
|
36
|
+
|
37
|
+
def description(event)
|
38
|
+
d = event.split(" ")
|
39
|
+
d[4..d.length-4].join(" ")
|
40
|
+
end
|
41
|
+
|
42
|
+
def location(event)
|
43
|
+
d = event.gsub(".", "").split(" ")
|
44
|
+
city, state, zip = d.last(3)
|
45
|
+
"#{city}, #{state} #{zip}"
|
46
|
+
end
|
51
47
|
end
|
data/lib/trackerific/services.rb
CHANGED
@@ -37,5 +37,15 @@ module Trackerific
|
|
37
37
|
@services.map {|n,s| s if s.can_track?(id) }.compact
|
38
38
|
end
|
39
39
|
end
|
40
|
+
|
41
|
+
module Concerns
|
42
|
+
require 'trackerific/services/concerns/soap'
|
43
|
+
require 'trackerific/services/concerns/xml'
|
44
|
+
end
|
45
|
+
|
46
|
+
require 'trackerific/services/base'
|
47
|
+
require 'trackerific/services/fedex'
|
48
|
+
require 'trackerific/services/ups'
|
49
|
+
require 'trackerific/services/usps'
|
40
50
|
end
|
41
51
|
end
|
@@ -1,58 +1,85 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
1
|
+
class Trackerific::Services::Base
|
2
|
+
class << self
|
3
|
+
attr_accessor :name
|
4
|
+
attr_accessor :config
|
5
|
+
|
6
|
+
# Configures the service
|
7
|
+
# @api semipublic
|
8
|
+
def configure(&block)
|
9
|
+
yield self.config = OpenStruct.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# Includes the service concerns for the given service type
|
13
|
+
# @param [Symbol] service_type The module name for the service type
|
14
|
+
# @api semipublic
|
15
|
+
def concerns(service_type)
|
16
|
+
self.send :include,
|
17
|
+
"Trackerific::Services::Concerns::#{service_type}".constantize
|
18
|
+
end
|
19
|
+
|
20
|
+
# Registers the service with Trackerific
|
21
|
+
# @api semipublic
|
22
|
+
def register(name, options={})
|
23
|
+
concerns(options[:as]) if options[:as].present?
|
24
|
+
self.name = name.to_sym
|
25
|
+
Trackerific::Services[self.name] = self
|
26
|
+
end
|
27
|
+
|
28
|
+
# Creates a new instance and calls #track with the given id
|
29
|
+
# @param id The package identifier
|
30
|
+
# @return Either a Trackerific::Details or Trackerific::Error
|
31
|
+
def track(id)
|
32
|
+
new.track(id)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Reads the credentials from Trackerific.config
|
36
|
+
# @return [Hash] The service's credentials
|
37
|
+
def credentials
|
38
|
+
Trackerific.config[name]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Checks if the given package ID can be tracked by this service
|
42
|
+
# @param [String] id The package ID
|
43
|
+
# @return [Boolean] true when this service can track the given ID
|
44
|
+
# @note This will always be false if no credentials were found for the
|
45
|
+
# service in Trackerific.config
|
46
|
+
def can_track?(id)
|
47
|
+
return false if credentials.nil?
|
48
|
+
package_id_matchers.each {|m| return true if id =~ m }
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
# An Array of Regexp that matches valid package ids for the service
|
53
|
+
# @api semipublic
|
54
|
+
def package_id_matchers
|
55
|
+
config.package_id_matchers
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize(credentials=self.class.credentials)
|
60
|
+
@credentials = credentials
|
61
|
+
|
62
|
+
if credentials.nil?
|
63
|
+
raise Trackerific::Error,
|
64
|
+
"Missing credentials for #{self.class.name}", caller
|
56
65
|
end
|
57
66
|
end
|
67
|
+
|
68
|
+
def config
|
69
|
+
self.class.config
|
70
|
+
end
|
71
|
+
|
72
|
+
def track(id)
|
73
|
+
result = config.parser.new(id, request(id)).parse
|
74
|
+
result.is_a?(Trackerific::Error) ? raise(result) : result
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
def builder(id)
|
80
|
+
members = config.builder.members - [:package_id]
|
81
|
+
credentials = @credentials.values_at(*members)
|
82
|
+
credentials << id
|
83
|
+
config.builder.new(*credentials)
|
84
|
+
end
|
58
85
|
end
|