[RFC] Module::Husbandry

2002-12-20 Thread Barrie Slaymaker
Part of QA is defining best practices.  I've been encoding a few
operations I use a lot when writing new perl module distributions in to
script form to make my code and POD more consistent.  Here's a
prerelease of them:

http://users.telerama.com/~rbs/src/Module-Husbandry-0.0001.tar.gz

and the README is below.

Feedback?

Thanks,

Barrie


README for Module::Husbandry

ALPHA CODE ALERT: This code is alpha; the API and behaviors are
likely to change.  This release is for evaluation and feedback;
see the TODO file for some ideas of where this might go.

A module and a set of wrapper scripts that let me manage perl
distributions a bit more easily.  There are more to come; the current
crop is:

newpmdist  Creates a new distribution tree and populates it
newpm  Creates a new module in a distribution
mvpm   Renames a module and all occurences of its module name
   and path:

   s{Foo::Bar}{Foo::Bat}g;
   s{lib/Foo/Bar.pm}{lib/Foo/Bat.pm}g;
   s{t/Foo-Bar.t}{t/Foo-Bat.t}g;

To use the new* scripts, you need a ~/.newpm/ directory with appropriate
templates (see below).  mvpm just makes some rash assumptions (also see
below) about your directory structure and test script naming convention.

Here are the usages:

newpmdist Foo-Bar## Create Foo-Bar/ and populate it
newpmdist Foo::Bar   ## as above


newpm Foo::Bar   ## Create lib/Foo/Bar.pm and t/Foo-Bar.t
newpm lib/Foo/Bar.pm ## Create lib/Foo/Bar.pm and t/Foo-Bar.t
newpm lib/Foo/Bar.pod## Create lib/Foo/Bar.pod only

newpm Foo::Bar Foo::Bar  ## as above, but for multiple modules


mvpm From::Module   To::Module# Use module names
mvpm lib/From/Module.pm lib/To/Module.pm  # or paths from main project dir
mvpm lib/From/Module.pm To::Module# or mix and match

mvpm -r FromTo# move dir tree (TODO)
mvpm -r lib/Fromlib/To# move dir tree (TODO)

As you can see, each can take a module name, if that's the way you
work, or a file or directory name.  Using file or directory names lets
you take advantage of your shell's autocompletion and history features.
For instance:

$ newpm lib/Fundingulus/Maximus.pm
$ edit !$

can be typed in a lot fewer keystrokes than

$ newpm Fundingulus::Maximus
$ edit lib/Fundingulus/Maximus.pm

newpmdist and newpm use a set of templates to instantiate new files.
These are stored (for now) in ~/.newpm:

$ cd ~/.newpm && find .
.
./Template.t
./Template.pm
./skel
./skel/MANIFEST.SKIP
./skel/Makefile.PL
./skel/t
./skel/CHANGES

This means that you control what files are populated how.  Some
example files (those) are in newpm_example_dir/ in the source tarball.

ASSumptions
===

These tools make the assumption that you use a working directory
structure like:

Foo-Bar/
lib/
Foo/
Bar.pm
Bah.pm
Bat.pm
Zed.pm
t/...

and not the Foo-Bar/Bar.pm style promulgates by h2xs.  This layout
(storing all .pm and .pod files under lib/) is more consistent and
flexible than storing the .pm and .pod files in the main directory,
but may not be to your liking.  Patches welcome :).

One side effect of this is that XS modules are may not be
well supported.

newpm and mvpm also create and check for test suites named after the
module like so:

Foo   t/Foo.t
Foo::Bar  t/Foo-Bar.t

.  This is also arbitrary, and even I don't really like it that
much, but it's the easiest way I could think of to be able to make
and rename test files automatically.


Example
===

Here's an example newpmdist invocation.  Towards the end, newpmdist
also does a newpm, so you can get a feel for what that's like (all lines
but the first are emitted by newpmdist):

$ newpmdist Foo-Bar
newpmdist$ mkdir -p Foo-Bar
newpmdist$ chdir Foo-Bar
newpmdist$ install Makefile.PL
newpmdist$ install MANIFEST.SKIP
newpmdist$ install CHANGES
newpmdist$ install lib/Foo/Bar.pm
newpmdist$ mkdir -p lib/Foo
newpmdist$ install t/Foo-Bar.t
newpmdist$ mkdir -p t
newpmdist/usr/local/bin/perl Makefile
Checking if your kit is complete...
Looks good
Writing Makefile for Foo::Bar
newpmdist$ chdir ..

Here's what things look like after all that:

$ cd Foo-Bar
$ find .
.
./Makefile.PL
./MANIFEST
./MANIFEST.SKIP
./CHANGES
./lib
./lib/Foo
./lib/Foo/Bar.pm
./t
./t/Foo-Bar.t
./Makefile
$ perl Makefile.PL
Writing Makefile for Foo::Bar
$ make distcheck
/usr/local/bin/perl "-MExtUtils::Manifest=fullcheck" -e fullcheck
$ make test
cp lib/Foo/Bar.pm blib/lib/Foo/Bar.pm
PERL_DL_NONLAZY=1 /usr/local/bin/perl "-MExtUtils::Command::MM" "-e" 
"test_harness(0, 'blib/lib', 'blib/arch')" t/*.t

Re: [RFC] Module::Husbandry

2002-12-20 Thread darren chamberlain
* Barrie Slaymaker <[EMAIL PROTECTED]> [2002-12-20 04:05]:
> Part of QA is defining best practices.  I've been encoding a few
> operations I use a lot when writing new perl module distributions in
> to script form to make my code and POD more consistent.

This is great.  I keep meaning to do something like this myself, get
about halfway there, and then end up doing something else.  Good work.

  - Using Template.{t,pm} is a little counter-intuitive -- this looks
like it's a ragular Perl module, instead of a template (despite the
name).  I'd use something like _skeleton.pm and _skeleton.t for
these files.

  - Something that I would consider adding is the ability to choose
different Template.pm's -- for example, if you are creating an Apache
module, as opposed to a POE component, the template you will want to
use will be radically different.  I think something like named templates
would work, so that:

  newpmdist -t Apache Foo::Bar

Would look for Apache.pm in ~/.newpm instead of Template.pm.  Of course,
this will break if you're writing a Template Toolkit plugin, but see my
previous comment about _skeleton.pm.

Overall, it looks very good.  When's the release party?  :)

(darren)

-- 
Don't be ashamed to say what you are not ashamed to think.



Re: [RFC] Module::Husbandry

2002-12-20 Thread Nicholas Clark
On Fri, Dec 20, 2002 at 08:17:16AM -0500, darren chamberlain wrote:
> This is great.  I keep meaning to do something like this myself, get
> about halfway there, and then end up doing something else.  Good work.
> 
>   - Using Template.{t,pm} is a little counter-intuitive -- this looks
> like it's a ragular Perl module, instead of a template (despite the
> name).  I'd use something like _skeleton.pm and _skeleton.t for
> these files.

Aargh. A bad pun has landed in my head and demands that I share it:

  tePMlate.pm

Hopefully it's clear that something with that name is not intended for
production.

Nicholas Clark



Re: [RFC] Module::Husbandry

2002-12-20 Thread darren chamberlain
* Nicholas Clark <[EMAIL PROTECTED]> [2002-12-20 08:30]:
> Aargh. A bad pun has landed in my head and demands that I share it:
> 
>   tePMlate.pm

And templaTe.t for the test skeleton.  I like it.

(darren)

-- 
The higher we soar the smaller we appear to those who cannot fly.
-- Friedrich Nietzsche



Re: [RFC] Module::Husbandry

2002-12-20 Thread Barrie Slaymaker
On Fri, Dec 20, 2002 at 08:17:16AM -0500, darren chamberlain wrote:
> * Barrie Slaymaker <[EMAIL PROTECTED]> [2002-12-20 04:05]:
> > Part of QA is defining best practices.  I've been encoding a few
> > operations I use a lot when writing new perl module distributions in
> > to script form to make my code and POD more consistent.
> 
> This is great.  I keep meaning to do something like this myself, get
> about halfway there, and then end up doing something else.  Good work.

Me too.  I finally got snorked at it and pushed it over the top.

> I'd use something like _skeleton.pm and _skeleton.t for
> [the template files]

Good point.  >>TODO

Probably "default.pm.template" or something.  I specifically resisted
the urge to use TT2, it's a cheesy s/<%...%>// thing, but I'd like to
make the templating system pluggable so folks can use their own, in
which case the final extension can be used to select it:

default.pm.tt2
apache.pm.mason

or something.

>   - the ability to choose different Template.pm

Good idea >>TODO.

I also want to be able to load a template and have it declare
(implicitly or explicitly) options so you could take the same template
and make it insert extra code, like default.pm would normally
instantiate a simple module, but could be cajoled in to instantiating as
a class (ie with new() and some POD for methods, perhaps an @ISA chunk
or whatever):

$ newpm Foo::Bar --class --isa=Camel::Jockey

I've kludged code to peer in to TT2 templates to get at [%META%]
declarations (it does not allow this by default AFAICS, surprisingly, I
had to grovel through the template object's guts for meta info), and
this has proven to be a useful way to let a template declare features or
request certain preprocessing functions, like options parsing :).

> Overall, it looks very good.  When's the release party?  :)

This is it, bub ;).  The URI I gave was to a real, live tarball.  I'll
at least fold in your ideas before it leaves alpha status.  Probably
over the holidays.

- Barrie



Re: [RFC] Module::Husbandry

2002-12-20 Thread darren chamberlain
* Barrie Slaymaker <[EMAIL PROTECTED]> [2002-12-20 08:52]:
> I've kludged code to peer in to TT2 templates to get at [%META%]
> declarations (it does not allow this by default AFAICS, surprisingly,
> I had to grovel through the template object's guts for meta info), and
> this has proven to be a useful way to let a template declare features
> or request certain preprocessing functions, like options parsing :).

Oh, that's a pretty straightforward (though completely undocumented) one:

  use Template::Provider;

  my $tp = Template::Provider->new(INCLUDE_PATH => [ 'here', 'there' ]);
  my $d = $tp->fetch("filename");

$d is a Template::Document, which has a nifty AUTOLOAD -- you can call
methods based on the name of the META variable.  So, with this template:

  [% META foo = 'bar' %]
  ...

You can call:

  my $foo = $d->foo();

and get 'bar'.

(darren)

-- 
Hard as it is to believe, sometimes bloody, proletariat revolution
just isn't the answer.  Like in Algebra class, for example.
-- Don Swain



Re: [RFC] Module::Husbandry

2002-12-20 Thread Barrie Slaymaker
On Fri, Dec 20, 2002 at 09:12:09AM -0500, darren chamberlain wrote:
> * Barrie Slaymaker <[EMAIL PROTECTED]> [2002-12-20 08:52]:
> > I've kludged code to peer in to TT2 templates to get at [%META%]
> > declarations (it does not allow this by default AFAICS, surprisingly,
> > I had to grovel through the template object's guts for meta info), and
> > this has proven to be a useful way to let a template declare features
> > or request certain preprocessing functions, like options parsing :).
> 
> Oh, that's a pretty straightforward (though completely undocumented) one:

heh, it's not straightforward if it's undocumented.  Yessirree, that's
my motto.

> You can call:
> 
>   my $foo = $d->foo();
> 
> and get 'bar'.

Thanks for the tip, that's a beaut I'll use.  Any chance of you sending
a docs patch to the TT2 folks?

- Barrie



Re: [RFC] Module::Husbandry

2002-12-20 Thread darren chamberlain
* Barrie Slaymaker <[EMAIL PROTECTED]> [2002-12-20 09:20]:
> On Fri, Dec 20, 2002 at 09:12:09AM -0500, darren chamberlain wrote:
> > Oh, that's a pretty straightforward (though completely undocumented)
> > one:
> 
> heh, it's not straightforward if it's undocumented.  Yessirree, that's
> my motto.

Fair enough. :)

> > You can call:
> > 
> >   my $foo = $d->foo();
> > 
> > and get 'bar'.
> 
> Thanks for the tip, that's a beaut I'll use.  Any chance of you sending
> a docs patch to the TT2 folks?

The docs for Template::Provider state:

  fetch($name)

Returns a compiled template for the name specified.  If the template
cannot be found then (undef, STATUS_DECLINED) is returned.  If an error
occurs (e.g. read error, parse error) then ($error, STATUS_ERROR) is
returned, where $error is the error message generated.  If the TOLERANT
flag is set the the method returns (undef, STATUS_DECLINED) instead of
returning an error.

Which, while acurrate, is not very informative. Seeing as how I'm one of
the TT2 folks, though, I can probably arrange for some examples to be
added to the docs.  :) 

(darren)

-- 
Going to church does not make a person religious, nor does going to
school make a person educated, any more than going to a garage makes
a person a car.



[OT] TT2 docs

2002-12-20 Thread Barrie Slaymaker
On Fri, Dec 20, 2002 at 09:54:50AM -0500, darren chamberlain wrote:
> 
> The docs for Template::Provider state:
> 
>   fetch($name)

fetch()ing's the easy part.  Even *I* got that far long ago.  It's the
fact that you can coax metadata out of the template objects with
AUTOLOADed methods that I didn't see in the docs (not that I've seen
every bit of the docs, but it didn't leap out at me).

I also have not yet tried to see what happens if you go to fetch
metadata that a template doesn't happen to declare.

- Barrie



Re: [OT] TT2 docs

2002-12-20 Thread darren chamberlain
* Barrie Slaymaker <[EMAIL PROTECTED]> [2002-12-20 10:08]:
> On Fri, Dec 20, 2002 at 09:54:50AM -0500, darren chamberlain wrote:
> > 
> > The docs for Template::Provider state:
> > 
> >   fetch($name)
> 
> fetch()ing's the easy part.  Even *I* got that far long ago.

Ah, sorry.

> It's the fact that you can coax metadata out of the template objects
> with AUTOLOADed methods that I didn't see in the docs (not that I've
> seen every bit of the docs, but it didn't leap out at me).
> 
> I also have not yet tried to see what happens if you go to fetch
> metadata that a template doesn't happen to declare.

Ah:

  $ perl -MTemplate::Provider -wle 'printf qq("%s"\n), 
(Template::Provider->new->fetch("foo.tt"))[0]->not_defined'
  Use of uninitialized value in printf at -e line 1.
  ""

With warnings on you get, well, a warning, but then an empty string in
return.

The AUTOLOAD thing is in the docs for Template::Document:

  AUTOLOAD

  An autoload method returns METADATA items.

print $doc->author()

It's probably unclear, however, that Template::Provider::fetch returns a
Template::Document.

(darren)

-- 
It's not that things are getting worse, it's just that news reporting
is getting better.