Author: particle
Date: Wed Jan 24 18:09:38 2007
New Revision: 16784

Modified:
   trunk/lib/Parrot/Distribution.pm

Log:
[lib]: refactoring Parrot::Distribution.pm -- part 1
~ apply DRY to source and documentation
~ separate object create and initialization to better allow subclass creation
~ memoize where appropriate for efficiency
~ a few doc and whitespace fixes

Modified: trunk/lib/Parrot/Distribution.pm
==============================================================================
--- trunk/lib/Parrot/Distribution.pm    (original)
+++ trunk/lib/Parrot/Distribution.pm    Wed Jan 24 18:09:38 2007
@@ -41,6 +41,7 @@
 use Parrot::Docs::Directory;
 use base qw(Parrot::Docs::Directory);
 
+
 =item C<new()>
 
 Searches up the file system tree from the current working directory
@@ -54,393 +55,232 @@
 
 =cut
 
+## i'm a singleton
 my $dist;
 
+
 sub new {
-    my $self = shift;
+    my( $class ) = @_;
 
     return $dist if defined $dist;
+    my $self = bless {}, $class;
+    return $self->_initialize;
+}
 
+
+sub _initialize {
+    my( $self ) = @_;
+
+    my $file = 'README';
     my $path = '.';
 
     while ( $self = $self->SUPER::new($path) ) {
-        return $dist = $self
-            if $self->file_exists_with_name('README')
-            and $self->file_with_name('README')->read =~ m/^This is Parrot/os;
+        if (
+            $self->file_exists_with_name($file)
+            and $self->file_with_name($file)->read =~ m/^This is Parrot/os
+        ) {
+            $dist = $self;
+            last;
+        }
 
         $path = $self->parent_path();
     }
 
-    die "Failed to find Parrot distribution root\n";
-}
-
-=back
-
-=head2 Instance Methods
-
-=over 4
-
-=item C<c_source_file_directories()>
-
-Returns the directories which contain C source files.
-
-=cut
-
-sub c_source_file_directories {
-    my $self = shift;
-
-    my %c_source_dirs =
-
-        # Make a hash out of the directories of those files
-        map { ( ( File::Spec->splitpath($_) )[1] => 1 ) }
-
-        # Only look at files ending in .c
-        grep { m|\.c$| }
-
-        keys %{ ExtUtils::Manifest::maniread( File::Spec->catfile( 
$self->path, "MANIFEST" ) ) };
-
-    return map $self->directory_with_name($_) => grep { !m|\.svn/$| }
-        sort keys %c_source_dirs;
-}
-
-=item C<c_source_file_with_name($name)>
-
-Returns the C source file with the specified name.
-
-=cut
-
-sub c_source_file_with_name {
-    my $self = shift;
-    my $name = shift || return;
+    # non-object call syntax since $self is undefined
+    _croak( undef, "Failed to find Parrot distribution root\n" )
+        unless $self;
 
-    $name .= '.c' unless $name =~ /\.[Cc]$/o;
-
-    foreach my $dir ( $self->c_source_file_directories ) {
-        return $dir->file_with_name($name)
-            if $dir->file_exists_with_name($name);
+    if( defined $dist ) {
+        $self->_manifest_files( [
+            sort keys %{ ExtUtils::Manifest::maniread(
+                File::Spec->catfile( $self->path, "MANIFEST" )
+            ) },
+        ] );
     }
 
-    print 'WARNING: ' . __FILE__ . ':' . __LINE__ . ' File not found:' . $name 
. "\n";
-
-    return;
-}
-
-=item C<c_source_files()>
-
-Returns a sorted list of the C source files listed within the MANIFEST of
-Parrot.  Returns a list of Parrot::IO::File objects.
-
-=cut
-
-sub c_source_files {
-    my $self = shift;
-
-    my @manifest_files = keys %{ ExtUtils::Manifest::maniread(
-            File::Spec->catfile( $self->path, "MANIFEST" ) ) };
-
-    my @c_files = sort grep m{\.[cC]$}o, @manifest_files;
-
-    return map ($self->file_with_name($_), @c_files);
+    return $self;
 }
 
-=item C<c_header_file_directories()>
-
-Returns the directories which contain C header files.
-
-=cut
-
-# XXX returns what exactly???  The docs need updating here to help with
-# debugging and further development
-
-sub c_header_file_directories {
-    my $self = shift;
-
-    my %c_header_dirs =
-
-        # Make a hash out of the directories of those files
-        map { ( ( File::Spec->splitpath($_) )[1] => 1 ) }
-
-        # Only look at files ending in .h
-        grep { m|\.h$| }
 
-        keys %{ ExtUtils::Manifest::maniread( File::Spec->catfile( 
$self->path, "MANIFEST" ) ) };
-
-    return map $self->directory_with_name($_) => grep { !m|\.svn/$| }
-        sort keys %c_header_dirs;
+sub _croak {
+    my( $self, @message ) = @_;
+    require Carp;
+    Carp::croak(@message);
 }
 
-=item C<c_header_file_with_name($name)>
 
-Returns the C header file with the specified name.
+BEGIN {
+    my @getter_setters = qw{ _manifest_files };
 
-=cut
+    for my $method ( @getter_setters ) {
+        no strict 'refs';
 
-sub c_header_file_with_name {
-    my $self = shift;
-    my $name = shift || return;
-
-    $name .= '.h' unless $name =~ /\.[Hh]$/o;
-
-    foreach my $dir ( $self->c_header_file_directories ) {
-        return $dir->file_with_name($name)
-            if $dir->file_exists_with_name($name);
+        *$method = sub {
+            my $self = shift;
+            unless (@_) {
+                $self->{$method} ||= [];
+                return wantarray
+                    ? @{ $self->{$method} }
+                    : $self->{$method};
+            }
+            $self->{$method} = shift;
+            return $self;
+        };
     }
-
-    print 'WARNING: ' . __FILE__ . ':' . __LINE__ . ' File not found:' . $name 
. "\n";
-
-    return;
 }
 
-=item C<c_header_files()>
-
-Returns a sorted list of the C header files listed within the MANIFEST of
-Parrot.  Returns a list of Parrot::IO::File objects.
 
-=cut
+=back
 
-sub c_header_files {
-    my $self = shift;
+=head2 Instance Methods
 
-    my @manifest_files = keys %{ ExtUtils::Manifest::maniread(
-            File::Spec->catfile( $self->path, "MANIFEST" ) ) };
+=over 4
 
-    my @h_files = sort grep m{\.[hH]$}o, @manifest_files;
+=item C<c_source_file_directories()>
 
-    return map ($self->file_with_name($_), @h_files);
-}
+=item C<c_header_file_directories()>
 
 =item C<pmc_source_file_directories()>
 
-Returns the directories which contain PMC source files.
-
-=cut
-
-sub pmc_source_file_directories {
-    my $self = shift;
+=item C<yacc_source_file_directories()>
 
-    my %pmc_source_dirs =
+=item C<lex_source_file_directories()>
 
-        # Make a hash out of the directories of those files
-        map { ( ( File::Spec->splitpath($_) )[1] => 1 ) }
+=item C<ops_source_file_directories()>
 
-        # Only look at files ending in .pmc
-        grep { m|\.pmc$| }
+Returns the directories which contain source files of the appropriate filetype.
 
-        keys %{ ExtUtils::Manifest::maniread( File::Spec->catfile( 
$self->path, "MANIFEST" ) ) };
+=item C<c_source_file_with_name($name)>
 
-    return map $self->directory_with_name($_) => grep { !m|\.svn/$| }
-        sort keys %pmc_source_dirs;
-}
+=item C<c_header_file_with_name($name)>
 
 =item C<pmc_source_file_with_name($name)>
 
-Returns the PMC source file with the specified name.
-
-=cut
-
-sub pmc_source_file_with_name {
-    my $self = shift;
-    my $name = shift || return;
-
-    $name .= '.pmc';
-
-    foreach my $dir ( $self->pmc_source_file_directories ) {
-        return $dir->file_with_name($name)
-            if $dir->file_exists_with_name($name);
-    }
-
-    print 'WARNING: ' . __FILE__ . ':' . __LINE__ . ' File not found:' . $name 
. "\n";
-
-    return;
-}
-
-=item C<pmc_source_files()>
-
-Returns a sorted list of the PMC files listed within the MANIFEST of
-Parrot.  Returns a list of Parrot::IO::File objects.
-
-=cut
-
-sub pmc_source_files {
-    my $self = shift;
-
-    my @manifest_files = keys %{ ExtUtils::Manifest::maniread(
-            File::Spec->catfile( $self->path, "MANIFEST" ) ) };
-
-    my @pmc_files = sort grep m{\.pmc$}o, @manifest_files;
-
-    return map ($self->file_with_name($_), @pmc_files);
-}
-
-=item C<yacc_source_file_directories()>
-
-Returns the directories which contain yacc source files.
-
-=cut
-
-sub yacc_source_file_directories {
-    my $self = shift;
-
-    return map $self->directory_with_name($_) =>
-        'compilers/imcc/',
-        'languages/cola/',
-        'languages/lua/doc',
-        'languages/regex/lib/Regex',
-        ;
-}
-
 =item C<yacc_source_file_with_name($name)>
 
-Returns the yacc source file with the specified name.
-
-=cut
-
-sub yacc_source_file_with_name {
-    my $self = shift;
-    my $name = shift || return;
-
-    $name .= '.y';
-
-    foreach my $dir ( $self->yacc_source_file_directories ) {
-        return $dir->file_with_name($name)
-            if $dir->file_exists_with_name($name);
-    }
-
-    print 'WARNING: ' . __FILE__ . ':' . __LINE__ . ' File not found:' . $name 
. "\n";
-
-    return;
-}
-
-=item C<yacc_source_files()>
-
-Returns a sorted list of the yacc files listed within the MANIFEST of
-Parrot.  Returns a list of Parrot::IO::File objects.
-
-=cut
-
-sub yacc_source_files {
-    my $self = shift;
-
-    my @manifest_files = keys %{ ExtUtils::Manifest::maniread(
-            File::Spec->catfile( $self->path, "MANIFEST" ) ) };
-
-    my @yacc_files = sort grep m{\.y$}o, @manifest_files;
-
-    return map ($self->file_with_name($_), @yacc_files);
-}
-
-=item C<lex_source_file_directories()>
-
-Returns the directories which contain lex source files.
-
-=cut
-
-sub lex_source_file_directories {
-    my $self = shift;
-
-    return map $self->directory_with_name($_) =>
-        'compilers/imcc/',
-        'languages/cola/',
-        ;
-}
-
 =item C<lex_source_file_with_name($name)>
 
-Returns the lex source file with the specified name.
-
-=cut
+=item C<ops_source_file_with_name($name)>
 
-sub lex_source_file_with_name {
-    my $self = shift;
-    my $name = shift || return;
+Returns the source file with the specified name and of the appropriate 
filetype.
 
-    $name .= '.l';
+=item C<c_source_files()>
 
-    foreach my $dir ( $self->lex_source_file_directories ) {
-        return $dir->file_with_name($name)
-            if $dir->file_exists_with_name($name);
-    }
+=item C<c_header_files()>
 
-    print 'WARNING: ' . __FILE__ . ':' . __LINE__ . ' File not found:' . $name 
. "\n";
+=item C<pmc_source_files()>
 
-    return;
-}
+=item C<yacc_source_files()>
 
 =item C<lex_source_files()>
 
-Returns a sorted list of the lex files listed within the MANIFEST of
-Parrot.  Returns a list of Parrot::IO::File objects.
-
-=cut
-
-sub lex_source_files {
-    my $self = shift;
-
-    my @manifest_files = keys %{ ExtUtils::Manifest::maniread(
-            File::Spec->catfile( $self->path, "MANIFEST" ) ) };
-
-    my @lex_files = sort grep m{\.l$}o, @manifest_files;
-
-    return map ($self->file_with_name($_), @lex_files);
-}
-
-=item C<ops_source_file_directories()>
+=item C<ops_source_files()>
 
-Returns the directories which contain ops source files.
+Returns a sorted list of the source files listed within the MANIFEST of
+Parrot.  Returns a list of Parrot::IO::File objects of the appropriate 
filetype.
 
 =cut
 
-sub ops_source_file_directories {
-    my $self = shift;
-
-    return map $self->directory_with_name($_) =>
-        'src/ops/',
-        'src/dynoplibs/',
-        'languages/tcl/src/ops/',
-        'languages/WMLScript/ops/',
-        'languages/dotnet/ops/',
-        ;
-}
-
-=item C<ops_source_file_with_name($name)>
-
-Returns the ops source file with the specified name.
+BEGIN {
+    my %file_class = (
+        source => {
+            c    => { file_exts => ['c'] },
+            pmc  => { file_exts => ['pmc'] },
+            ops  => { file_exts => ['ops'] },
+            lex  => {
+                file_exts => ['l'],
+                except_dirs => [
+                    qw{ languages/lisp examples/library }
+                ],
+            },
+            yacc => { file_exts => ['y'] },
+#            perl => { file_exts => ['pl', 'pm', 'in', 't'] },
+        },
+        header => {
+            c    => { file_exts => ['h'] },
+        },
+    );
 
-=cut
+    my @ignore_dirs = qw{ .svn };
 
-sub ops_source_file_with_name {
-    my $self = shift;
-    my $name = shift || return;
 
-    $name .= '.ops';
-
-    foreach my $dir ( $self->ops_source_file_directories ) {
-        return $dir->file_with_name($name)
-            if $dir->file_exists_with_name($name);
+    for my $class ( keys %file_class ) {
+        for my $type ( keys %{ $file_class{$class} } ) {
+            no strict 'refs';
+
+            my @exts       = @{ $file_class{$class}{$type}{file_exts} };
+            my @exceptions = defined $file_class{$class}{$type}{except_dirs}
+                ? @{ $file_class{$class}{$type}{except_dirs} }
+                : ();
+            my $method     = join '_' => $type, $class;
+            my $filter_ext = join '|' => map { "\\.${_}\$" } @exts;
+            my $filter_dir = join '|' =>
+                map { qr{\b$_\b} }
+                map { quotemeta($_) }
+                @ignore_dirs,
+                @exceptions;
+
+            next unless $method;
+
+            *{ $method . '_file_directories' } = sub {
+                my $self = shift;
+
+                # Look through the manifest
+                # for files ending in the proper extension(s)
+                # and make a hash out of the directories
+                my %dirs =
+                    map { ( ( File::Spec->splitpath($_) )[1] => 1 ) }
+                    grep { m|(?i)(?:$filter_ext)| }
+                    $self->_manifest_files;
+
+                # Filter out ignored directories
+                # and return the results
+                return
+                    sort
+                    map { $self->directory_with_name($_) }
+                    grep { !m|(?:$filter_dir)| }
+                    keys %dirs;
+            };
+
+
+            *{ $method . '_file_with_name' } = sub {
+                my( $self, $name ) = @_;
+                return unless length $name;
+
+                if ( 1 == @exts ) {
+                    my $ext = $exts[0];
+                    $name .= ".$ext"
+                       if $name !~ qr/(?i)\.$ext$/;
+                }
+
+                my $meth = $method . '_file_directories';
+                for my $dir ( $self->$meth ) {
+                    return $dir->file_with_name($name)
+                        if $dir->file_exists_with_name($name);
+                }
+
+                print 'WARNING: ' . __FILE__ . ':' . __LINE__
+                    . ' File not found: ' . $name . "\n";
+                return;
+            };
+
+
+            *{ $method . '_files' } = sub {
+                my( $self ) = @_;
+
+                # Look through the manifest
+                # for files ending in the proper extension(s)
+                # and return a sorted list of filenames
+                return
+                    sort
+                    map { $self->file_with_name($_) }
+                    grep { m|(?i)(?:$filter_ext)| }
+                    $self->_manifest_files;
+            };
+        }
     }
-
-    print 'WARNING: ' . __FILE__ . ':' . __LINE__ . ' File not found:' . $name 
. "\n";
-
-    return;
 }
 
-=item C<ops_source_files()>
-
-Returns a sorted list of the ops files listed within the MANIFEST of
-Parrot.  Returns a list of Parrot::IO::File objects.
-
-=cut
-
-sub ops_source_files {
-    my $self = shift;
-
-    my @manifest_files = keys %{ ExtUtils::Manifest::maniread(
-            File::Spec->catfile( $self->path, "MANIFEST" ) ) };
-
-    my @ops_files = sort grep m{\.ops$}o, @manifest_files;
-
-    return map ($self->file_with_name($_), @ops_files);
-}
 
 =item C<get_c_language_files()>
 
@@ -452,7 +292,7 @@
 
 =item C header files C<*.h>
 
-=item (f)lex files C<*.lex>
+=item (f)lex files C<*.l>
 
 =item yacc/bison files C<*.y>
 
@@ -490,6 +330,7 @@
     # XXX: lex_source_files() collects lisp files as well...  how to fix ???
 }
 
+
 =item C<is_c_exemption()>
 
 Determines if the given filename is an exemption to being in the C source.
@@ -497,29 +338,26 @@
 
 =cut
 
-sub is_c_exemption {
-    my $self = shift;
-    my $file = shift;
+{
+    my @exemptions;
 
-    my @exemptions = qw(
-        config/gen/cpu/i386/memcpy_mmx.c
-        config/gen/cpu/i386/memcpy_sse.c
-        compilers/imcc/imclexer.c
-        compilers/imcc/imcparser.c
-        compilers/imcc/imcparser.h
-        languages/cola/lexer.c
-        languages/cola/parser.c
-        languages/cola/parser.h
-        );
-
-    # XXX this is inefficient isn't it?
-    foreach my $exemption ( @exemptions ) {
-        return 1 if $file->path =~ $exemption;
-    }
+    sub is_c_exemption {
+        my( $self, $file ) = @_;
 
-    return 0;
+        push @exemptions => map { File::Spec->canonpath($_) } qw{
+            config/gen/cpu/i386/memcpy_mmx.c config/gen/cpu/i386/memcpy_sse.c
+            compilers/imcc/imclexer.c        compilers/imcc/imcparser.c
+            compilers/imcc/imcparser.h       languages/cola/lexer.c
+            languages/cola/parser.c          languages/cola/parser.h
+        } unless @exemptions;
+
+        $file->path =~ /\Q$_\E$/ && return 1
+            for @exemptions;
+        return;
+    }
 }
 
+
 =item C<get_perl_language_files()>
 
 Returns the Perl language source files within Parrot.  Namely:
@@ -552,6 +390,7 @@
     return @files;
 }
 
+
 =item C<is_perl_exemption()>
 
 Determines if the given filename is an exemption to being in the Perl
@@ -560,31 +399,25 @@
 
 =cut
 
-sub is_perl_exemption {
-    my $self = shift;
-    my $file = shift;
+{
+    my @exemptions;
 
-    my @exemptions = qw(
-        languages/lua/Lua/parser.pm
-        languages/regex/lib/Regex/Grammar.pm
-        lib/Class/*
-        lib/Digest/*
-        lib/File/*
-        lib/Parse/*
-        lib/Pod/*
-        lib/SmartLink.pm
-        lib/Test/*
-        lib/Text/*
-        );
-
-    # XXX this is inefficient isn't it?
-    foreach my $exemption ( @exemptions ) {
-        return 1 if $file =~ $exemption;
-    }
+    sub is_perl_exemption {
+        my( $self, $file ) = @_;
 
-    return 0;
+        push @exemptions => map { File::Spec->canonpath($_) } qw{
+            languages/lua/Lua/parser.pm  languages/regex/lib/Regex/Grammar.pm
+            lib/Class/* lib/Digest/*     lib/File/*     lib/Parse/*
+            lib/Pod/*   lib/SmartLink.pm lib/Test/*     lib/Text/*
+        } unless @exemptions;
+
+        $file->path =~ /\Q$_\E$/ && return 1
+            for @exemptions;
+        return;
+    }
 }
 
+
 =item C<is_perl()>
 
 Determines if the given filename is Perl source
@@ -615,7 +448,7 @@
     # Now let's check to see if there's a perl shebang.
 
     open my $file_handle, '<', $filename
-        or die "Could not open $filename for reading";
+        or $self->_croak( "Could not open $filename for reading" );
     my $line = <$file_handle>;
     close $file_handle;
 
@@ -626,6 +459,7 @@
     return 0;
 }
 
+
 =item C<file_for_perl_module($module)>
 
 Returns the Perl module file for the specified module.
@@ -650,6 +484,7 @@
     return $dir->existing_file_with_name($module);
 }
 
+
 =item C<docs_directory()>
 
 Returns the documentation directory.
@@ -662,6 +497,7 @@
     return $self->existing_directory_with_name('docs');
 }
 
+
 =item C<html_docs_directory()>
 
 Returns the HTML documentation directory.
@@ -674,6 +510,7 @@
     return $self->docs_directory->directory_with_name('html');
 }
 
+
 =item C<delete_html_docs()>
 
 Deletes the HTML documentation directory.
@@ -686,6 +523,7 @@
     return $self->html_docs_directory->delete();
 }
 
+
 =item C<gen_manifest_skip>
 
 Query the svn:ignore property and generate the lines for MANIFEST.SKIP.
@@ -732,6 +570,7 @@
     return [EMAIL PROTECTED];
 }
 
+
 =item C<generated_files>
 
 Returns a hash where the keys are the files in F<MANIFEST.generated> and the

Reply via email to