line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Mojolicious::Command::donuts; |
2
|
1
|
|
|
1
|
|
595
|
use Mojo::Base 'Mojolicious::Command'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
7
|
|
3
|
|
|
|
|
|
|
|
4
|
1
|
|
|
1
|
|
101703
|
use Mojo::JSON qw(encode_json); |
|
1
|
|
|
|
|
2377
|
|
|
1
|
|
|
|
|
59
|
|
5
|
1
|
|
|
1
|
|
14
|
use Mojo::Util qw(dumper); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
35
|
|
6
|
1
|
|
|
1
|
|
821
|
use Getopt::Long qw(GetOptionsFromArray); |
|
1
|
|
|
|
|
9000
|
|
|
1
|
|
|
|
|
5
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = 0.03; |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
has description => 'Find fresh donuts near you!'; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
has usage => <
|
13
|
|
|
|
|
|
|
USAGE: |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
mojo donuts [OPTIONS] |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
OPTIONS: |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
--geo, -g Specify geo to be used for search (recommended) |
20
|
|
|
|
|
|
|
--fresh, -f Filter results by stores serving fresh donuts |
21
|
|
|
|
|
|
|
--raw, -r Print raw data structure returned from search response |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
EOF |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
has ua => sub { Mojo::UserAgent->new() }; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
has [qw( geo fresh raw )]; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub _mk_request { |
30
|
0
|
|
|
0
|
|
|
my ($self, $url, $form) = @_; |
31
|
0
|
0
|
|
|
|
|
my $tx = $form ? $self->ua->get($url) : $self->ua->post($url => form => $form); |
32
|
|
|
|
|
|
|
|
33
|
0
|
0
|
|
|
|
|
unless ($tx->success) { |
34
|
0
|
|
|
|
|
|
my $err = $tx->error; |
35
|
0
|
|
|
|
|
|
die join( |
36
|
|
|
|
|
|
|
'', # |
37
|
|
|
|
|
|
|
'Trouble finding IP address.', |
38
|
|
|
|
|
|
|
'If problem persists, you might have to provide geo.', |
39
|
|
|
|
|
|
|
"$url error code and response:", |
40
|
|
|
|
|
|
|
$err->{code}, $err->{response}, |
41
|
|
|
|
|
|
|
); |
42
|
|
|
|
|
|
|
} |
43
|
0
|
|
|
|
|
|
return $tx->res; |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub _ip2geo { |
47
|
0
|
|
|
0
|
|
|
my ($self, $ip) = @_; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# fetch latitude and longitude from geocodeip.com |
50
|
|
|
|
|
|
|
my $geo = $self->_mk_request('http://www.geocodeip.com/', {IP => $ip}) |
51
|
|
|
|
|
|
|
->dom->find('div#data_display > table.table > tr > td') |
52
|
0
|
|
|
0
|
|
|
->grep(sub { $_->text =~ /\b(?:Latitude|Longitude)\b/; }) |
53
|
0
|
|
|
0
|
|
|
->map(sub { $_->following->first->text })->to_array; |
|
0
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
|
55
|
0
|
0
|
0
|
|
|
|
die 'Failed to scape geo from geocodeip.com. You may need to provide geo instead.' |
|
|
|
0
|
|
|
|
|
56
|
|
|
|
|
|
|
unless $geo |
57
|
|
|
|
|
|
|
&& $geo->[0] |
58
|
|
|
|
|
|
|
&& $geo->[1]; |
59
|
|
|
|
|
|
|
|
60
|
0
|
|
|
|
|
|
return $geo; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sub _geocode_ip { |
64
|
0
|
|
|
0
|
|
|
my $self = shift; |
65
|
|
|
|
|
|
|
|
66
|
0
|
0
|
|
|
|
|
return if $self->geo; |
67
|
|
|
|
|
|
|
|
68
|
0
|
|
|
|
|
|
my $ip = $self->_mk_request('http://icanhazip.com')->body; |
69
|
0
|
|
|
|
|
|
return $self->_ip2geo($ip); |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
sub run { |
73
|
0
|
|
|
0
|
1
|
|
my ($self, @args) = @_; |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
GetOptionsFromArray( |
76
|
|
|
|
|
|
|
\@args, # |
77
|
0
|
|
|
0
|
|
|
'geo|g=s' => sub { $self->geo([split ',', $_[1]]) }, |
78
|
0
|
|
|
0
|
|
|
'fresh|f' => sub { $self->fresh(1) }, |
79
|
0
|
|
|
0
|
|
|
'raw|r' => sub { $self->raw(1) }, |
80
|
0
|
|
|
|
|
|
); |
81
|
|
|
|
|
|
|
|
82
|
0
|
|
|
|
|
|
my $base_url = 'http://krispykreme.com/Locate/Location-Search'; |
83
|
0
|
|
|
|
|
|
my $hotlight_url = 'http://services.krispykreme.com/api/locationsearchresult/'; |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
# user may not provide geo |
86
|
0
|
0
|
|
|
|
|
$self->geo($self->_geocode_ip) unless $self->geo; |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# get Krispy Kreme stores |
89
|
0
|
|
|
|
|
|
my $tx = $self->ua->get( |
90
|
|
|
|
|
|
|
$hotlight_url => {Referer => $base_url} => form => { |
91
|
|
|
|
|
|
|
responseType => 'Full', |
92
|
|
|
|
|
|
|
lat => $self->geo->[0], |
93
|
|
|
|
|
|
|
lng => $self->geo->[1], |
94
|
|
|
|
|
|
|
search => encode_json( |
95
|
|
|
|
|
|
|
{ Where => { |
96
|
|
|
|
|
|
|
LocationTypes => [qw(Store Commissary Franchise)], |
97
|
|
|
|
|
|
|
OpeningDate => {"ComparisonType" => 0}, |
98
|
|
|
|
|
|
|
}, |
99
|
|
|
|
|
|
|
Take => {Min => 3, DistanceRadius => 100}, |
100
|
|
|
|
|
|
|
PropertyFilters => {Attributes => [qw(FoursquareVenueId OpeningType)]}, |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
) |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
); |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
# pull out the Location key of each store returned |
107
|
0
|
0
|
|
|
|
|
my @locations = |
108
|
0
|
|
|
|
|
|
grep { $self->fresh ? $_->{Hotlight} : 1 } |
109
|
0
|
|
|
|
|
|
map { $_->{Location} } @{$tx->res->json}; |
|
0
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
|
111
|
0
|
0
|
|
|
|
|
if ($self->raw) { |
112
|
0
|
|
|
|
|
|
say dumper \@locations; |
113
|
0
|
|
|
|
|
|
exit 1; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# print out some info for each store |
117
|
0
|
|
|
|
|
|
for my $loc (@locations) { |
118
|
0
|
|
|
|
|
|
my $addr = join ', ', grep { s/\s+$//; 1 } # |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
$loc->{Address1}, $loc->{City}, $loc->{Province}; |
120
|
|
|
|
|
|
|
|
121
|
0
|
0
|
|
|
|
|
$addr .= ' [HOTLIGHT ON]' if $loc->{Hotlight}; |
122
|
|
|
|
|
|
|
|
123
|
0
|
|
|
|
|
|
say $addr; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
1; |
128
|
|
|
|
|
|
|
__END__ |