[Jifty-commit] r4512 - in Jifty-Book: . doc doc/Jifty doc/Jifty/Book doc/Jifty/Book/Appendices doc/Jifty/Book/Part-A doc/Jifty/Book/Part-B doc/Jifty/Book/Part-C doc/Jifty/Book/Part-D inc inc/.author inc/Module/Install t

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Wed Nov 21 02:23:16 EST 2007


Author: sterling
Date: Wed Nov 21 02:23:15 2007
New Revision: 4512

Added:
   Jifty-Book/AUTHORS
   Jifty-Book/Makefile.PL
   Jifty-Book/README
   Jifty-Book/doc/
   Jifty-Book/doc/Jifty/
   Jifty-Book/doc/Jifty/Book/
   Jifty-Book/doc/Jifty/Book.pod
   Jifty-Book/doc/Jifty/Book/Appendices/
   Jifty-Book/doc/Jifty/Book/Part-A/
   Jifty-Book/doc/Jifty/Book/Part-A/01-Introduction.pod
   Jifty-Book/doc/Jifty/Book/Part-A/02-Installing_Jifty.pod
   Jifty-Book/doc/Jifty/Book/Part-A/03-Creating_a_Jifty_Application.pod
   Jifty-Book/doc/Jifty/Book/Part-B/
   Jifty-Book/doc/Jifty/Book/Part-B/04-Building_a_Data_Model.pod
   Jifty-Book/doc/Jifty/Book/Part-B/05-Advanced_Data_Models.pod
   Jifty-Book/doc/Jifty/Book/Part-C/
   Jifty-Book/doc/Jifty/Book/Part-D/
   Jifty-Book/inc/
   Jifty-Book/inc/.author/
   Jifty-Book/inc/Module/
   Jifty-Book/inc/Module/Install/
   Jifty-Book/inc/Module/Install.pm
   Jifty-Book/inc/Module/Install/Base.pm
   Jifty-Book/inc/Module/Install/Can.pm
   Jifty-Book/inc/Module/Install/Fetch.pm
   Jifty-Book/inc/Module/Install/Makefile.pm
   Jifty-Book/inc/Module/Install/Metadata.pm
   Jifty-Book/inc/Module/Install/Win32.pm
   Jifty-Book/inc/Module/Install/WriteAll.pm
   Jifty-Book/t/
   Jifty-Book/t/ppod-check.t

Log:
Initial commit of part of the outline of the Jifty-Book. Please see README.

Added: Jifty-Book/AUTHORS
==============================================================================
--- (empty file)
+++ Jifty-Book/AUTHORS	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,2 @@
+In order of first commit:
+Andrew Sterling Hanenkamp <hanenkamp at cpan.org>

Added: Jifty-Book/Makefile.PL
==============================================================================
--- (empty file)
+++ Jifty-Book/Makefile.PL	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,13 @@
+use strict;
+use warnings;
+
+use inc::Module::Install;
+
+name     'Jifty-Book';
+license  'CC-BY-SA';
+version  '0.71120';
+
+requires 'File::Find::Rule';
+requires 'Pod::PseudoPod';
+
+WriteAll;

Added: Jifty-Book/README
==============================================================================
--- (empty file)
+++ Jifty-Book/README	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,76 @@
+Jifty Book
+==========
+
+Introduction to the Book Idea 
+
+by Andrew Sterling Hanenkamp.
+(a.k.a. sterling in Subversion)
+(a.k.a. zostay in IRC)
+
+Last Updated November 21, 2007 around 1 AM CST
+
+HISTORY
+
+At OSCON 2007, I just happened to be talking with Jesse and made the half-joking
+remark that Jifty really needs a book. Jesse took the idea quite seriously and
+we've been corresponding on the idea since. He and I have worked out an outline
+and some basic conventions for the book so far.
+
+As of this writing, I am just starting the writing portion. I haven't fully
+settled on it, but I plan to use Spensive which Jesse committed earlier today as
+the tutorial application.
+
+I hope to have the rest of the outline (as far as I've finished it) in the
+repository as I have time.
+
+CONTRIBUTING
+
+I've taken on the role of primary writer for the book, but additional
+contributions are very welcome. As with the Jifty framework itself, be sure to
+add yourself to the AUTHORS file first so that we know to attribute part of the
+text to you should it become published. I cannot promise any royalties or fame
+or glory in exchange for your contributions, but you will be helping the Jifty
+community.
+
+If you're just correcting typos or errors or fleshing out a section, feel free
+to commit your changes however you like. If you want to make structural changes
+to the chapter outlines themselves, I'd appreciate it if we can discuss it on
+the mailing list or in IRC first, but I won't be upset if you just commit the
+change. I subscribe to the "Be bold" philosophy of Wikipedia, so I'd rather have
+a problematic contribution has to be rolled back than miss out on something
+valuable. So, be bold.
+
+LICENSE
+
+Be aware that all contributions are licensed under the the Creative Commons
+Attribute-Share Alike 3.0 United States License.
+
+  http://creativecommons.org/licenses/by-sa/3.0/us/
+
+Be sure to understand that license before using this text, but know that you can
+use the text of this book for your own purposes according to the terms of this
+license.
+
+FORMATTING
+
+I've chosen to use the PseudoPod format for the book, which is an extension of
+regular Plain Old Documentation. If you don't know how to use PseudoPod you can
+either just pretend it's POD or read the tutorial:
+
+  http://search.cpan.org/~arandal/Pod-PseudoPod-0.13/lib/Pod/PseudoPod/Tutorial.pod
+
+TESTING
+
+If you do make any changes, I'd appreciate it if you ran:
+
+  perl Makefile.PL
+  make test
+
+which will test for errors in your PseudoPod. Please correct your errors before
+committing, if you can.
+
+CONTACTING ME
+
+If you want to discuss something related to this, you can get me via zostay on
+IRC or you can talk to obra (Jesse). You can post to the Jifty mailing list. Or
+if you want to email me directly, just use hanenkamp at cpan.org.

Added: Jifty-Book/doc/Jifty/Book.pod
==============================================================================
--- (empty file)
+++ Jifty-Book/doc/Jifty/Book.pod	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,13 @@
+=head0 Preface
+
+Welcome to the Jifty Book.
+
+=head1 Authors
+
+See the F<AUTHORS> file.
+
+=head1 License
+
+Copyright 2007 Andrew Sterling Hanenkamp.
+
+This work is licensed under a Creative Commons Attribute-Share Alike 3.0 United States License (U<http://creativecommons.org/licenses/by-sa/3.0/us/>).

Added: Jifty-Book/doc/Jifty/Book/Part-A/01-Introduction.pod
==============================================================================
--- (empty file)
+++ Jifty-Book/doc/Jifty/Book/Part-A/01-Introduction.pod	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,91 @@
+=head0 Introduction
+
+Jifty is a web application framework. That is, Jifty helps you build applications centered in a web browser using HTML, JavaScript, and dynamic server-side features. Jifty is data-centric. Jifty does Ajax and DHTML. It provides tools to build use these features clearly and easily. It is based upon the following principles:
+
+=over
+
+=item * DRY (Don't Repeat Yourself)
+
+=item * Full-stack
+
+=item * Continuations
+
+=item * Form-based dispatch
+
+=item * Perl
+
+=item * CPAN
+
+=item * A Pony
+
+=back
+
+=head1 Web 2.0
+
+=head1 What is Jifty?
+
+=head2 History
+
+=head2 Purpose
+
+=head2 Philosophy
+
+=head1 Audience for this Book
+
+=head1 Conventions in this Book
+
+=head2 Chapter Structure
+
+The basic structure of each chapter will be as follows:
+
+=head3 Abstract
+
+This will get you started on what this chapter covers regarding Jifty. What does that part of Jifty do and how does the chapter intend to cover it.
+
+=head3 Tutorial
+
+This book is built with a running tutorial based upon the "Spensive" application built by Best Practical Solutions to track business expenses. 
+
+If you want to just figure out how to build an application, you can just read this first section of each chapter to progressively expand the tutorial application. Later, you can come back and read the reference and testing sections of each chapter to go deeper.
+
+=head3 Reference
+
+The second major section of each chapter in this book is the reference section. Here we describe the gory details of each part of Jifty in some detail. The reference section is arranged to explain how the pieces work, how they're meant to be used, and how they fit together.
+
+If you need to dig deeper than the reference section goes, you will want to see the API documentation of Jifty, which often goes into even more detail about the topics covered in each chapter. Ultimately, you can read the implementation in the code itself if you really want to know how it works. It's Open Source so you have that option if you really need to dig deep.
+
+=head3 Testing
+
+The final major section of each chapter will cover testing strategies. The key to developing good applications is to build solid tests that will verify that your code works as expected. The major difficulty is how to do that. For each topic covered, we've tried to provide some examples related to the tutorial and guidelines on how to apply test strategies in your applications.
+
+=head2 Other Conventions
+
+=head1 Getting More Help
+
+This book is only one help available to you. You should also consider these additional resources:
+
+=over
+
+=item * Wiki: U<http://jifty.org>
+
+=item * Mailing Lists: U<http://jifty.org/view/JiftyLists>
+
+=item * IRC in #jifty on irc.freenode.net
+
+=item * API docs: U<http://search.cpan.org/dist/Jifty> (or U<http://localhost:8888/__jifty/online_docs/> for your typical base Jifty application running in AdminMode)
+
+=item * 
+
+If you need commercial support, contact Best Practical Solutions, LLP.
+
+=over
+=item Contact Information
+U<http://www.bestpractical.com/about/contact.html>
+=item Email
+C<< sales at bestpractical.com >>
+=item Phone/Fax
++1 617 812 0745
+=back
+
+=back
+

Added: Jifty-Book/doc/Jifty/Book/Part-A/02-Installing_Jifty.pod
==============================================================================
--- (empty file)
+++ Jifty-Book/doc/Jifty/Book/Part-A/02-Installing_Jifty.pod	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,33 @@
+=head0 Installing Jifty
+
+Jifty is a Perl-based frameowkr, so you'll need to know Perl and need to have Perl installed. If you don't know how to use Perl, we recommend you read T<Learning Perl> by Randal L. Schwartz and Tom Phoenix first (O'Reilly).
+
+You can install Jifty in one of the following ways:
+
+=over
+
+=item 1 Prebuilt package (TODO)
+
+=item 1 CPAN
+
+=item 1 Subversion repository
+
+=back
+
+=head1 Tutorial
+
+=head2 Introducing Spensive
+
+=head1 Reference
+
+=head2 Pakcage (TODO)
+
+=head2 CPAN
+
+=head2 Subversion
+
+=head1 Testing
+
+=head2 Testing your Jifty Installation
+
+=head2 Reporting Problems

Added: Jifty-Book/doc/Jifty/Book/Part-A/03-Creating_a_Jifty_Application.pod
==============================================================================
--- (empty file)
+++ Jifty-Book/doc/Jifty/Book/Part-A/03-Creating_a_Jifty_Application.pod	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,57 @@
+=head0 Creating a Jifty Application
+
+Jifty will get you started by providing the scaffolding needed to build your application. It will essentially build the "Hello World" application for you.N<Though, it's more like the "Hello Pony" application.> You can use the command-line to build your initial application and use it to generate additional code for you to get you started on other components as well.
+
+=head1 Tutorial: Creating the Scaffolding
+
+=head2 Creating the Application
+
+=head2 Setting Up the Schema
+
+=head2 Running the Server
+
+=head2 Trying your Server
+
+=head1 Reference
+
+=head2 Introducing the CLI
+
+=head2 Build an application
+
+=head3 How to run jifty app
+
+=head3 The Directory Structure
+
+=head2 Prepping your Application
+
+=head3 How to Run jifty schema
+
+=head3 Using SQLite
+
+=head3 Cleaning Up the Development Database
+
+=head2 Run the Server
+
+=head3 How to Run jifty server
+
+=head3 Trying Out the Server
+
+=head2 Configuration
+
+=head3 etc/config.yml
+
+=head4 Framework
+
+=head4 App
+
+=head3 Log4perl Configuration
+
+=head1 Testing
+
+=head2 Introduction to the Testing Suite
+
+=head2 How to Add Tests
+
+=head2 Jifty::Test
+
+=head2 Jifty::Test::WWW::Mechanize

Added: Jifty-Book/doc/Jifty/Book/Part-B/04-Building_a_Data_Model.pod
==============================================================================
--- (empty file)
+++ Jifty-Book/doc/Jifty/Book/Part-B/04-Building_a_Data_Model.pod	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,57 @@
+=head0 Building a Data Model
+
+This chapter will help you store the data for your application. Jifty stores data in database tables defined by a schema, which is just a fancy name for the column definitions. Jifty uses a special declarative syntax that makes these very easy to build and very easy to read later on. 
+
+The great thing is, once you put in your schema, Jifty does almost all the hard work for you. This includes creating tables, altering tables when changes occur, and automatically building classes and methods for reading and writing your data.
+
+=head1 Tutorial
+
+=head1 Reference
+
+=head2 What's in a Model?
+
+=head2 Model Declaration
+
+=head3 Schema Declarations
+
+=head3 Column Definitions
+
+=head3 Column Attributes
+
+=head2 Model Records
+
+=head3 Adding a Record
+
+=head3 Loading a Record
+
+=head3 Changing a Record
+
+=head3 Removing a Record
+
+=head2 Model Collections
+
+=head3 Listing Records
+
+=head3 Limiting your Results
+
+=head3 Helpful Methods
+
+=head4 items_array_ref
+
+=head4 first/last
+
+=head4 goto_first_item/goto_item
+
+=head4 count/count_all
+
+=head3 The Pager
+
+=head3 Ordering a Collection
+
+=head1 Testing
+
+=head2 Creating Records as Superuser
+
+=head2 Adding Lots of Data
+
+=head2 Using Model Method Return Values

Added: Jifty-Book/doc/Jifty/Book/Part-B/05-Advanced_Data_Models.pod
==============================================================================
--- (empty file)
+++ Jifty-Book/doc/Jifty/Book/Part-B/05-Advanced_Data_Models.pod	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,71 @@
+=head0 Advanced Data Models
+
+Now that we know how to store basic information in the model, let's look at the rest of the iceberg. With Jifty, you can also create relationships between models, called references. You can trigger actions whenever a certain change happens. You can further enhance your models with custom code. 
+
+This chapter also helps you fill your tables with start data and hows you how to deal with changes to your schema as your application evolves.
+
+=head1 Tutorial
+
+=head1 Reference
+
+=head2 References
+
+=head3 Linking Records
+
+=head3 Linking Collections
+
+=head2 Triggesr
+
+=head3 Using add_trigger()
+
+=head3 Available Hooks
+
+=head4 before_create
+
+=head4 after_create
+
+=head4 before_set_X
+
+=head4 after_set_X
+
+=head4 before_set
+
+=head4 after_set
+
+=head4 before_delete
+
+=head4 after_delete
+
+=head4 before_access
+
+=head2 Custom Methods
+
+=head2 Boostrapping
+
+=head2 Upgrades
+
+=head3 The Upgrade Class
+
+=head3 Adding since Declarations
+
+=head3 Handling Table Renaming
+
+=head3 Handling Added and Dropped Columns
+
+=head3 Handling Renamed Columns
+
+=head2 Mixins
+
+=head3 Modifying the Schema
+
+=head3 Exporting Custom Methods
+
+=head3 Adding Triggers
+
+=head1 Testing
+
+=head2 Testing References
+
+=head2 Testing Bootstrap
+
+=head2 Testing Upgrades

Added: Jifty-Book/inc/Module/Install.pm
==============================================================================
--- (empty file)
+++ Jifty-Book/inc/Module/Install.pm	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,281 @@
+#line 1
+package Module::Install;
+
+# For any maintainers:
+# The load order for Module::Install is a bit magic.
+# It goes something like this...
+#
+# IF ( host has Module::Install installed, creating author mode ) {
+#     1. Makefile.PL calls "use inc::Module::Install"
+#     2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install
+#     3. The installed version of inc::Module::Install loads
+#     4. inc::Module::Install calls "require Module::Install"
+#     5. The ./inc/ version of Module::Install loads
+# } ELSE {
+#     1. Makefile.PL calls "use inc::Module::Install"
+#     2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install
+#     3. The ./inc/ version of Module::Install loads
+# }
+
+use 5.004;
+use strict 'vars';
+
+use vars qw{$VERSION};
+BEGIN {
+    # All Module::Install core packages now require synchronised versions.
+    # This will be used to ensure we don't accidentally load old or
+    # different versions of modules.
+    # This is not enforced yet, but will be some time in the next few
+    # releases once we can make sure it won't clash with custom
+    # Module::Install extensions.
+    $VERSION = '0.68';
+}
+
+# Whether or not inc::Module::Install is actually loaded, the
+# $INC{inc/Module/Install.pm} is what will still get set as long as
+# the caller loaded module this in the documented manner.
+# If not set, the caller may NOT have loaded the bundled version, and thus
+# they may not have a MI version that works with the Makefile.PL. This would
+# result in false errors or unexpected behaviour. And we don't want that.
+my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm';
+unless ( $INC{$file} ) {
+    die <<"END_DIE";
+Please invoke ${\__PACKAGE__} with:
+
+    use inc::${\__PACKAGE__};
+
+not:
+
+    use ${\__PACKAGE__};
+
+END_DIE
+}
+
+# If the script that is loading Module::Install is from the future,
+# then make will detect this and cause it to re-run over and over
+# again. This is bad. Rather than taking action to touch it (which
+# is unreliable on some platforms and requires write permissions)
+# for now we should catch this and refuse to run.
+if ( -f $0 and (stat($0))[9] > time ) {
+	die << "END_DIE";
+Your installer $0 has a modification time in the future.
+
+This is known to create infinite loops in make.
+
+Please correct this, then run $0 again.
+
+END_DIE
+}
+
+use Cwd        ();
+use File::Find ();
+use File::Path ();
+use FindBin;
+
+*inc::Module::Install::VERSION = *VERSION;
+ at inc::Module::Install::ISA     = __PACKAGE__;
+
+sub autoload {
+    my $self = shift;
+    my $who  = $self->_caller;
+    my $cwd  = Cwd::cwd();
+    my $sym  = "${who}::AUTOLOAD";
+    $sym->{$cwd} = sub {
+        my $pwd = Cwd::cwd();
+        if ( my $code = $sym->{$pwd} ) {
+            # delegate back to parent dirs
+            goto &$code unless $cwd eq $pwd;
+        }
+        $$sym =~ /([^:]+)$/ or die "Cannot autoload $who - $sym";
+        unshift @_, ($self, $1);
+        goto &{$self->can('call')} unless uc($1) eq $1;
+    };
+}
+
+sub import {
+    my $class = shift;
+    my $self  = $class->new(@_);
+    my $who   = $self->_caller;
+
+    unless ( -f $self->{file} ) {
+        require "$self->{path}/$self->{dispatch}.pm";
+        File::Path::mkpath("$self->{prefix}/$self->{author}");
+        $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self );
+        $self->{admin}->init;
+        @_ = ($class, _self => $self);
+        goto &{"$self->{name}::import"};
+    }
+
+    *{"${who}::AUTOLOAD"} = $self->autoload;
+    $self->preload;
+
+    # Unregister loader and worker packages so subdirs can use them again
+    delete $INC{"$self->{file}"};
+    delete $INC{"$self->{path}.pm"};
+}
+
+sub preload {
+    my ($self) = @_;
+
+    unless ( $self->{extensions} ) {
+        $self->load_extensions(
+            "$self->{prefix}/$self->{path}", $self
+        );
+    }
+
+    my @exts = @{$self->{extensions}};
+    unless ( @exts ) {
+        my $admin = $self->{admin};
+        @exts = $admin->load_all_extensions;
+    }
+
+    my %seen;
+    foreach my $obj ( @exts ) {
+        while (my ($method, $glob) = each %{ref($obj) . '::'}) {
+            next unless $obj->can($method);
+            next if $method =~ /^_/;
+            next if $method eq uc($method);
+            $seen{$method}++;
+        }
+    }
+
+    my $who = $self->_caller;
+    foreach my $name ( sort keys %seen ) {
+        *{"${who}::$name"} = sub {
+            ${"${who}::AUTOLOAD"} = "${who}::$name";
+            goto &{"${who}::AUTOLOAD"};
+        };
+    }
+}
+
+sub new {
+    my ($class, %args) = @_;
+
+    # ignore the prefix on extension modules built from top level.
+    my $base_path = Cwd::abs_path($FindBin::Bin);
+    unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) {
+        delete $args{prefix};
+    }
+
+    return $args{_self} if $args{_self};
+
+    $args{dispatch} ||= 'Admin';
+    $args{prefix}   ||= 'inc';
+    $args{author}   ||= ($^O eq 'VMS' ? '_author' : '.author');
+    $args{bundle}   ||= 'inc/BUNDLES';
+    $args{base}     ||= $base_path;
+    $class =~ s/^\Q$args{prefix}\E:://;
+    $args{name}     ||= $class;
+    $args{version}  ||= $class->VERSION;
+    unless ( $args{path} ) {
+        $args{path}  = $args{name};
+        $args{path}  =~ s!::!/!g;
+    }
+    $args{file}     ||= "$args{base}/$args{prefix}/$args{path}.pm";
+
+    bless( \%args, $class );
+}
+
+sub call {
+	my ($self, $method) = @_;
+	my $obj = $self->load($method) or return;
+        splice(@_, 0, 2, $obj);
+	goto &{$obj->can($method)};
+}
+
+sub load {
+    my ($self, $method) = @_;
+
+    $self->load_extensions(
+        "$self->{prefix}/$self->{path}", $self
+    ) unless $self->{extensions};
+
+    foreach my $obj (@{$self->{extensions}}) {
+        return $obj if $obj->can($method);
+    }
+
+    my $admin = $self->{admin} or die <<"END_DIE";
+The '$method' method does not exist in the '$self->{prefix}' path!
+Please remove the '$self->{prefix}' directory and run $0 again to load it.
+END_DIE
+
+    my $obj = $admin->load($method, 1);
+    push @{$self->{extensions}}, $obj;
+
+    $obj;
+}
+
+sub load_extensions {
+    my ($self, $path, $top) = @_;
+
+    unless ( grep { lc $_ eq lc $self->{prefix} } @INC ) {
+        unshift @INC, $self->{prefix};
+    }
+
+    foreach my $rv ( $self->find_extensions($path) ) {
+        my ($file, $pkg) = @{$rv};
+        next if $self->{pathnames}{$pkg};
+
+        local $@;
+        my $new = eval { require $file; $pkg->can('new') };
+        unless ( $new ) {
+            warn $@ if $@;
+            next;
+        }
+        $self->{pathnames}{$pkg} = delete $INC{$file};
+        push @{$self->{extensions}}, &{$new}($pkg, _top => $top );
+    }
+
+    $self->{extensions} ||= [];
+}
+
+sub find_extensions {
+    my ($self, $path) = @_;
+
+    my @found;
+    File::Find::find( sub {
+        my $file = $File::Find::name;
+        return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is;
+        my $subpath = $1;
+        return if lc($subpath) eq lc($self->{dispatch});
+
+        $file = "$self->{path}/$subpath.pm";
+        my $pkg = "$self->{name}::$subpath";
+        $pkg =~ s!/!::!g;
+
+        # If we have a mixed-case package name, assume case has been preserved
+        # correctly.  Otherwise, root through the file to locate the case-preserved
+        # version of the package name.
+        if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) {
+            open PKGFILE, "<$subpath.pm" or die "find_extensions: Can't open $subpath.pm: $!";
+            my $in_pod = 0;
+            while ( <PKGFILE> ) {
+                $in_pod = 1 if /^=\w/;
+                $in_pod = 0 if /^=cut/;
+                next if ($in_pod || /^=cut/);  # skip pod text
+                next if /^\s*#/;               # and comments
+                if ( m/^\s*package\s+($pkg)\s*;/i ) {
+                    $pkg = $1;
+                    last;
+                }
+            }
+            close PKGFILE;
+        }
+
+        push @found, [ $file, $pkg ];
+    }, $path ) if -d $path;
+
+    @found;
+}
+
+sub _caller {
+    my $depth = 0;
+    my $call  = caller($depth);
+    while ( $call eq __PACKAGE__ ) {
+        $depth++;
+        $call = caller($depth);
+    }
+    return $call;
+}
+
+1;

Added: Jifty-Book/inc/Module/Install/Base.pm
==============================================================================
--- (empty file)
+++ Jifty-Book/inc/Module/Install/Base.pm	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,70 @@
+#line 1
+package Module::Install::Base;
+
+$VERSION = '0.68';
+
+# Suspend handler for "redefined" warnings
+BEGIN {
+	my $w = $SIG{__WARN__};
+	$SIG{__WARN__} = sub { $w };
+}
+
+### This is the ONLY module that shouldn't have strict on
+# use strict;
+
+#line 41
+
+sub new {
+    my ($class, %args) = @_;
+
+    foreach my $method ( qw(call load) ) {
+        *{"$class\::$method"} = sub {
+            shift()->_top->$method(@_);
+        } unless defined &{"$class\::$method"};
+    }
+
+    bless( \%args, $class );
+}
+
+#line 61
+
+sub AUTOLOAD {
+    my $self = shift;
+    local $@;
+    my $autoload = eval { $self->_top->autoload } or return;
+    goto &$autoload;
+}
+
+#line 76
+
+sub _top { $_[0]->{_top} }
+
+#line 89
+
+sub admin {
+    $_[0]->_top->{admin} or Module::Install::Base::FakeAdmin->new;
+}
+
+sub is_admin {
+    $_[0]->admin->VERSION;
+}
+
+sub DESTROY {}
+
+package Module::Install::Base::FakeAdmin;
+
+my $Fake;
+sub new { $Fake ||= bless(\@_, $_[0]) }
+
+sub AUTOLOAD {}
+
+sub DESTROY {}
+
+# Restore warning handler
+BEGIN {
+	$SIG{__WARN__} = $SIG{__WARN__}->();
+}
+
+1;
+
+#line 138

Added: Jifty-Book/inc/Module/Install/Can.pm
==============================================================================
--- (empty file)
+++ Jifty-Book/inc/Module/Install/Can.pm	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,82 @@
+#line 1
+package Module::Install::Can;
+
+use strict;
+use Module::Install::Base;
+use Config ();
+### This adds a 5.005 Perl version dependency.
+### This is a bug and will be fixed.
+use File::Spec ();
+use ExtUtils::MakeMaker ();
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.68';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+# check if we can load some module
+### Upgrade this to not have to load the module if possible
+sub can_use {
+	my ($self, $mod, $ver) = @_;
+	$mod =~ s{::|\\}{/}g;
+	$mod .= '.pm' unless $mod =~ /\.pm$/i;
+
+	my $pkg = $mod;
+	$pkg =~ s{/}{::}g;
+	$pkg =~ s{\.pm$}{}i;
+
+	local $@;
+	eval { require $mod; $pkg->VERSION($ver || 0); 1 };
+}
+
+# check if we can run some command
+sub can_run {
+	my ($self, $cmd) = @_;
+
+	my $_cmd = $cmd;
+	return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd));
+
+	for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') {
+		my $abs = File::Spec->catfile($dir, $_[1]);
+		return $abs if (-x $abs or $abs = MM->maybe_command($abs));
+	}
+
+	return;
+}
+
+# can we locate a (the) C compiler
+sub can_cc {
+	my $self   = shift;
+	my @chunks = split(/ /, $Config::Config{cc}) or return;
+
+	# $Config{cc} may contain args; try to find out the program part
+	while (@chunks) {
+		return $self->can_run("@chunks") || (pop(@chunks), next);
+	}
+
+	return;
+}
+
+# Fix Cygwin bug on maybe_command();
+if ( $^O eq 'cygwin' ) {
+	require ExtUtils::MM_Cygwin;
+	require ExtUtils::MM_Win32;
+	if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) {
+		*ExtUtils::MM_Cygwin::maybe_command = sub {
+			my ($self, $file) = @_;
+			if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) {
+				ExtUtils::MM_Win32->maybe_command($file);
+			} else {
+				ExtUtils::MM_Unix->maybe_command($file);
+			}
+		}
+	}
+}
+
+1;
+
+__END__
+
+#line 157

Added: Jifty-Book/inc/Module/Install/Fetch.pm
==============================================================================
--- (empty file)
+++ Jifty-Book/inc/Module/Install/Fetch.pm	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,93 @@
+#line 1
+package Module::Install::Fetch;
+
+use strict;
+use Module::Install::Base;
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.68';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+sub get_file {
+    my ($self, %args) = @_;
+    my ($scheme, $host, $path, $file) = 
+        $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
+
+    if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) {
+        $args{url} = $args{ftp_url}
+            or (warn("LWP support unavailable!\n"), return);
+        ($scheme, $host, $path, $file) = 
+            $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
+    }
+
+    $|++;
+    print "Fetching '$file' from $host... ";
+
+    unless (eval { require Socket; Socket::inet_aton($host) }) {
+        warn "'$host' resolve failed!\n";
+        return;
+    }
+
+    return unless $scheme eq 'ftp' or $scheme eq 'http';
+
+    require Cwd;
+    my $dir = Cwd::getcwd();
+    chdir $args{local_dir} or return if exists $args{local_dir};
+
+    if (eval { require LWP::Simple; 1 }) {
+        LWP::Simple::mirror($args{url}, $file);
+    }
+    elsif (eval { require Net::FTP; 1 }) { eval {
+        # use Net::FTP to get past firewall
+        my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600);
+        $ftp->login("anonymous", 'anonymous at example.com');
+        $ftp->cwd($path);
+        $ftp->binary;
+        $ftp->get($file) or (warn("$!\n"), return);
+        $ftp->quit;
+    } }
+    elsif (my $ftp = $self->can_run('ftp')) { eval {
+        # no Net::FTP, fallback to ftp.exe
+        require FileHandle;
+        my $fh = FileHandle->new;
+
+        local $SIG{CHLD} = 'IGNORE';
+        unless ($fh->open("|$ftp -n")) {
+            warn "Couldn't open ftp: $!\n";
+            chdir $dir; return;
+        }
+
+        my @dialog = split(/\n/, <<"END_FTP");
+open $host
+user anonymous anonymous\@example.com
+cd $path
+binary
+get $file $file
+quit
+END_FTP
+        foreach (@dialog) { $fh->print("$_\n") }
+        $fh->close;
+    } }
+    else {
+        warn "No working 'ftp' program available!\n";
+        chdir $dir; return;
+    }
+
+    unless (-f $file) {
+        warn "Fetching failed: $@\n";
+        chdir $dir; return;
+    }
+
+    return if exists $args{size} and -s $file != $args{size};
+    system($args{run}) if exists $args{run};
+    unlink($file) if $args{remove};
+
+    print(((!exists $args{check_for} or -e $args{check_for})
+        ? "done!" : "failed! ($!)"), "\n");
+    chdir $dir; return !$?;
+}
+
+1;

Added: Jifty-Book/inc/Module/Install/Makefile.pm
==============================================================================
--- (empty file)
+++ Jifty-Book/inc/Module/Install/Makefile.pm	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,237 @@
+#line 1
+package Module::Install::Makefile;
+
+use strict 'vars';
+use Module::Install::Base;
+use ExtUtils::MakeMaker ();
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.68';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+sub Makefile { $_[0] }
+
+my %seen = ();
+
+sub prompt {
+	shift;
+
+	# Infinite loop protection
+	my @c = caller();
+	if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) {
+		die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])";
+	}
+
+	# In automated testing, always use defaults
+	if ( $ENV{AUTOMATED_TESTING} and ! $ENV{PERL_MM_USE_DEFAULT} ) {
+		local $ENV{PERL_MM_USE_DEFAULT} = 1;
+		goto &ExtUtils::MakeMaker::prompt;
+	} else {
+		goto &ExtUtils::MakeMaker::prompt;
+	}
+}
+
+sub makemaker_args {
+	my $self = shift;
+	my $args = ($self->{makemaker_args} ||= {});
+	%$args = ( %$args, @_ ) if @_;
+	$args;
+}
+
+# For mm args that take multiple space-seperated args,
+# append an argument to the current list.
+sub makemaker_append {
+	my $self = sShift;
+	my $name = shift;
+	my $args = $self->makemaker_args;
+	$args->{name} = defined $args->{$name}
+		? join( ' ', $args->{name}, @_ )
+		: join( ' ', @_ );
+}
+
+sub build_subdirs {
+	my $self    = shift;
+	my $subdirs = $self->makemaker_args->{DIR} ||= [];
+	for my $subdir (@_) {
+		push @$subdirs, $subdir;
+	}
+}
+
+sub clean_files {
+	my $self  = shift;
+	my $clean = $self->makemaker_args->{clean} ||= {};
+	%$clean = (
+		%$clean, 
+		FILES => join(' ', grep length, $clean->{FILES}, @_),
+	);
+}
+
+sub realclean_files {
+	my $self  = shift;
+	my $realclean = $self->makemaker_args->{realclean} ||= {};
+	%$realclean = (
+		%$realclean, 
+		FILES => join(' ', grep length, $realclean->{FILES}, @_),
+	);
+}
+
+sub libs {
+	my $self = shift;
+	my $libs = ref $_[0] ? shift : [ shift ];
+	$self->makemaker_args( LIBS => $libs );
+}
+
+sub inc {
+	my $self = shift;
+	$self->makemaker_args( INC => shift );
+}
+
+my %test_dir = ();
+
+sub _wanted_t {
+	/\.t$/ and -f $_ and $test_dir{$File::Find::dir} = 1;
+}
+
+sub tests_recursive {
+	my $self = shift;
+	if ( $self->tests ) {
+		die "tests_recursive will not work if tests are already defined";
+	}
+	my $dir = shift || 't';
+	unless ( -d $dir ) {
+		die "tests_recursive dir '$dir' does not exist";
+	}
+	require File::Find;
+	%test_dir = ();
+	File::Find::find( \&_wanted_t, $dir );
+	$self->tests( join ' ', map { "$_/*.t" } sort keys %test_dir );
+}
+
+sub write {
+	my $self = shift;
+	die "&Makefile->write() takes no arguments\n" if @_;
+
+	my $args = $self->makemaker_args;
+	$args->{DISTNAME} = $self->name;
+	$args->{NAME}     = $self->module_name || $self->name || $self->determine_NAME($args);
+	$args->{VERSION}  = $self->version || $self->determine_VERSION($args);
+	$args->{NAME}     =~ s/-/::/g;
+	if ( $self->tests ) {
+		$args->{test} = { TESTS => $self->tests };
+	}
+	if ($] >= 5.005) {
+		$args->{ABSTRACT} = $self->abstract;
+		$args->{AUTHOR}   = $self->author;
+	}
+	if ( eval($ExtUtils::MakeMaker::VERSION) >= 6.10 ) {
+		$args->{NO_META} = 1;
+	}
+	if ( eval($ExtUtils::MakeMaker::VERSION) > 6.17 and $self->sign ) {
+		$args->{SIGN} = 1;
+	}
+	unless ( $self->is_admin ) {
+		delete $args->{SIGN};
+	}
+
+	# merge both kinds of requires into prereq_pm
+	my $prereq = ($args->{PREREQ_PM} ||= {});
+	%$prereq = ( %$prereq,
+		map { @$_ }
+		map { @$_ }
+		grep $_,
+		($self->build_requires, $self->requires)
+	);
+
+	# merge both kinds of requires into prereq_pm
+	my $subdirs = ($args->{DIR} ||= []);
+	if ($self->bundles) {
+		foreach my $bundle (@{ $self->bundles }) {
+			my ($file, $dir) = @$bundle;
+			push @$subdirs, $dir if -d $dir;
+			delete $prereq->{$file};
+		}
+	}
+
+	if ( my $perl_version = $self->perl_version ) {
+		eval "use $perl_version; 1"
+			or die "ERROR: perl: Version $] is installed, "
+			. "but we need version >= $perl_version";
+	}
+
+	$args->{INSTALLDIRS} = $self->installdirs;
+
+	my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_})} keys %$args;
+
+	my $user_preop = delete $args{dist}->{PREOP};
+	if (my $preop = $self->admin->preop($user_preop)) {
+		$args{dist} = $preop;
+	}
+
+	my $mm = ExtUtils::MakeMaker::WriteMakefile(%args);
+	$self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile');
+}
+
+sub fix_up_makefile {
+	my $self          = shift;
+	my $makefile_name = shift;
+	my $top_class     = ref($self->_top) || '';
+	my $top_version   = $self->_top->VERSION || '';
+
+	my $preamble = $self->preamble 
+		? "# Preamble by $top_class $top_version\n"
+			. $self->preamble
+		: '';
+	my $postamble = "# Postamble by $top_class $top_version\n"
+		. ($self->postamble || '');
+
+	local *MAKEFILE;
+	open MAKEFILE, "< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+	my $makefile = do { local $/; <MAKEFILE> };
+	close MAKEFILE or die $!;
+
+	$makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /;
+	$makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g;
+	$makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g;
+	$makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m;
+	$makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m;
+
+	# Module::Install will never be used to build the Core Perl
+	# Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks
+	# PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist
+	$makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m;
+	#$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m;
+
+	# Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well.
+	$makefile =~ s/("?)-I\$\(PERL_LIB\)\1//g;
+
+	# XXX - This is currently unused; not sure if it breaks other MM-users
+	# $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg;
+
+	open  MAKEFILE, "> $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+	print MAKEFILE  "$preamble$makefile$postamble" or die $!;
+	close MAKEFILE  or die $!;
+
+	1;
+}
+
+sub preamble {
+	my ($self, $text) = @_;
+	$self->{preamble} = $text . $self->{preamble} if defined $text;
+	$self->{preamble};
+}
+
+sub postamble {
+	my ($self, $text) = @_;
+	$self->{postamble} ||= $self->admin->postamble;
+	$self->{postamble} .= $text if defined $text;
+	$self->{postamble}
+}
+
+1;
+
+__END__
+
+#line 363

Added: Jifty-Book/inc/Module/Install/Metadata.pm
==============================================================================
--- (empty file)
+++ Jifty-Book/inc/Module/Install/Metadata.pm	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,336 @@
+#line 1
+package Module::Install::Metadata;
+
+use strict 'vars';
+use Module::Install::Base;
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.68';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+my @scalar_keys = qw{
+    name module_name abstract author version license
+    distribution_type perl_version tests installdirs
+};
+
+my @tuple_keys = qw{
+    build_requires requires recommends bundles
+};
+
+sub Meta            { shift        }
+sub Meta_ScalarKeys { @scalar_keys }
+sub Meta_TupleKeys  { @tuple_keys  }
+
+foreach my $key (@scalar_keys) {
+    *$key = sub {
+        my $self = shift;
+        return $self->{values}{$key} if defined wantarray and !@_;
+        $self->{values}{$key} = shift;
+        return $self;
+    };
+}
+
+foreach my $key (@tuple_keys) {
+    *$key = sub {
+        my $self = shift;
+        return $self->{values}{$key} unless @_;
+
+        my @rv;
+        while (@_) {
+            my $module = shift or last;
+            my $version = shift || 0;
+            if ( $module eq 'perl' ) {
+                $version =~ s{^(\d+)\.(\d+)\.(\d+)}
+                             {$1 + $2/1_000 + $3/1_000_000}e;
+                $self->perl_version($version);
+                next;
+            }
+            my $rv = [ $module, $version ];
+            push @rv, $rv;
+        }
+        push @{ $self->{values}{$key} }, @rv;
+        @rv;
+    };
+}
+
+# configure_requires is currently a null-op
+sub configure_requires { 1 }
+
+# Aliases for build_requires that will have alternative
+# meanings in some future version of META.yml.
+sub test_requires      { shift->build_requires(@_)  }
+sub install_requires   { shift->build_requires(@_)  }
+
+# Aliases for installdirs options
+sub install_as_core    { $_[0]->installdirs('perl')   }
+sub install_as_cpan    { $_[0]->installdirs('site')   }
+sub install_as_site    { $_[0]->installdirs('site')   }
+sub install_as_vendor  { $_[0]->installdirs('vendor') }
+
+sub sign {
+    my $self = shift;
+    return $self->{'values'}{'sign'} if defined wantarray and ! @_;
+    $self->{'values'}{'sign'} = ( @_ ? $_[0] : 1 );
+    return $self;
+}
+
+sub dynamic_config {
+	my $self = shift;
+	unless ( @_ ) {
+		warn "You MUST provide an explicit true/false value to dynamic_config, skipping\n";
+		return $self;
+	}
+	$self->{'values'}{'dynamic_config'} = $_[0] ? 1 : 0;
+	return $self;
+}
+
+sub all_from {
+    my ( $self, $file ) = @_;
+
+    unless ( defined($file) ) {
+        my $name = $self->name
+            or die "all_from called with no args without setting name() first";
+        $file = join('/', 'lib', split(/-/, $name)) . '.pm';
+        $file =~ s{.*/}{} unless -e $file;
+        die "all_from: cannot find $file from $name" unless -e $file;
+    }
+
+    $self->version_from($file)      unless $self->version;
+    $self->perl_version_from($file) unless $self->perl_version;
+
+    # The remaining probes read from POD sections; if the file
+    # has an accompanying .pod, use that instead
+    my $pod = $file;
+    if ( $pod =~ s/\.pm$/.pod/i and -e $pod ) {
+        $file = $pod;
+    }
+
+    $self->author_from($file)   unless $self->author;
+    $self->license_from($file)  unless $self->license;
+    $self->abstract_from($file) unless $self->abstract;
+}
+
+sub provides {
+    my $self     = shift;
+    my $provides = ( $self->{values}{provides} ||= {} );
+    %$provides = (%$provides, @_) if @_;
+    return $provides;
+}
+
+sub auto_provides {
+    my $self = shift;
+    return $self unless $self->is_admin;
+
+    unless (-e 'MANIFEST') {
+        warn "Cannot deduce auto_provides without a MANIFEST, skipping\n";
+        return $self;
+    }
+
+    # Avoid spurious warnings as we are not checking manifest here.
+
+    local $SIG{__WARN__} = sub {1};
+    require ExtUtils::Manifest;
+    local *ExtUtils::Manifest::manicheck = sub { return };
+
+    require Module::Build;
+    my $build = Module::Build->new(
+        dist_name    => $self->name,
+        dist_version => $self->version,
+        license      => $self->license,
+    );
+    $self->provides(%{ $build->find_dist_packages || {} });
+}
+
+sub feature {
+    my $self     = shift;
+    my $name     = shift;
+    my $features = ( $self->{values}{features} ||= [] );
+
+    my $mods;
+
+    if ( @_ == 1 and ref( $_[0] ) ) {
+        # The user used ->feature like ->features by passing in the second
+        # argument as a reference.  Accomodate for that.
+        $mods = $_[0];
+    } else {
+        $mods = \@_;
+    }
+
+    my $count = 0;
+    push @$features, (
+        $name => [
+            map {
+                ref($_) ? ( ref($_) eq 'HASH' ) ? %$_
+                                                : @$_
+                        : $_
+            } @$mods
+        ]
+    );
+
+    return @$features;
+}
+
+sub features {
+    my $self = shift;
+    while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) {
+        $self->feature( $name, @$mods );
+    }
+    return $self->{values}->{features}
+    	? @{ $self->{values}->{features} }
+    	: ();
+}
+
+sub no_index {
+    my $self = shift;
+    my $type = shift;
+    push @{ $self->{values}{no_index}{$type} }, @_ if $type;
+    return $self->{values}{no_index};
+}
+
+sub read {
+    my $self = shift;
+    $self->include_deps( 'YAML', 0 );
+
+    require YAML;
+    my $data = YAML::LoadFile('META.yml');
+
+    # Call methods explicitly in case user has already set some values.
+    while ( my ( $key, $value ) = each %$data ) {
+        next unless $self->can($key);
+        if ( ref $value eq 'HASH' ) {
+            while ( my ( $module, $version ) = each %$value ) {
+                $self->can($key)->($self, $module => $version );
+            }
+        }
+        else {
+            $self->can($key)->($self, $value);
+        }
+    }
+    return $self;
+}
+
+sub write {
+    my $self = shift;
+    return $self unless $self->is_admin;
+    $self->admin->write_meta;
+    return $self;
+}
+
+sub version_from {
+    my ( $self, $file ) = @_;
+    require ExtUtils::MM_Unix;
+    $self->version( ExtUtils::MM_Unix->parse_version($file) );
+}
+
+sub abstract_from {
+    my ( $self, $file ) = @_;
+    require ExtUtils::MM_Unix;
+    $self->abstract(
+        bless(
+            { DISTNAME => $self->name },
+            'ExtUtils::MM_Unix'
+        )->parse_abstract($file)
+     );
+}
+
+sub _slurp {
+    my ( $self, $file ) = @_;
+
+    local *FH;
+    open FH, "< $file" or die "Cannot open $file.pod: $!";
+    do { local $/; <FH> };
+}
+
+sub perl_version_from {
+    my ( $self, $file ) = @_;
+
+    if (
+        $self->_slurp($file) =~ m/
+        ^
+        use \s*
+        v?
+        ([\d_\.]+)
+        \s* ;
+    /ixms
+      )
+    {
+        my $v = $1;
+        $v =~ s{_}{}g;
+        $self->perl_version($1);
+    }
+    else {
+        warn "Cannot determine perl version info from $file\n";
+        return;
+    }
+}
+
+sub author_from {
+    my ( $self, $file ) = @_;
+    my $content = $self->_slurp($file);
+    if ($content =~ m/
+        =head \d \s+ (?:authors?)\b \s*
+        ([^\n]*)
+        |
+        =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s*
+        .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s*
+        ([^\n]*)
+    /ixms) {
+        my $author = $1 || $2;
+        $author =~ s{E<lt>}{<}g;
+        $author =~ s{E<gt>}{>}g;
+        $self->author($author); 
+    }
+    else {
+        warn "Cannot determine author info from $file\n";
+    }
+}
+
+sub license_from {
+    my ( $self, $file ) = @_;
+
+    if (
+        $self->_slurp($file) =~ m/
+        (
+            =head \d \s+
+            (?:licen[cs]e|licensing|copyright|legal)\b
+            .*?
+        )
+        (=head\\d.*|=cut.*|)
+        \z
+    /ixms
+      )
+    {
+        my $license_text = $1;
+        my @phrases      = (
+            'under the same (?:terms|license) as perl itself' => 'perl',        1,
+            'GNU public license'                              => 'gpl',         1,
+            'GNU lesser public license'                       => 'gpl',         1,
+            'BSD license'                                     => 'bsd',         1,
+            'Artistic license'                                => 'artistic',    1,
+            'GPL'                                             => 'gpl',         1,
+            'LGPL'                                            => 'lgpl',        1,
+            'BSD'                                             => 'bsd',         1,
+            'Artistic'                                        => 'artistic',    1,
+            'MIT'                                             => 'mit',         1,
+            'proprietary'                                     => 'proprietary', 0,
+        );
+        while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) {
+            $pattern =~ s{\s+}{\\s+}g;
+            if ( $license_text =~ /\b$pattern\b/i ) {
+                if ( $osi and $license_text =~ /All rights reserved/i ) {
+                        warn "LEGAL WARNING: 'All rights reserved' may invalidate Open Source licenses. Consider removing it.";
+		}
+                $self->license($license);
+                return 1;
+            }
+        }
+    }
+
+    warn "Cannot determine license info from $file\n";
+    return 'unknown';
+}
+
+1;

Added: Jifty-Book/inc/Module/Install/Win32.pm
==============================================================================
--- (empty file)
+++ Jifty-Book/inc/Module/Install/Win32.pm	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,65 @@
+#line 1
+package Module::Install::Win32;
+
+use strict;
+use Module::Install::Base;
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.68';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+# determine if the user needs nmake, and download it if needed
+sub check_nmake {
+	my $self = shift;
+	$self->load('can_run');
+	$self->load('get_file');
+	
+	require Config;
+	return unless (
+		$^O eq 'MSWin32'                     and
+		$Config::Config{make}                and
+		$Config::Config{make} =~ /^nmake\b/i and
+		! $self->can_run('nmake')
+	);
+
+	print "The required 'nmake' executable not found, fetching it...\n";
+
+	require File::Basename;
+	my $rv = $self->get_file(
+		url       => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe',
+		ftp_url   => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe',
+		local_dir => File::Basename::dirname($^X),
+		size      => 51928,
+		run       => 'Nmake15.exe /o > nul',
+		check_for => 'Nmake.exe',
+		remove    => 1,
+	);
+
+	if (!$rv) {
+        die <<'END_MESSAGE';
+
+-------------------------------------------------------------------------------
+
+Since you are using Microsoft Windows, you will need the 'nmake' utility
+before installation. It's available at:
+
+  http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe
+      or
+  ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe
+
+Please download the file manually, save it to a directory in %PATH% (e.g.
+C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to
+that directory, and run "Nmake15.exe" from there; that will create the
+'nmake.exe' file needed by this module.
+
+You may then resume the installation process described in README.
+
+-------------------------------------------------------------------------------
+END_MESSAGE
+	}
+}
+
+1;

Added: Jifty-Book/inc/Module/Install/WriteAll.pm
==============================================================================
--- (empty file)
+++ Jifty-Book/inc/Module/Install/WriteAll.pm	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,43 @@
+#line 1
+package Module::Install::WriteAll;
+
+use strict;
+use Module::Install::Base;
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.68';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+sub WriteAll {
+    my $self = shift;
+    my %args = (
+        meta        => 1,
+        sign        => 0,
+        inline      => 0,
+        check_nmake => 1,
+        @_
+    );
+
+    $self->sign(1)                if $args{sign};
+    $self->Meta->write            if $args{meta};
+    $self->admin->WriteAll(%args) if $self->is_admin;
+
+    if ( $0 =~ /Build.PL$/i ) {
+        $self->Build->write;
+    } else {
+        $self->check_nmake if $args{check_nmake};
+        unless ( $self->makemaker_args->{'PL_FILES'} ) {
+        	$self->makemaker_args( PL_FILES => {} );
+        }
+        if ($args{inline}) {
+            $self->Inline->write;
+        } else {
+            $self->Makefile->write;
+        }
+    }
+}
+
+1;

Added: Jifty-Book/t/ppod-check.t
==============================================================================
--- (empty file)
+++ Jifty-Book/t/ppod-check.t	Wed Nov 21 02:23:15 2007
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+use File::Find::Rule;
+use Pod::PseudoPod::Checker;
+use Test::More;
+
+my @files = File::Find::Rule->file
+                            ->name('*.pod')
+                            ->in('doc');
+
+plan tests => scalar @files;
+
+for my $file (@files) {
+    my $errors = '';
+    open my $capture, '>', \$errors 
+        or die "Cannot open an internal reference, upgrade Perl.";
+
+    my $parser = Pod::PseudoPod::Checker->new;
+    $parser->output_fh($capture);
+    $parser->parse_file($file);
+
+    if ($errors =~ /No errors seen!/) {
+        pass($errors);
+    }
+    else {
+        fail($errors);
+    }
+}
+
+exit;


More information about the Jifty-commit mailing list