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   69762 use 5.006;
  1         4  
4 1     1   6 use strict;
  1         2  
  1         30  
5 1     1   5 use warnings;
  1         2  
  1         24  
6              
7 1     1   5 use File::Find;
  1         10  
  1         75  
8 1     1   7 use Carp;
  1         2  
  1         66  
9 1     1   556 use Want qw/howmany/;
  1         1824  
  1         1591  
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.19';
35              
36             sub _standard_dir($);
37             sub _dir(%);
38              
39             sub random_file {
40 36620     36620 1 76892 my @params = my ($dir, $check, $recursive, $follow) = _params_random_file(@_);
41            
42 36566 100       97964 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 37356 my %args = @_;
50 18560 100       38431 my $rf = random_file(%args) or return undef;
51 16250         55993 my $dir = _standard_dir _dir %args;
52            
53 16250 50       489752 open RANDOM_FILE, "<", "$dir/$rf"
54             or die "Can't open the randomly selected file '$dir/$rf'";
55 16250         277843 my @content = ();
56 16250         143063 close RANDOM_FILE;
57 16250 100       150043 return wantarray ? @content : join "", @content;
58             }
59              
60             sub random_line {
61 345     345 1 15868 my ($fname, $nr_of_lines) = @_;
62 345 100       720 defined $fname or die "Need a defined filename to read a random line";
63 344 100 100     1129 if (!defined($nr_of_lines) and wantarray) {
64 68   100     162 $nr_of_lines = howmany() || 1;
65             }
66 344 100 100     2442 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     825 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     1282 $nr_of_lines ||= 1;
73 342         528 my @line = ();
74 342 100       9093 open FILE, '<', $fname or die "Can't open '$fname' to read random_line";
75 340 100       1075 if ($nr_of_lines == 1) {
76             # Algorithm from Cookbook, chapter 8.6
77 203   100     5071 rand($.) < 1 && ($line[0] = $_) while ();
78             } else {
79             wantarray or
80 137 100       318 carp "random_line($fname,$nr_of_lines) was called in scalar context, ".
81             "what doesn't make a lot sense";
82 137         2036 while () {
83 1096         1819 for my $lnr (0 .. $nr_of_lines-1) {
84 5640 100       12145 $line[$lnr] = $_ if rand($.) < 1;
85             }
86             }
87             }
88 340         2708 close FILE;
89 340 100       2293 return wantarray ? @line : $line[0];
90             }
91              
92             sub _random_file_non_recursive {
93 24316     24316   45437 my ($dir, $check) = @_;
94              
95 24316 100       605885 opendir DIR, $dir or die "Can't open directory '$dir'";
96 24298 100       472670 my @files = grep {-f "$dir/$_" and _valid_file($check, $_)} (readdir DIR);
  216192         2973715  
97 24298         242538 closedir DIR;
98              
99 24298 100       112228 return undef unless @files;
100 19750         173908 return $files[rand @files];
101             }
102              
103             sub _random_file_recursive {
104 12250     12250   22524 my ($dir, $check, $recursive, $follow) = @_;
105              
106 12250         17591 my $i = 1;
107 12250         14872 my $fname;
108              
109             my $accept_routine = sub {
110 196000 100   196000   5295224 return unless -f;
111            
112             # Calculate filename with relative path
113 147000         1204972 my ($f) = $File::Find::name =~ m:^$dir[/\\]*(.*)$:;
114 147000 100       387974 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       1247132 if (rand($i++) < 1) {
118 37041         518684 $fname = $f;
119             }
120 12250         43753 };
121 12250         802154 find({wanted => $accept_routine, follow => $follow}, $dir);
122              
123 12250         141492 return $fname;
124             }
125              
126             sub _valid_file {
127 275000     275000   630225 my ($check, $name) = @_;
128 275000         627344 for (ref($check)) {
129 275000 50 50     1318780 /Regexp/ && return $name =~ /$check/
      100        
130             or /CODE/ && ($_ = $name, return $check->($name));
131             }
132             }
133              
134             sub _dir (%) {
135 52834     52834   129255 my %args = @_;
136 52834   66     222907 return $args{-d} || $args{-dir} || $args{-directory};
137             }
138              
139             sub _params_random_file {
140 36620     36620   70293 my %args = @_;
141            
142 36620         65278 for (qw/-d -dir -directory -c -check/) {
143 183040 100 100     422525 exists $args{$_} and ! $args{$_} and
144             die "Parameter $_ is declared with a false value";
145             }
146            
147 36584         75844 foreach (keys %args) {
148 75635 100       291267 /^\-(d|dir|directory|
149             c|check|
150             r|rec|recursive|
151             f|follow)$/x or carp "Unknown option '$_'";
152             }
153            
154 36584         119789 my $dir = _standard_dir _dir %args;
155 36584   100 84000   150106 my $check = $args{-c} || $args{-check} || sub {"always O.K."};
  84000         269807  
156 36584   100     129161 my $recursive = $args{-r} || $args{-rec} || $args{-recursive};
157 36584   33     86307 my $follow = $args{-f} || $args{-follow};
158              
159 36584 100 50     166839 unless (!defined($check) or (scalar ref($check) =~ /^(Regexp|CODE)$/)) {
160 18         241 die "-check Parameter has to be either a Regexp or a sub routine,".
161             "not a '" . ref($check) . "'";
162             }
163            
164 36566         135082 return ($dir, $check, $recursive, $follow);
165             }
166              
167             sub _standard_dir($) {
168 52834   100 52834   114216 my $dir = shift() || '.';
169 52834         192176 $dir =~ s:[/\\]+$::;
170 52834         99407 return $dir;
171             }
172              
173             1;
174             __END__