[RFC] Module::Husbandry
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
* 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
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
* 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
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
* 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
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
* 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
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
* 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.