File Coverage

blib/lib/Business/UPS/Tracking/Utils.pm
Criterion Covered Total %
statement 35 56 62.5
branch 0 6 0.0
condition 0 6 0.0
subroutine 12 19 63.1
pod 3 3 100.0
total 50 90 55.5


line stmt bran cond sub pod time code
1             # ============================================================================
2             package Business::UPS::Tracking::Utils;
3             # ============================================================================
4 5     5   36 use utf8;
  5         10  
  5         36  
5 5     5   230 use 5.0100;
  5         21  
6              
7 5     5   26 use strict;
  5         12  
  5         86  
8 5     5   26 use warnings;
  5         13  
  5         140  
9              
10 5     5   24 use Try::Tiny;
  5         20  
  5         261  
11 5     5   30 use Business::UPS::Tracking::Exception;
  5         13  
  5         105  
12 5     5   24 use Moose::Util::TypeConstraints;
  5         10  
  5         62  
13 5     5   10038 use Business::UPS::Tracking;
  5         11  
  5         120  
14 5     5   2214 use MooseX::Getopt::OptionTypeMap;
  5         80509  
  5         187  
15 5     5   2127 use Business::UPS::Tracking::Meta::Attribute::Trait::Printable;
  5         21  
  5         148  
16 5     5   2805 use Encode;
  5         39124  
  5         1739  
17              
18             =encoding utf8
19              
20             =head1 NAME
21              
22             Business::UPS::Tracking::Utils - Utility functions
23              
24             =head1 SYNOPSIS
25              
26             use Business::UPS::Tracking::Utils;
27              
28             =head1 DESCRIPTION
29              
30             This module provides some basic utility functions for
31             L<Business::UPS::Tracking> and defines some Moose type constraints and
32             coercions.
33              
34             =head1 FUNCTIONS
35              
36             =cut
37              
38             subtype 'Business::UPS::Tracking::Type::XMLDocument'
39             => as class_type('XML::LibXML::Document');
40              
41             coerce 'Business::UPS::Tracking::Type::XMLDocument'
42             => from 'Str'
43             => via {
44             my $xml = $_;
45             $xml = decode("iso-8859-1", $xml);
46              
47             my $parser = XML::LibXML->new(
48             #encoding => 'iso-8859-15'
49             );
50             return try {
51             return $parser->parse_string($xml);
52             } catch {
53             Business::UPS::Tracking::X::XML->throw(
54             error => $_ || 'Unknown error parsing xml document',
55             xml => $xml,
56             );
57             }
58             };
59              
60             subtype 'Business::UPS::Tracking::Type::Date'
61             => as class_type('DateTime');
62              
63             subtype 'Business::UPS::Tracking::Type::DateStr'
64             => as 'Str'
65             => where {
66             m/^
67             (19|20)\d\d #year
68             (0[1-9]|1[012]) #month
69             (3[01]|[12]\d|0[1-9]) #day
70             $/x;
71             };
72              
73             coerce 'Business::UPS::Tracking::Type::DateStr'
74             => from 'Business::UPS::Tracking::Type::Date'
75             => via {
76             return $_->format_cldr('yyyyMMdd');
77             };
78              
79             subtype 'Business::UPS::Tracking::Type::TrackingNumber'
80             => as 'Str'
81             => where {
82             my $trackingnumber = $_;
83             return 0
84             unless ($trackingnumber =~ m/^1Z(?<tracking>[A-Z0-9]{8}\d{7})(?<checksum>\d)$/);
85             # Checksum check fails because UPS testdata has invalid checksum!
86             return 1
87             unless $Business::UPS::Tracking::CHECKSUM;
88 5     5   2307 my $checksum = $+{checksum};
  5         1516  
  5         2794  
89             my $tracking = $+{tracking};
90             $tracking =~ tr/ABCDEFGHIJKLMNOPQRSTUVWXYZ/23456789012345678901234567/;
91             my ($odd,$even,$pos) = (0,0,0);
92             foreach (split //,$tracking) {
93             $pos ++;
94             if ($pos % 2) {
95             $odd += $_;
96             } else {
97             $even += $_;
98             }
99             }
100             $even *= 2;
101             my $calculated = $odd + $even;
102             $calculated =~ s/^\d+(\d)$/$1/e;
103             $calculated = 10 - $calculated
104             unless ($calculated == 0);
105             return ($checksum == $calculated);
106             }
107             => message { "Tracking numbers must start withn '1Z', contain 15 additional characters and end with a valid checksum : '$_'" };
108              
109             subtype 'Business::UPS::Tracking::Type::CountryCode'
110             => as 'Str'
111             => where { m/^[A-Z]{2}$/ }
112             => message { "Must be an uppercase ISO 3166-1 alpha-2 code" };
113              
114             =head3 parse_date
115              
116             $datetime = parse_date($string);
117              
118             Parses a date string (YYYYMMDD) and returns a L<DateTime> object.
119              
120             =cut
121              
122             sub parse_date {
123 0     0 1   my $datestr = shift;
124              
125             return
126 0 0 0       unless $datestr
127             && $datestr =~ m/^
128             (?<year>(19|20)\d\d)
129             (?<month>0[1-9]|1[012])
130             (?<day>3[01]|[12]\d|0[1-9])
131             $/x;
132              
133             return try {
134             DateTime->new(
135             year => $+{year},
136             month => $+{month},
137             day => $+{day},
138 0     0     );
139             } catch {
140 0     0     Business::UPS::Tracking::X::XML->throw(
141             error => "Invalid date string: ".$_,
142             xml => $datestr,
143             );
144 0           };
145             }
146              
147             =head3 parse_time
148              
149             $datetime = parse_time($string,$datetime);
150              
151             Parses a time string (HHMMSS) and appends the parsed values to the given
152             L<DateTime> object
153              
154             =cut
155              
156             sub parse_time {
157 0     0 1   my ($timestr,$datetime) = @_;
158              
159             return
160 0 0         unless $datetime;
161              
162 0 0 0       return $datetime
163             unless $timestr
164             && $timestr =~ m/^
165             (?<hour>\d\d)
166             (?<minute>\d\d)
167             (?<second>\d\d)
168             $/x;
169              
170             try {
171 0     0     $datetime->set_hour( $+{hour} );
172 0           $datetime->set_minute( $+{minute} );
173 0           $datetime->set_second( $+{second} );
174 0           return 1;
175             } catch {
176 0     0     Business::UPS::Tracking::X::XML->throw(
177             error => "Invalid time string: ".$_,
178             xml => $timestr,
179             );
180             }
181              
182 0           return $datetime;
183             }
184              
185              
186             =head3 escape_xml
187              
188             my $escaped_string = escape_xml($string);
189              
190             Escapes a string for xml
191              
192             =cut
193              
194             sub escape_xml {
195 0     0 1   my ($string) = @_;
196              
197 0           $string =~ s/&/&amp;/g;
198 0           $string =~ s/</&gt;/g;
199 0           $string =~ s/>/&lt;/g;
200 0           $string =~ s/"/&qout;/g;
201 0           $string =~ s/'/&apos;/g;
202              
203 0           return $string;
204             }
205              
206             1;