[svk-commit] r2192 - in trunk/lib/SVK: . MimeDetect Path

clkao at bestpractical.com clkao at bestpractical.com
Tue Nov 21 03:42:26 EST 2006


Author: clkao
Date: Tue Nov 21 03:42:26 2006
New Revision: 2192

Modified:
   trunk/lib/SVK/Log/Filter.pm
   trunk/lib/SVK/Merge.pm
   trunk/lib/SVK/MimeDetect/FileMMagic.pm
   trunk/lib/SVK/MimeDetect/FileType.pm
   trunk/lib/SVK/MimeDetect/Internal.pm
   trunk/lib/SVK/MirrorCatalog.pm
   trunk/lib/SVK/Path/Checkout.pm

Log:
Unbreak trunk.

Modified: trunk/lib/SVK/Log/Filter.pm
==============================================================================
--- trunk/lib/SVK/Log/Filter.pm	(original)
+++ trunk/lib/SVK/Log/Filter.pm	Tue Nov 21 03:42:26 2006
@@ -48,3 +48,308 @@
 # distribute those contributions and any derivatives thereof.
 # 
 # END BPS TAGGED BLOCK }}}
+package SVK::Log::Filter;
+use strict;
+use warnings;
+
+sub new {
+    my ($pkg, $argument) = @_;
+    return bless { argument => $argument }, $pkg;
+}
+
+# generate exceptions which control the filter pipeline.  The wording is
+# so that we can distinguish control exceptions from real exceptions
+sub pipeline {
+    my ($self, $command) = @_;
+    die "pipeline, $command please";
+}
+
+# empty implementations so that derived classes only have to implement
+# the methods that they need
+sub setup    { 1 }
+sub header   { 1 }
+sub footer   { 1 }
+sub teardown { 1 }
+sub revision { 1 }
+
+1;
+
+__END__
+
+=head1 NAME
+
+SVK::Log::Filter - base class for all log filters
+
+=head1 DESCRIPTION
+
+SVK::Log::Filter is a class for displaying or otherwise processing revision
+properties.  The SVK "log" command uses filter classes to handle the details
+of processing the revision properties.  The bulk of this document explains how
+to write those filter classes.
+
+A log filter is just a Perl class with special methods.  At specific points
+while processing log information, SVK calls these methods on the filter
+object.  SVK::Log::Filter provides sensible defaults for each of the methods
+it calls.  The methods (in order of invocation) are L</setup>, L</header>,
+L</revision>, L</footer>, L</teardown>. Each is fully documented in the
+section L</METHOD REFERENCE>.
+
+
+=head1 TUTORIAL
+
+Although log filters which output and log filters which select are exactly the
+same kind of objects, they are generally conceptualized separately.  The
+following tutorial provides a simple example for each type of filter.
+
+=head2 OUTPUT
+
+For our simple output filter example, we want to display something like the following
+
+    1. r3200 by john
+    2. r3194 by tom
+    3. r3193 by larry
+
+Namely, the number the revisions we've seen, then show the actual revision
+number from the repository and indicate the author of that revision.   We want
+this log filter to be accessible by a command like "svk log --output list"
+The code to accomplish that is
+
+   1   package SVK::Log::Filter::List;
+   2   use base qw( SVK::Log::Filter );
+       
+   3   sub setup {
+   4       my ($self) = @_;
+   5       $self->{count} = 1;
+   6   }
+       
+   7   sub revision {
+   8       my ($self, $args) = @_;
+       
+   9       printf "%d. r%d by %s\n",
+  10           $self->{count}++,
+  11           $args->{rev},
+  12           $args->{props}{'svn:author'}
+  13       ;
+  14   }
+
+First, we must establish the name of this filter.  SVK looks for filters with
+the namespace prefix C<SVK::Log::Filter>.  The final portion of the name can
+either have the first letter capitalized or all the letters capitalized.  On
+line 2, we use SVK::Log::Filter as the base class so that we can get the
+default method implementations.
+
+On lines 3-6, we get to the first meat.  Since we want to count the revisions
+that we see, we have to store the information somewhere that will persist
+between method calls.  We just store it in the log filter object itself.
+Finally, on line 6, our C<setup> method is finished.  The return value of the
+method is irrelevant.
+
+The C<revision> method on lines 7-14 does the real work of the filter.  First
+(line 8) we extract arguments into a hashref C<$args>.  Then it simply prints
+what we want it to display.  SVK takes care of directing output to the
+appropriate place.  You'll notice that the revision properties are provided as
+a hash.  The key of the hash is the name of the property and the value of the
+hash is the value of the property.
+
+That's it.  Put SVK::Log::Filter::List somewhere in C<@INC> and SVK will find
+it.
+
+=head2 SELECTION
+
+Our simple selection filter example will pass revisions based on whether the
+revision number is even or odd.  The filter accepts a single argument 'odd' or
+'even' indicating which revisions should be passed down the pipeline.
+Additionally, if the filter ever encounters the revision number "42" it will
+stop the entire pipeline and process no more revisions.  The invocation is
+something like "svk log --filter 'parity even'" to display all even revisions
+up to r42.
+
+   1   package SVK::Log::Filter::Parity;
+   2   use base qw( SVK::Log::Filter );
+       
+   3   sub setup {
+   4       my ($self) = @_;
+       
+   5       my $argument = lc $self->{argument};
+   6       $self->{bit} = $argument eq 'even' ? 0
+   7                    : $argument eq 'odd'  ? 1
+   8                    : die "Parity argument not 'even' or 'odd'\n"
+   9                    ;
+  10   }
+       
+  11   sub revision {
+  12       my ($self, $args) = @_;
+       
+  13       my $rev = $args->{rev};
+  14       $self->pipeline('last') if $rev == 42;
+  15       $self->pipeline('next') if $rev % 2 != $self->{bit};
+  16   }
+
+There are only a few differences between this implementation and the output
+filter implementation.  The first difference is in line 5.  When a log filter
+object is created, the default C<new> method creates the 'argument' key which
+contains the command-line argument provided to your filter.  In this case, the
+argument should be either 'even' or 'odd'.  Based on the argument, we update
+the object to remind us what parity we're looking for.
+
+The unique characteristics of C<revision> are the calls to the C<pipeline>
+method in lines 14 and 15.  If we want to stop the pipeline entirely, call
+C<pipeline> with the argument 'last' (think "this is the last revision").  The
+current revision and all subsequent revisions will not be processed by the
+filter pipelin.  If the argument to C<pipeline> is 'next' (think "go to the
+next revision"), the current revision will not be displayed and the pipeline
+will proceed with the next revision in sequence.  If you don't call
+C<pipeline>, the current revision is passed down the remainder of the pipeline
+so that it can be processed and displayed.
+
+=head1 METHODS
+
+This is a list of all the methods that L<SVK::Log::Filter> implements and a
+description of how they should be called.  When defining a subclass, one need
+only override those methods that are necessary for implementing the filter.
+All methods have sensible defaults (namely, do nothing).  The methods are
+listed here in the order in which they are called by the pipeline.
+
+All methods except L</new> and L</pipeline> receive a single hash reference
+as their first argument (after the invocant, of course).  The 'Receives'
+section in the documentation below indicates which named arguments are present
+in that hash.
+
+=head2 new
+
+Builds a new object from a hash reference.  The value of any arguments
+provided to the log filter on the command line are placed in the 'argument'
+attribute of the object.  Generally, there is no need to override the C<new>
+method because the L</setup> method can be overriden instead.
+
+=head2 setup
+
+Receives: L</stash>
+
+This method is called once just before the filter is used for the first time.
+It's conceptually similar to L</new>, but allows the filter developer to
+ignore the creation of the filter object.  This is the place to do filter
+initialization, process command-line arguments, read configuration files,
+connect to a database, etc.
+
+=head2 header
+
+Receives: L</stash>
+
+This method is called once just before the first revision is processed but
+after C<setup> has completed.  This is an ideal place to display information
+which should appear at the top of the log display.
+
+=head2 revision
+
+Receives: L</paths>, L</rev>, L</props>, L</stash>, L</get_remoterev>
+
+This method is called for each revision that SVK wants to process.  The bulk
+of a log filter's code implements this method.  Output filters may simply
+print the information that they want displayed.  Other filters should either
+modify the revision properties (see L</props>) or use pipeline commands (see
+L</pipeline>) to skip irrelevant revisions.
+
+=head2 footer
+
+Receives: L</stash>
+
+This method is similar to the C<header> method, but it's called once after all
+the revisions have been displayed.  This is the place to do any final output.
+
+=head2 teardown
+
+Receives: L</stash>
+
+This method is called once just before the log filter is discarded.  This is
+the place to disconnect from databases, close file handles, etc.
+
+=head2 pipeline
+
+This method is not called by the filter pipeline.  Rather, it's used by log
+filters to control the pipeline's behavior.  It accepts a single scalar as the
+argument.  If the argument is 'next', the pipeline stops processing the
+current revision (including any output filter) and starts processing the next
+revision starting over at the beginning of the pipeline.  If the argument to
+C<pipeline> is 'last', the pipeline is stopped entirely (including any output
+filters).  Once the pipeline has stopped, the SVK log command finishes any
+final details and stops.
+
+=head1 ARGUMENTS
+
+This section describes the possible keys and values of the hashref that's
+provided to method calls.
+
+=head1 get_remoterev
+
+If the value of this argument is true, the value is a coderef.  When the
+coderef is invoked with a single revision number as the argument, it returns
+the number of the equivalent revision in the upstream repository.  The value
+of this key may be undefined if the logs are being processed for something
+other than a mirror.  The following code may be useful when working with
+"get_remoterev"
+
+    my           ( $stash, $rev, $get_remoterev)
+    = @{$args}{qw(  stash   rev   get_remoterev )};
+    my $remote_rev = $get_remoterev ? $get_remoterev->($rev) : 'unknown';
+    print "The remote revision for r$rev is $remote_rev.\n";
+
+
+=head2 paths
+
+The value of the 'paths' argument is an L<SVK::Log::ChangedPaths> object.
+The object provides methods for indicating which paths were changed by this
+revision and approximately how they were changed (modified file contents,
+modified file properties, etc.)
+
+See the documentation for SVK::Log::ChangedPaths for more details.
+
+=head2 rev
+
+The value of the 'rev' argument is the Subversion revision number for the
+current revision.
+
+=head2 props
+
+The value of the 'props' argument is a hash reference containing all the
+revision properties for the current revision.  The keys of the hash are the
+property names and the values of the hash are the property values.  For
+example, the author of a revision is available with
+C<< $args->{'svn:author'} >>.
+
+If you change values in the 'props' hashref, those changes are visible to all
+subsequent filters in the pipeline.  This can be useful and dangerous.
+Dangerous if you accidentally modify a property, useful if you intentionally
+modify a property.  For instance, it's possible to make a "selection" filter
+which uses Babelfish to translate log messages from one language to another
+(see L<SVK::Log::Filter::Babelfish> on CPAN).  By modifying the 'svn:log'
+property, other log filters can operate on the translated log message without
+knowing that it's translated.
+
+=head2 stash
+
+The value of the 'stash' argument is a reference to a hash.  The stash
+persists throughout the entire log filtering process and is provided to every
+method that the filter pipeline calls.  The stash may be used to pass
+information from one filter to another filter in the pipeline.
+
+When creating new keys in the stash, it's important to avoid unintentional
+name collisions with other filters in the pipeline.  A good practice is to
+preface the name of each stash key with the name of your filter
+("myfilter_key") or to create your own hash reference inside the stash (C<<
+$stash->{myfilter}{key} >>).  If your filter puts information into the stash
+that other filters may want to access, please document the location and format
+of that information for other filter authors.
+
+=head1 STASH REFERENCE
+
+=head1 quiet
+
+If the user included the "--quiet" flag when invoking "svk log" the value of
+this key will be a true value.  Otherwise, the value will be false.
+
+=head1 verbose
+
+If the user included the "--verbose" flag when invoking "svk log" the value of
+this key will be a true value.  Otherwise, the value will be false.
+

Modified: trunk/lib/SVK/Merge.pm
==============================================================================
--- trunk/lib/SVK/Merge.pm	(original)
+++ trunk/lib/SVK/Merge.pm	Tue Nov 21 03:42:26 2006
@@ -766,5 +766,6 @@
 
 L<SVK::Editor::Merge>, L<SVK::Command::Merge>, Star-merge from GNU Arch
 
+=cut
 
 1;

Modified: trunk/lib/SVK/MimeDetect/FileMMagic.pm
==============================================================================
--- trunk/lib/SVK/MimeDetect/FileMMagic.pm	(original)
+++ trunk/lib/SVK/MimeDetect/FileMMagic.pm	Tue Nov 21 03:42:26 2006
@@ -48,6 +48,21 @@
 # distribute those contributions and any derivatives thereof.
 # 
 # END BPS TAGGED BLOCK }}}
+package SVK::MimeDetect::FileMMagic;
+use strict;
+use warnings;
+use base qw( File::MMagic );
+
+use SVK::Util qw( is_binary_file );
+
+=for Workaround:
+
+File::MMagic 1.27 doesn't correctly handle subclassing.  The object returned by
+new is blessed into 'File::MMagic' instead of the subclass.  The author has
+accepted a patch to correct this behavior.  Once the patched version is
+released on CPAN, new() should be removed and the fixed version required.
+
+=cut
 sub new {
     my $pkg = shift;
     my $new_self = $pkg->SUPER::new(@_);
@@ -83,4 +98,3 @@
 =head1 DESCRIPTION
 
 Implement MIME type detection using the module File::MMagic.
-

Modified: trunk/lib/SVK/MimeDetect/FileType.pm
==============================================================================
--- trunk/lib/SVK/MimeDetect/FileType.pm	(original)
+++ trunk/lib/SVK/MimeDetect/FileType.pm	Tue Nov 21 03:42:26 2006
@@ -48,3 +48,38 @@
 # distribute those contributions and any derivatives thereof.
 # 
 # END BPS TAGGED BLOCK }}}
+package SVK::MimeDetect::FileType;
+use strict;
+use warnings;
+use base qw( File::Type );
+
+use SVK::Util qw( is_binary_file );
+
+sub checktype_filename {
+    my ($self, $filename) = @_;
+
+    return 'text/plain' if -z $filename;
+
+    open my $fh, '<', $filename or die $!;
+    binmode($fh);
+    read $fh, my $data, 16*1024 or return undef;
+
+    my $type = File::Type->checktype_contents($data);
+    return $type if $type ne 'application/octet-stream';
+
+    # verify File::Type's opinion on supposedly binary data
+    return 'application/octet-stream' if is_binary_file($filename);
+    return 'text/plain';
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+SVK::MimeDetect::FileType
+
+=head1 DESCRIPTION
+
+Use L<File::Type> for automatic MIME type detection.

Modified: trunk/lib/SVK/MimeDetect/Internal.pm
==============================================================================
--- trunk/lib/SVK/MimeDetect/Internal.pm	(original)
+++ trunk/lib/SVK/MimeDetect/Internal.pm	Tue Nov 21 03:42:26 2006
@@ -48,3 +48,34 @@
 # distribute those contributions and any derivatives thereof.
 # 
 # END BPS TAGGED BLOCK }}}
+package SVK::MimeDetect::Internal;
+use strict;
+use warnings;
+
+use SVK::Util qw( is_binary_file );
+
+sub new {
+    my ($pkg) = @_;
+    return bless {}, $pkg;
+}
+
+sub checktype_filename {
+    my ($self, $filename) = @_;
+    return 'application/octet-stream' if is_binary_file($filename);
+    return 'text/plain';
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+SVK::MimeDetect::Internal - minimalist MIME type detection
+
+=head1 DESCRIPTION
+
+This class performs the least amount of MIME type detection possible while
+still providing enough metadata for SVK to function properly.  It simply
+assigns 'application/octet-stream' to any file that looks like binary data.  It
+assigns 'text/plain' to everything else.

Modified: trunk/lib/SVK/MirrorCatalog.pm
==============================================================================
--- trunk/lib/SVK/MirrorCatalog.pm	(original)
+++ trunk/lib/SVK/MirrorCatalog.pm	Tue Nov 21 03:42:26 2006
@@ -137,5 +137,6 @@
 
 L<SVN::Mirror>
 
+=cut
 
 1;

Modified: trunk/lib/SVK/Path/Checkout.pm
==============================================================================
--- trunk/lib/SVK/Path/Checkout.pm	(original)
+++ trunk/lib/SVK/Path/Checkout.pm	Tue Nov 21 03:42:26 2006
@@ -48,6 +48,37 @@
 # distribute those contributions and any derivatives thereof.
 # 
 # END BPS TAGGED BLOCK }}}
+package SVK::Path::Checkout;
+use strict;
+use SVK::Version;  our $VERSION = $SVK::VERSION;
+
+use base 'SVK::Accessor';
+
+use SVK::Path;
+
+__PACKAGE__->mk_shared_accessors(qw(xd));
+__PACKAGE__->mk_clonable_accessors(qw(report source copath_anchor copath_target));
+__PACKAGE__->mk_accessors(qw(_pool _inspector));
+
+use Class::Autouse qw(SVK::Editor::XD SVK::Root::Checkout);
+
+use autouse 'SVK::Util' => qw( get_anchor catfile abs2rel get_encoder to_native );
+
+=head1 NAME
+
+SVK::Path::Checkout - SVK path class associating a checkout
+
+=head1 SYNOPSIS
+
+ See below
+
+=head1 DESCRIPTION
+
+The class represents a node in svk depot, associated with a checkout
+copy.
+
+=cut
+
 sub real_new {
     my $class = shift;
     my $self = $class->SUPER::real_new(@_);
@@ -337,5 +368,6 @@
 
 L<SVK::Path>
 
+=cut
 
 1;


More information about the svk-commit mailing list