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   77662 use 5.006;
  1         4  
4 1     1   6 use strict;
  1         2  
  1         24  
5 1     1   4 use warnings;
  1         2  
  1         30  
6              
7 1     1   5 use File::Find;
  1         2  
  1         88  
8 1     1   8 use Carp;
  1         1  
  1         70  
9 1     1   620 use Want qw/howmany/;
  1         2118  
  1         1816  
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.21';
35              
36             sub _standard_dir($);
37             sub _dir(%);
38              
39             sub random_file {
40 36620     36620 1 74718 my @params = my ($dir, $check, $recursive, $follow) = _params_random_file(@_);
41            
42 36566 100       94172 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 41826 my %args = @_;
50 18560 100       39998 my $rf = random_file(%args) or return undef;
51 16250         57718 my $dir = _standard_dir _dir %args;
52            
53 16250 50       518079 open RANDOM_FILE, "<", "$dir/$rf"
54             or die "Can't open the randomly selected file '$dir/$rf'";
55 16250         289397 my @content = ();
56 16250         151592 close RANDOM_FILE;
57 16250 100       153312 return wantarray ? @content : join "", @content;
58             }
59              
60             sub random_line {
61 345     345 1 16329 my ($fname, $nr_of_lines) = @_;
62 345 100       789 defined $fname or die "Need a defined filename to read a random line";
63 344 100 100     1131 if (!defined($nr_of_lines) and wantarray) {
64 68   100     166 $nr_of_lines = howmany() || 1;
65             }
66 344 100 100     2676 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     799 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     1447 $nr_of_lines ||= 1;
73 342         519 my @line = ();
74 342 100       10443 open FILE, '<', $fname or die "Can't open '$fname' to read random_line";
75 340 100       1145 if ($nr_of_lines == 1) {
76             # Algorithm from Cookbook, chapter 8.6
77 203   100     5434 rand($.) < 1 && ($line[0] = $_) while ();
78             } else {
79             wantarray or
80 137 100       291 carp "random_line($fname,$nr_of_lines) was called in scalar context, ".
81             "what doesn't make a lot sense";
82 137         2092 while () {
83 1096         1920 for my $lnr (0 .. $nr_of_lines-1) {
84 5640 100       12514 $line[$lnr] = $_ if rand($.) < 1;
85             }
86             }
87             }
88 340         3046 close FILE;
89 340 100       2522 return wantarray ? @line : $line[0];
90             }
91              
92             sub _random_file_non_recursive {
93 24316     24316   40965 my ($dir, $check) = @_;
94              
95 24316 100       647295 opendir DIR, $dir or die "Can't open directory '$dir'";
96 24298 100       498929 my @files = grep {-f "$dir/$_" and _valid_file($check, $_)} (readdir DIR);
  216192         3224220  
97 24298         255218 closedir DIR;
98              
99 24298 100       117084 return undef unless @files;
100 19750         178844 return $files[rand @files];
101             }
102              
103             sub _random_file_recursive {
104 12250     12250   22356 my ($dir, $check, $recursive, $follow) = @_;
105              
106 12250         15629 my $i = 1;
107 12250         14920 my $fname;
108              
109             my $accept_routine = sub {
110 196000 100   196000   5659037 return unless -f;
111            
112             # Calculate filename with relative path
113 147000         1255251 my ($f) = $File::Find::name =~ m:^$dir[/\\]*(.*)$:;
114 147000 100       397169 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       1241332 if (rand($i++) < 1) {
118 37049         526198 $fname = $f;
119             }
120 12250         43408 };
121 12250         822704 find({wanted => $accept_routine, follow => $follow}, $dir);
122              
123 12250         143077 return $fname;
124             }
125              
126             sub _valid_file {
127 275000     275000   626043 my ($check, $name) = @_;
128 275000         694760 for (ref($check)) {
129 275000 50 50     1342366 /Regexp/ && return $name =~ /$check/
      100        
130             or /CODE/ && ($_ = $name, return $check->($name));
131             }
132             }
133              
134             sub _dir (%) {
135 52834     52834   130615 my %args = @_;
136 52834   66     220158 return $args{-d} || $args{-dir} || $args{-directory};
137             }
138              
139             sub _params_random_file {
140 36620     36620   72120 my %args = @_;
141            
142 36620         66414 for (qw/-d -dir -directory -c -check/) {
143 183040 100 100     406524 exists $args{$_} and ! $args{$_} and
144             die "Parameter $_ is declared with a false value";
145             }
146            
147 36584         67611 foreach (keys %args) {
148 75635 100       296701 /^\-(d|dir|directory|
149             c|check|
150             r|rec|recursive|
151             f|follow)$/x or carp "Unknown option '$_'";
152             }
153            
154 36584         113255 my $dir = _standard_dir _dir %args;
155 36584   100 84000   151950 my $check = $args{-c} || $args{-check} || sub {"always O.K."};
  84000         283008  
156 36584   100     117575 my $recursive = $args{-r} || $args{-rec} || $args{-recursive};
157 36584   33     81787 my $follow = $args{-f} || $args{-follow};
158              
159 36584 100 50     163421 unless (!defined($check) or (scalar ref($check) =~ /^(Regexp|CODE)$/)) {
160 18         227 die "-check Parameter has to be either a Regexp or a sub routine,".
161             "not a '" . ref($check) . "'";
162             }
163            
164 36566         131198 return ($dir, $check, $recursive, $follow);
165             }
166              
167             sub _standard_dir($) {
168 52834   100 52834   108402 my $dir = shift() || '.';
169 52834         187062 $dir =~ s:[/\\]+$::;
170 52834         88731 return $dir;
171             }
172              
173             1;
174             __END__