File Coverage

blib/lib/File/Random.pm
Criterion Covered Total %
statement 83 83 100.0
branch 44 46 95.6
condition 32 37 86.4
subroutine 17 17 100.0
pod 3 3 100.0
total 179 186 96.2


line stmt bran cond sub pod time code
1             package File::Random;
2              
3 1     1   70105 use 5.006;
  1         4  
4 1     1   6 use strict;
  1         2  
  1         19  
5 1     1   5 use warnings;
  1         1  
  1         68  
6              
7 1     1   8 use File::Find;
  1         2  
  1         73  
8 1     1   8 use Carp;
  1         2  
  1         62  
9 1     1   564 use Want qw/howmany/;
  1         1842  
  1         1574  
10              
11             require Exporter;
12              
13             our @ISA = qw(Exporter);
14              
15             our %EXPORT_TAGS = (
16             ':all' => [ qw(
17             random_file
18             content_of_random_file corf
19             random_line
20             ) ],
21             # and for some backward compability
22             'all' => [ qw(
23             random_file
24             content_of_random_file corf
25             random_line
26             ) ]
27             );
28              
29             our @EXPORT_OK = ( @{ $EXPORT_TAGS{':all'} }, 'corf' );
30              
31             our @EXPORT = qw(
32            
33             );
34             our $VERSION = '0.20';
35              
36             sub _standard_dir($);
37             sub _dir(%);
38              
39             sub random_file {
40 36620     36620 1 84342 my @params = my ($dir, $check, $recursive, $follow) = _params_random_file(@_);
41            
42 36566 100       97651 return $recursive ? _random_file_recursive (@params)
43             : _random_file_non_recursive(@params);
44             }
45              
46             *corf = *content_of_random_file;
47              
48             sub content_of_random_file {
49 18560     18560 1 39281 my %args = @_;
50 18560 100       41154 my $rf = random_file(%args) or return undef;
51 16250         57810 my $dir = _standard_dir _dir %args;
52            
53 16250 50       551418 open RANDOM_FILE, "<", "$dir/$rf"
54             or die "Can't open the randomly selected file '$dir/$rf'";
55 16250         317832 my @content = ();
56 16250         162324 close RANDOM_FILE;
57 16250 100       164358 return wantarray ? @content : join "", @content;
58             }
59              
60             sub random_line {
61 345     345 1 16255 my ($fname, $nr_of_lines) = @_;
62 345 100       749 defined $fname or die "Need a defined filename to read a random line";
63 344 100 100     1112 if (!defined($nr_of_lines) and wantarray) {
64 68   100     152 $nr_of_lines = howmany() || 1;
65             }
66 344 100 100     2485 unless (!defined($nr_of_lines) or $nr_of_lines =~ /^\d+$/) {
67 2         19 die "Number of random_lines should be a number, not '$nr_of_lines'";
68             }
69 342 100 100     743 defined($nr_of_lines) and $nr_of_lines == 0 and
70             carp "doesn't make a lot of sense to return 0 random lines, " .
71             "you called me with random_line($fname,$nr_of_lines)";
72 342   100     1299 $nr_of_lines ||= 1;
73 342         496 my @line = ();
74 342 100       9932 open FILE, '<', $fname or die "Can't open '$fname' to read random_line";
75 340 100       1057 if ($nr_of_lines == 1) {
76             # Algorithm from Cookbook, chapter 8.6
77 203   100     5421 rand($.) < 1 && ($line[0] = $_) while ();
78             } else {
79             wantarray or
80 137 100       272 carp "random_line($fname,$nr_of_lines) was called in scalar context, ".
81             "what doesn't make a lot sense";
82 137         2057 while () {
83 1096         1829 for my $lnr (0 .. $nr_of_lines-1) {
84 5640 100       12238 $line[$lnr] = $_ if rand($.) < 1;
85             }
86             }
87             }
88 340         2923 close FILE;
89 340 100       2247 return wantarray ? @line : $line[0];
90             }
91              
92             sub _random_file_non_recursive {
93 24316     24316   44114 my ($dir, $check) = @_;
94              
95 24316 100       677826 opendir DIR, $dir or die "Can't open directory '$dir'";
96 24298 100       525715 my @files = grep {-f "$dir/$_" and _valid_file($check, $_)} (readdir DIR);
  216192         3295207  
97 24298         268816 closedir DIR;
98              
99 24298 100       123545 return undef unless @files;
100 19750         181124 return $files[rand @files];
101             }
102              
103             sub _random_file_recursive {
104 12250     12250   23416 my ($dir, $check, $recursive, $follow) = @_;
105              
106 12250         17187 my $i = 1;
107 12250         15229 my $fname;
108              
109             my $accept_routine = sub {
110 196000 100   196000   5846973 return unless -f;
111            
112             # Calculate filename with relative path
113 147000         1271371 my ($f) = $File::Find::name =~ m:^$dir[/\\]*(.*)$:;
114 147000 100       386668 return unless _valid_file($check,$f);
115             # Algorithm from Cookbook, chapter 8.6
116             # similar to selecting a random line from a file
117 138000 100       1315986 if (rand($i++) < 1) {
118 36886         539375 $fname = $f;
119             }
120 12250         41799 };
121 12250         866851 find({wanted => $accept_routine, follow => $follow}, $dir);
122              
123 12250         151175 return $fname;
124             }
125              
126             sub _valid_file {
127 275000     275000   627923 my ($check, $name) = @_;
128 275000         712967 for (ref($check)) {
129 275000 50 50     1377609 /Regexp/ && return $name =~ /$check/
      100        
130             or /CODE/ && ($_ = $name, return $check->($name));
131             }
132             }
133              
134             sub _dir (%) {
135 52834     52834   127026 my %args = @_;
136 52834   66     227518 return $args{-d} || $args{-dir} || $args{-directory};
137             }
138              
139             sub _params_random_file {
140 36620     36620   71871 my %args = @_;
141            
142 36620         67280 for (qw/-d -dir -directory -c -check/) {
143 183040 100 100     426517 exists $args{$_} and ! $args{$_} and
144             die "Parameter $_ is declared with a false value";
145             }
146            
147 36584         69174 foreach (keys %args) {
148 75635 100       302873 /^\-(d|dir|directory|
149             c|check|
150             r|rec|recursive|
151             f|follow)$/x or carp "Unknown option '$_'";
152             }
153            
154 36584         113078 my $dir = _standard_dir _dir %args;
155 36584   100 84000   152935 my $check = $args{-c} || $args{-check} || sub {"always O.K."};
  84000         283374  
156 36584   100     127302 my $recursive = $args{-r} || $args{-rec} || $args{-recursive};
157 36584   33     87666 my $follow = $args{-f} || $args{-follow};
158              
159 36584 100 50     176329 unless (!defined($check) or (scalar ref($check) =~ /^(Regexp|CODE)$/)) {
160 18         234 die "-check Parameter has to be either a Regexp or a sub routine,".
161             "not a '" . ref($check) . "'";
162             }
163            
164 36566         133131 return ($dir, $check, $recursive, $follow);
165             }
166              
167             sub _standard_dir($) {
168 52834   100 52834   112024 my $dir = shift() || '.';
169 52834         193574 $dir =~ s:[/\\]+$::;
170 52834         90173 return $dir;
171             }
172              
173             1;
174             __END__