File Coverage

blib/lib/File/Stat/Moose.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -c
2              
3             package File::Stat::Moose;
4              
5             =head1 NAME
6              
7             File::Stat::Moose - Status info for a file - Moose-based
8              
9             =head1 SYNOPSIS
10              
11             use File::Stat::Moose;
12             $st = File::Stat::Moose->new( file => '/etc/passwd' );
13             print "Size: ", $st->size, "\n"; # named attribute
14             print "Blocks: ". $st->[12], "\n"; # numbered attribute
15              
16             =head1 DESCRIPTION
17              
18             This class provides methods that returns status info for a file. It is the
19             OO-style version of stat/lstat functions. It also throws an exception
20             immediately after error is occurred.
21              
22             =for readme stop
23              
24             =cut
25              
26              
27 1     1   5161 use 5.008;
  1         14  
  1         41  
28 1     1   5 use strict;
  1         2  
  1         36  
29 1     1   14 use warnings FATAL => 'all';
  1         2  
  1         65  
30              
31             our $VERSION = '0.06';
32              
33 1     1   414 use Moose;
  0            
  0            
34              
35             # Additional types
36             use MooseX::Types::OpenHandle;
37             use MooseX::Types::CacheFileHandle;
38              
39             # Run-time Assertions
40             use Test::Assert ':assert';
41              
42             # TRUE/FALSE
43             use constant::boolean;
44              
45             # atime, ctime, mtime attributes
46             use DateTime;
47              
48             use Scalar::Util 'reftype';
49              
50              
51             use Exception::Base (
52             '+ignore_package' => [ __PACKAGE__, qr/^File::Spec(::|$)/, 'Sub::Exporter', qr/^Moose::/, qr/^Class::MOP::/ ],
53             );
54             use Exception::Argument;
55             use Exception::IO;
56              
57              
58             use overload (
59             '@{}' => '_deref_array',
60             fallback => TRUE,
61             );
62              
63              
64             use Sub::Exporter -setup => {
65             exports => [
66              
67             # Get file status
68             stat => sub {
69             sub (;*) {
70             my $st = __PACKAGE__->new(
71             file => (defined $_[0] ? $_[0] : $_),
72             follow => TRUE,
73             );
74             return wantarray ? @{ $st } : $st;
75             };
76             },
77              
78             # Get link status
79             lstat => sub {
80             sub (;*) {
81             my $st = __PACKAGE__->new(
82             file => (defined $_[0] ? $_[0] : $_),
83             follow => FALSE,
84             );
85             return wantarray ? @{ $st } : $st;
86             };
87             },
88              
89             ],
90             groups => { all => [ qw{ stat lstat } ] },
91             };
92              
93              
94             # File which is checked with stat
95             has 'file' => (
96             is => 'ro',
97             isa => 'Str | FileHandle | CacheFileHandle | OpenHandle',
98             required => TRUE,
99             predicate => 'has_file',
100             );
101              
102             # Follow symlink or read symlink itself
103             has 'follow' => (
104             is => 'ro',
105             isa => 'Bool',
106             default => FALSE,
107             );
108              
109             # Speeds up stat on Win32
110             has 'sloppy' => (
111             is => 'ro',
112             isa => 'Bool',
113             default => FALSE,
114             );
115              
116             # Use accessors rather than direct hash
117             has 'strict_accessors' => (
118             is => 'rw',
119             isa => 'Bool',
120             default => FALSE,
121             );
122              
123             {
124             foreach my $attr ( qw{ dev ino mode nlink uid gid rdev size blksize blocks } ) {
125              
126             # Numeric informations about a file
127             has "$attr" => (
128             is => 'ro',
129             isa => 'Maybe[Int]',
130             writer => "_set_$attr",
131             );
132             };
133              
134             };
135              
136             {
137             foreach my $attr ( qw{ atime mtime ctime } ) {
138              
139             my $reader = "_get_${attr}_epoch";
140              
141             # Numeric informations about a file (time as unix timestamp)
142             has "_${attr}_epoch" => (
143             isa => 'Maybe[Int]',
144             reader => $reader,
145             writer => "_set_${attr}_epoch",
146             );
147              
148             # Time as DateTime object (lazy evaluationed)
149             has "$attr" => (
150             is => 'ro',
151             isa => 'Maybe[DateTime]',
152             lazy => TRUE,
153             default => sub {
154             defined $_[0]->$reader
155             ? DateTime->from_epoch( epoch => $_[0]->$reader )
156             : undef
157             },
158             clearer => "_clear_$attr",
159             predicate => "has_$attr",
160             );
161              
162             };
163             };
164              
165              
166             ## no critic (ProhibitBuiltinHomonyms)
167             ## no critic (RequireArgUnpacking)
168              
169             # Object initialization
170             sub BUILD {
171             my ($self, $params) = @_;
172              
173             assert_not_null($self->file) if ASSERT;
174              
175             $self->_init_stat;
176              
177             return $self;
178             };
179              
180              
181             # Call stat method
182             sub _init_stat {
183             my ($self) = @_;
184              
185             return $self->stat;
186             };
187              
188              
189             # Call stat or lstat method
190             sub stat {
191             my $self = shift;
192             Exception::Argument->throw( message => 'Usage: $st->stat()' ) if @_ > 0 or not blessed $self;
193              
194             my $file = $self->file;
195             assert_not_null($file) if ASSERT;
196              
197             # Clear lazy attributes
198             if ($self->strict_accessors) {
199             foreach my $attr (qw{ atime mtime ctime }) {
200             my $clearer = "_clear_$attr";
201             $self->$clearer;
202             };
203             }
204             else {
205             delete @{$self}{ qw{ _atime_epoch _mtime_epoch _ctime_epoch } };
206             };
207              
208             local ${^WIN32_SLOPPY_STAT} = $self->sloppy;
209              
210             if ($self->follow or (ref $file || '') eq 'GLOB' or (reftype $file || '') eq 'GLOB') {
211             if ($self->strict_accessors) {
212             my %stat;
213             @stat{ qw{ dev ino mode nlink uid gid rdev size atime mtime ctime blksize blocks } }
214             = map { defined $_ && $_ eq '' ? undef : $_ }
215             CORE::stat $file or Exception::IO->throw( message => 'Cannot stat' );
216              
217             foreach my $attr (qw{ dev ino mode nlink uid gid rdev size blksize blocks }) {
218             my $writer = "_set_$attr";
219             $self->$writer( $stat{$attr} );
220             };
221             foreach my $attr (qw{ atime mtime ctime }) {
222             my $writer = "_set_${attr}_epoch";
223             $self->$writer( $stat{$attr} );
224             };
225             }
226             else {
227             @{$self}{ qw{ dev ino mode nlink uid gid rdev size _atime_epoch _mtime_epoch _ctime_epoch blksize blocks } }
228             = map { defined $_ && $_ eq '' ? undef : $_ }
229             CORE::stat $file or Exception::IO->throw( message => 'Cannot stat' );
230             };
231             }
232             else {
233             no warnings 'io'; # lstat() on filehandle
234              
235             if ($self->strict_accessors) {
236             my %stat;
237             @stat{ qw{ dev ino mode nlink uid gid rdev size atime mtime ctime blksize blocks } }
238             = map { defined $_ && $_ eq '' ? undef : $_ }
239             CORE::lstat $file or Exception::IO->throw( message => 'Cannot stat' );
240              
241             foreach my $attr (qw{ dev ino mode nlink uid gid rdev size blksize blocks }) {
242             my $writer = "_set_$attr";
243             $self->$writer( $stat{$attr} );
244             };
245             foreach my $attr (qw{ atime mtime ctime }) {
246             my $writer = "_set_${attr}_epoch";
247             $self->$writer( $stat{$attr} );
248             };
249             }
250             else {
251             @{$self}{ qw{ dev ino mode nlink uid gid rdev size _atime_epoch _mtime_epoch _ctime_epoch blksize blocks } }
252             = map { defined $_ && $_ eq '' ? undef : $_ }
253             CORE::lstat $file or Exception::IO->throw( message => 'Cannot stat' );
254             };
255             };
256              
257             return $self;
258             };
259              
260              
261             # Array dereference
262             sub _deref_array {
263             my ($self) = @_;
264              
265             my @stat;
266             if ($self->strict_accessors) {
267             foreach my $attr (qw{ dev ino mode nlink uid gid rdev size blksize blocks }) {
268             my $reader = $attr;
269             push @stat, $self->$reader;
270             };
271             foreach my $attr (qw{ atime mtime ctime }) {
272             my $reader = "_get_${attr}_epoch";
273             push @stat, $self->$reader;
274             };
275             }
276             else {
277             @stat = @{$self}{ qw{ dev ino mode nlink uid gid rdev size _atime_epoch _mtime_epoch _ctime_epoch blksize blocks } }
278             };
279              
280             return \@stat;
281             };
282              
283              
284             # Module initialization
285             __PACKAGE__->meta->make_immutable();
286              
287              
288             1;
289              
290              
291             __END__
292              
293             =begin umlwiki
294              
295             = Component Diagram =
296              
297             [ <<library>> {=}
298             File::Stat::Moose
299             ---------------------------------
300             File::Stat::Moose
301             MooseX::Types::OpenHandle
302             MooseX::Types::CacheFileHandle
303             <<exception>> Exception::IO
304             <<type>> OpenHandle
305             <<type>> CacheFileHandle ]
306              
307             = Class Diagram =
308              
309             [ File::Stat::Moose
310             ----------------------------------------------------------------------------------------
311             +file : Str|FileHandle|CacheFileHandle|OpenHandle {ro, required}
312             +follow : Bool = false {ro}
313             +sloppy : Bool = false {ro}
314             +strict_accessors : Bool = false {rw}
315             +dev : Maybe[Int] {ro}
316             +ino : Maybe[Int] {ro}
317             +mode : Maybe[Int] {ro}
318             +nlink : Maybe[Int] {ro}
319             +uid : Maybe[Int] {ro}
320             +gid : Maybe[Int] {ro}
321             +rdev : Maybe[Int] {ro}
322             +size : Maybe[Int] {ro}
323             +atime : Maybe[DateTime] {ro, lazy}
324             +mtime : Maybe[DateTime] {ro, lazy}
325             +ctime : Maybe[DateTime] {ro, lazy}
326             +blksize : Maybe[Int] {ro}
327             +blocks : Maybe[Int] {ro}
328             #_atime_epoch : Maybe[Int] {ro}
329             #_mtime_epoch : Maybe[Int] {ro}
330             #_ctime_epoch : Maybe[Int] {ro}
331             ----------------------------------------------------------------------------------------
332             +stat() : Self
333             <<utility>> +stat( file : Str|FileHandle|CacheFileHandle|OpenHandle = $_ ) : Self|Array
334             <<utility>> +lstat( file : Str|FileHandle|CacheFileHandle|OpenHandle = $_ ) : Self|Array
335             -_deref_array() : ArrayRef {overload="@{}"}
336             ]
337              
338             [File::Stat::Moose] ---> <<exception>> [Exception::Argument] [Exception::IO]
339              
340             =end umlwiki
341              
342             =head1 IMPORTS
343              
344             By default, the class does not export its symbols.
345              
346             =over
347              
348             =item stat
349              
350             =item lstat
351              
352             Imports C<stat> and/or C<lstat> functions.
353              
354             use File::Stat::Moose 'stat', 'lstat';
355              
356             =item :all
357              
358             Imports all available symbols.
359              
360             use File::Stat::Moose ':all';
361              
362             =back
363              
364             =head1 INHERITANCE
365              
366             =over 2
367              
368             =item *
369              
370             extends L<Moose::Object>
371              
372             =back
373              
374             =head1 EXCEPTIONS
375              
376             =over
377              
378             =item L<Exception::Argument>
379              
380             Thrown whether a methods is called with wrong arguments.
381              
382             =item L<Exception::IO>
383              
384             Thrown whether an IO error is occurred.
385              
386             =back
387              
388             =head1 ATTRIBUTES
389              
390             =over
391              
392             =item file : Str|FileHandle|CacheFileHandle|OpenHandle {ro, required}
393              
394             Contains the file for check. The attribute can hold file name or file
395             handler or IO object.
396              
397             =item follow : Bool = false {ro}
398              
399             If the value is true and the I<file> for check is symlink, then follows it
400             than checking the symlink itself.
401              
402             =item sloppy : Bool = false {ro}
403              
404             On Win32 L<perlfunc/stat> needs to open the file to determine the link count
405             and update attributes that may have been changed through hard links. If the
406             I<sloppy> is set to true value, L<perlfunc/stat> speeds up by not performing
407             this operation.
408              
409             =item strict_accessors : Bool = false {rw}
410              
411             By default the accessors might be avoided for performance reason. This
412             optimization can be disabled if the attribute is set to true value.
413              
414             =item dev : Maybe[Int] {ro}
415              
416             ID of device containing file. If this value and following has no meaning on
417             the platform, it will contain undefined value.
418              
419             =item ino : Maybe[Int] {ro}
420              
421             inode number.
422              
423             =item mode : Maybe[Int] {ro}
424              
425             Unix mode for file.
426              
427             =item nlink : Maybe[Int] {ro}
428              
429             Number of hard links.
430              
431             =item uid : Maybe[Int] {ro}
432              
433             User ID of owner.
434              
435             =item gid : Maybe[Int] {ro}
436              
437             Group ID of owner.
438              
439             =item rdev : Maybe[Int] {ro}
440              
441             Device ID (if special file).
442              
443             =item size : Maybe[Int] {ro}
444              
445             Total size, in bytes.
446              
447             =item atime : Maybe[DateTime] {ro}
448              
449             Time of last access as DateTime object.
450              
451             =item mtime : Maybe[DateTime] {ro}
452              
453             Time of last modification as DateTime object.
454              
455             =item ctime : Maybe[DateTime] {ro}
456              
457             Time of last status change as DateTime object.
458              
459             =item blksize : Maybe[Int] {ro}
460              
461             Block size for filesystem I/O.
462              
463             =item blocks : Maybe[Int] {ro}
464              
465             Number of blocks allocated.
466              
467             =back
468              
469             =head1 OVERLOADS
470              
471             =over
472              
473             =item Array dereferencing
474              
475             If C<File::Stat::Moose> object is dereferenced as array it returns an array
476             with the same order of values as in L<perlfunc/stat> or L<perlfunc/lstat>
477             functions. Attributes C<atime>, C<ctime> and C<mtime> are returned as number
478             values (Unix timestamp).
479              
480             $st = File::Stat::Moose->new( file => '/etc/passwd' );
481             @st = @$st;
482              
483             =back
484              
485             =head1 CONSTRUCTORS
486              
487             =over
488              
489             =item new( I<args> : Hash ) : Self
490              
491             Creates the C<File::Stat::Moose> object and calls C<update> method.
492              
493             If the I<file> is symlink and the I<follow> is true, it will check the file
494             that it refers to. If the I<follow> is false, it will check the symlink
495             itself.
496              
497             $st = File::Stat::Moose->new( file => '/etc/cdrom', follow => 1 );
498             print "Device: ", $st->rdev, "\n"; # check real device, not symlink
499              
500             The object is dereferenced in array context to the array reference which
501             contains the same values as L<perlfunc/stat> function output.
502              
503             $st = File::Stat::Moose->new( file => '/etc/passwd' );
504             print "Size: ", $st->size, "\n"; # object's attribute
505             print "Size: ", $st->[7], "\n"; # array dereference
506              
507             =back
508              
509             =head1 METHODS
510              
511             =over
512              
513             =item stat(I<>) : Self
514              
515             Updates all attributes which represent status of file.
516              
517             Calls L<perlfunc/stat> function if C<follow> method is true value or
518             L<perlfunc/lstat> function otherwise.
519              
520             =back
521              
522             =head1 FUNCTIONS
523              
524             =over
525              
526             =item stat( I<file> : Str|FileHandle|CacheFileHandle|OpenHandle = $_ ) : Self|Array
527              
528             Calls stat on given I<file>. If the I<file> is undefined, the C<$_> variable
529             is used instead.
530              
531             If it is called in array context, it returns an array with the same values as
532             for output of core C<stat> function.
533              
534             use File::Stat::Moose 'stat';
535             $_ = '/etc/passwd';
536             @st = stat;
537             print "Size: $st[7]\n";
538              
539             If it is called with scalar context, it returns the C<File::Stat::Moose>
540             object.
541              
542             use File::Stat::Moose 'stat';
543             $st = stat '/etc/passwd';
544             @st = @$st;
545              
546             =item lstat( I<file> : Str|FileHandle|CacheFileHandle|OpenHandle = $_ ) : Self|Array
547              
548             It is identical to C<stat>, except that if I<file> is a symbolic link, then
549             the link itself is checked, not the file that it refers to.
550              
551             use File::Stat::Moose 'lstat';
552             @st = lstat '/etc/motd';
553              
554             =back
555              
556             =head1 BUGS
557              
558             C<stat> and C<lstat> functions does not accept special handler C<_> written
559             as bareword. You have to use it as a glob reference C<\*_>.
560              
561             use File::Stat::Moose 'stat';
562             stat "/etc/passwd"; # set the special filehandle _
563             @st = stat _; # does not work
564             @st = stat \*_; # ok
565              
566             =head1 PERFORMANCE
567              
568             The L<File::Stat::Moose> module is 4 times slower than L<File::stat>
569             module and 30 times slower than L<perlfunc/stat> function. The function
570             interface is 1.5 times slower than OO interface. The strict accessors are
571             2.5 times slower that optimized direct access to hash.
572              
573             =head1 SEE ALSO
574              
575             L<Exception::Base>, L<MooseX::Types::OpenHandle>,
576             L<MooseX::Types::CacheFileHandle>, L<Moose>, L<File::stat>, L<DateTime>.
577              
578             =for readme continue
579              
580             =head1 AUTHOR
581              
582             Piotr Roszatycki <dexter@cpan.org>
583              
584             =head1 LICENSE
585              
586             Copyright (C) 2007, 2008, 2009 by Piotr Roszatycki <dexter@cpan.org>.
587              
588             This program is free software; you can redistribute it and/or modify it
589             under the same terms as Perl itself.
590              
591             See L<http://www.perl.com/perl/misc/Artistic.html>