File Coverage

blib/lib/Data/Tranco.pm
Criterion Covered Total %
statement 111 114 97.3
branch 11 24 45.8
condition 1 3 33.3
subroutine 27 27 100.0
pod 1 9 11.1
total 151 177 85.3


line stmt bran cond sub pod time code
1             package Data::Tranco;
2             # ABSTRACT: An interface to the Tranco domain list.
3 1     1   297129 use Archive::Zip qw(:ERROR_CODES);
  1         84441  
  1         132  
4 1     1   537 use Archive::Zip::MemberRead;
  1         2716  
  1         44  
5 1     1   8 use Carp;
  1         1  
  1         76  
6 1     1   1121 use DBD::SQLite;
  1         38001  
  1         39  
7 1     1   8 use DBI;
  1         3  
  1         42  
8 1     1   780 use Data::Mirror qw(mirror_file);
  1         175247  
  1         185  
9 1     1   17 use File::Basename qw(basename dirname);
  1         4  
  1         66  
10 1     1   10 use File::Spec;
  1         2  
  1         25  
11 1     1   5 use File::stat;
  1         46  
  1         77  
12 1     1   6 use POSIX qw(getlogin);
  1         3  
  1         14  
13 1     1   150 use Text::CSV_XS;
  1         3  
  1         54  
14 1     1   6 use constant TRANCO_URL => 'https://tranco-list.eu/top-1m.csv.zip';
  1         3  
  1         105  
15 1     1   7 use feature qw(state);
  1         3  
  1         179  
16 1     1   7 use open qw(:encoding(utf8));
  1         1  
  1         11  
17 1     1   3629 use strict;
  1         10  
  1         29  
18 1     1   6 use utf8;
  1         4  
  1         20  
19 1     1   39 use vars qw($TTL $ZIPFILE $DBFILE $DSN $STATIC);
  1         2  
  1         80  
20 1     1   5 use warnings;
  1         2  
  1         1745  
21              
22             $TTL = 86400;
23             $ZIPFILE = mirror_file(TRANCO_URL);
24             $DBFILE = File::Spec->catfile(dirname($ZIPFILE), basename($ZIPFILE, '.zip').'.db');
25             $DSN = 'dbi:SQLite:dbname='.$DBFILE;
26             $STATIC = undef;
27              
28              
29             sub random_domain {
30 1     1 0 3233 my ($package, $suffix) = @_;
31              
32 1         9 state $sth = $package->get_db->prepare(q{
33             SELECT * FROM `domains`
34             WHERE `domain` LIKE ?
35             ORDER BY RANDOM()
36             LIMIT 0,1});
37              
38 1 50       515651 $sth->execute($suffix ? '%.'.$suffix : '%');
39              
40 1         1016 return reverse($sth->fetchrow_array);
41             }
42              
43              
44             sub top_domain {
45 1     1 0 1341 my ($package, $suffix) = @_;
46              
47 1         7 state $sth = $package->get_db->prepare(q{
48             SELECT * FROM `domains`
49             WHERE `domain` LIKE ?
50             ORDER BY `id`
51             LIMIT 0,1});
52              
53 1 50       390 $sth->execute($suffix ? '%.'.$suffix : '%');
54              
55 1         33 return reverse($sth->fetchrow_array);
56             }
57              
58              
59             sub sample {
60 1     1 0 860 my ($package, $count, $suffix) = @_;
61              
62 1         5 state $sth = $package->get_db->prepare(q{
63             SELECT `domain` FROM `domains`
64             WHERE `domain` LIKE ?
65             ORDER BY RANDOM()
66             LIMIT 0,?});
67              
68 1 50       457795 $sth->execute($suffix ? '%.'.$suffix : '%', int($count));
69              
70 1         9 my @domains;
71              
72 1         33 while (my @row = $sth->fetchrow_array) {
73 5         64 push(@domains, @row);
74             }
75              
76 1         14 return @domains;
77             }
78              
79              
80             sub top_domains {
81 1     1 0 1139 my ($package, $count, $suffix) = @_;
82              
83 1         7 state $sth = $package->get_db->prepare(q{
84             SELECT `domain` FROM `domains`
85             WHERE `domain` LIKE ?
86             ORDER BY `id`
87             LIMIT 0,?});
88              
89 1 50       336 $sth->execute($suffix ? '%.'.$suffix : '%', int($count));
90              
91 1         6 my @domains;
92              
93 1         37 while (my @row = $sth->fetchrow_array) {
94 5         44 push(@domains, @row);
95             }
96              
97 1         10 return @domains;
98             }
99              
100              
101             sub all {
102 1     1 0 923 my ($package, $suffix) = @_;
103              
104 1         7 state $sth = $package->get_db->prepare(q{
105             SELECT `domain` FROM `domains`
106             WHERE `domain` LIKE ?
107             ORDER BY `id`});
108              
109 1 50       288 $sth->execute($suffix ? '%.'.$suffix : '%');
110              
111 1         6 my @domains;
112              
113 1         71 while (my @row = $sth->fetchrow_array) {
114 15         329883 push(@domains, @row);
115             }
116              
117 1         20 return @domains;
118             }
119              
120              
121             sub rank {
122 1     1 0 1142 my ($package, $domain) = @_;
123              
124 1         6 state $sth = $package->get_db->prepare(q{
125             SELECT `id`
126             FROM `domains`
127             WHERE (`domain`=?)
128             });
129              
130 1         306 $sth->execute($domain);
131              
132 1         31 return $sth->fetchrow_array;
133             }
134              
135              
136             sub get_db {
137 6     6 0 18 my $package = shift;
138              
139 6         14 state $db;
140              
141 6 100       32 if (!$db) {
142 1 50       4 $package->update_db if ($package->needs_update);
143            
144 1         20 $db = DBI->connect($DSN);
145             }
146              
147 6         993 return $db;
148             }
149              
150              
151             #
152             # returns true if the database needs updating, that is:
153             #
154             # 0. $STATIC is not defined
155             # 1. the DB file doesn't exist
156             # 2. the zip file doesn't exist
157             # 3. the DB file is older than the zip file
158             # 4. the zip file is more than TTL seconds old
159             #
160             sub needs_update {
161 1     1 0 2 my $package = shift;
162              
163 1 50       17 return undef if ($STATIC);
164              
165 1 50 33     90 return 1 unless (-e $DBFILE && -e $ZIPFILE);
166 0 0       0 return 1 unless (stat($DBFILE)->mtime > stat($ZIPFILE)->mtime);
167 0 0       0 return 1 unless (stat($ZIPFILE)->mtime > time() - $TTL);
168              
169 0         0 return undef;
170             }
171              
172             sub update_db {
173 1     1 1 3 my $package = shift;
174              
175 1         6 mirror_file(TRANCO_URL, $TTL);
176              
177 1         449 my $zip = Archive::Zip->new;
178              
179 1 50       56 croak('Zip read error') unless ($zip->read($ZIPFILE) == AZ_OK);
180              
181 1         1056 my $db = DBI->connect($DSN, undef, undef, { AutoCommit => 0 });
182              
183 1         4730 $db->do(q{
184             CREATE TABLE IF NOT EXISTS `domains` (
185             `id` INTEGER PRIMARY KEY,
186             `domain` TEXT UNIQUE COLLATE NOCASE
187             )
188             });
189              
190 1         950 my $sth = $db->prepare(q{INSERT INTO `domains` (`id`, `domain`) VALUES (?, ?)});
191              
192 1         121 $db->do(q{DELETE FROM `domains`});
193              
194 1         148 my $fh = Archive::Zip::MemberRead->new($zip, basename(TRANCO_URL, '.zip'));
195 1         1225 my $csv = Text::CSV_XS->new;
196 1         136 while (my $row = $csv->getline($fh)) {
197 1000000         60197143 $sth->execute(@{$row});
  1000000         62975696  
198             }
199              
200 1         424148 $db->commit;
201 1         819 $db->disconnect;
202             }
203              
204             1;
205              
206             __END__