Re: cpan name spaces (was: Re: Re3: Re: How about class Foo {...} definition for Perl? )
david nicol wrote: Here's a controversial assertion: Just because Damian Conway does something that doesn't make it right. I reccommend changing the name of the module when the interface changes, for instance I published Net::SMTP::Server::Client2 instead of hijacking Net::SMTP::Server::Client and producing incompatible higher-numbred versions. (internally I've got a Client3 as well, but it's not going to get published) In my opinion as soon as he broke compatability with something that people were actually using, he should have changed the name. http://search.cpan.org/~tbone/Parse-RecDescent-FAQ-3.25/FAQ.pm does not contain a section on version compatibility, maybe you could submit one? I am author maintainer of the Parse::RecDescent::FAQ - what happened vis-a-vis version compatibility? I have been far away from the mechanics of Parse::RecDescent for quite awhile. And yes, please email me something that you want put in there.
Re: cpan name spaces (was: Re: Re3: Re: How about class Foo {...} definition for Perl? )
On Wed, Jan 21, 2004 at 03:53:34AM -0500, Terrence Brannon wrote: I am author maintainer of the Parse::RecDescent::FAQ - what happened vis-a-vis version compatibility? I have been far away from the mechanics of Parse::RecDescent for quite awhile. And yes, please email me something that you want put in there. From the Changes file: 1.90Tue Mar 25 01:17:38 2003 - BACKWARDS INCOMPATIBLE CHANGE: The key of an %item entry for a repeated subrule now includes the repetition specifier. For example, in: sentence: subject verb word(s) the various matched items will be stored in $item{'subject'}, $item{'verb'}, and $item{'word(s)'} (i.e. *not* in $item{'word'}, as it would have been in previous versions of the module). (thanks Anthony) F
Re: cpan name spaces (was: Re: Re3: Re: How about class Foo {...} definition for Perl? )
On Tue, Jan 20, 2004 at 11:12:25PM -0600, david nicol wrote: Here's a controversial assertion: Just because Damian Conway does something that doesn't make it right. It certainly doesn't but he's not alone in doing it. Just to come clean, I was never really bitten by the Parse::RecDescent change, it actually hit me very early on in development of my module so I just switched to using 1.9x style without any hassle but it was over 2 years between 1.80 and 1.90 so I could have been and I'd guess a lot of people were bitten. I reccommend changing the name of the module when the interface changes, for instance I published Net::SMTP::Server::Client2 instead of hijacking Net::SMTP::Server::Client and producing incompatible higher-numbred versions. (internally I've got a Client3 as well, but it's not going to get published) In my opinion as soon as he broke compatability with something that people were actually using, he should have changed the name. That's what's necessary in the current scheme but good names are in short supply so you end up with Client2, Client3, Client3_5 etc which is not so nice and especially for things like Net::POP3. Again, the result of gluing 2 strings together without a delimiter. This also makes it hard for say search.cpan.org to make you aware that there is a Client3 when you're looking at the Client2 page. A better (IMHO) alternative is to make the interface part of the version number as important as the name. This is equivalent to including it in the name except you don't lose information like you do when you just glue a number on the end of the name. You also get to use '.'s in the version number because you're not try to make a valid Perl module name. Then CPAN and other tools could understand the relationship between different versions of modules. Unfortunately, this is the bit I think will never happen, I don't think it would be possible to convince people that this is worthwhile, possibly because it's not worthwhile at this late stage. So in the absence of the full solution perhaps we should urge people towards sticking interface version numbers in the names of the modules. I've done it privately too but I'm not convinced that CPAN should be littered with My::Module, My::Module2, My::Module3 etc, F
Re: cpan name spaces
On Tue, Jan 20, 2004 at 10:07:43PM -0500, David Manura wrote: In consideration of what Fergal said, should every public method or function in a module be individually versioned? So, when I do use Text::Balanced qw(extract_multiple extract_codeblock), 1.95; this could (under new semantics) assert only that those two functions have the same interface and expected behavior as the corresponding functions in module version 1.95. If a future version of Text::Balanced (e.g. 1.96) adds or changes the interface/behavior of other functions, my code will still accept the new module. Only when extract_multiple or extract_codeblock themselves change interface/behavior would my code reject a new module version. There is no need for my code to provide an acceptable version range; that is the module's responsibility to deduce. (OO-like modules must be handled by a different mechanism.) It may be worth it in some cases but perhaps if the functions are so unrelated that they can change independently they should not be in the same module. Making Text::Balanced::Multiple::extract() and Text::Balanced::Codeblock::extract() would then allow you version them with the module. There's nothing to stop you still making them available to export from Text::Balanced. Consider further that another author comes out with a module named Text::Balanced::Python having the same interface as Text::Balanced 1.95 but whose extract_quotelike extracts Pythonic quotes rather than Perl-like quotes (i.e. differing behavior). I haven't considered how useful it would be to express this relationship in the versioning metadata, but that might be a further direction. This resembles (OO) interfaces, but I believe the versioning considerations make it different. That is exactly what Java's etc interfaces do. So interfaces are what you want rather than versions however it might be useful to be able to specify the version of the interface. This is getting very far away from anything that might realistically happen... F
Re: [Class::MultiList] Need feedback for first distribution
David Manura wrote: Hi Ruslan, Do you have a link? or some POD documentation? -davidm I realy realy sorry, I've forgot to attach file :( Was tired a lot. Ruslan U. Zakirov wrote: Hello. I'm planning to do my first CPAN upload. I realy need some feedback. Reasons: 1) Module is very simple, but I like it and use it. 2) I didn't find any similar on CPAN. Questions: 1) I'm not native English speaker, so I want some feedback about Name. 2) Code, coding style, and so. 3) Something like: fo..., useless, suxxx is OK, but with reasons :) My ideas: 1) May be it's better to prefix with _ all funcs which have AUTOLOAD magic? There is many such methods in module so it's hard to inherit this object. Best regards. Ruslan. Beforehead thanks. package Class::MultiList; use 5.006; use strict; use warnings; our $VERSION = '0.1'; our $AUTOLOAD; sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; bless ($self, $class); return $self; } sub InitList { my $self = shift; my $args = { Name = undef, @_, }; die 'Where Name?' unless ($args-{'Name'}); $self-{'Lists'}-{ $args-{'Name'} } = { List = [], Current = undef, Counter = 0, Hash = {}, }; } sub AUTOLOAD { my $self = shift; my @avail = qw(Count First Current Last Next Prev Push Pop Unshift Shift); my $re = join('|', @avail); my ($func, $list) = ($AUTOLOAD =~ /^.*::($re)(.*)$/); unless ( $func ) { my ($package, $filename, $line) = caller; die $AUTOLOAD Unimplemented in $package. ($filename line $line) \n; } unless ( $self-Exists( List = $list ) ) { die List $list not exists; } no strict qw(refs); *{$AUTOLOAD} = sub { return ( $self-$func( List = $list, @_ ) ) }; return ( $self-$func( List = $list, @_ ) ); } sub Exists { my $self = shift; my $args = { List = undef, @_, }; return exists($self-{'Lists'}-{ $args-{'List'} }); } sub Names { my $self = shift; return keys %{$self-{'Lists'}}; } sub First { my $self = shift; my $args = { List = undef, @_, }; my $list = $self-__GetList( $args-{'List'} ); $list-{'Current'} = 0; return $self-Current( List = $args-{'List'} ); } sub Current { my $self = shift; my $args = { List = undef, @_, }; my $list = $self-__GetList( $args-{'List'} ); return $list-{'List'}-{ $list-{'Current'} }; } sub Last { my $self = shift; my $args = { List = undef, @_, }; my $list = $self-__GetList( $args-{'List'} ); $list-{'Current'} = $self-Count( List = $args-{'List'} ); return $self-Current( List = $args-{'List'} ); } sub Next { my $self = shift; my $args = { List = undef, @_, }; my $list = $self-__GetList( $args-{'List'} ); $list-{'Counter'}++; return $self-Current( List = $args-{'List'} ) if( $list-{'Counter'} $self-Count( List = $args-{'List'} ) ); $list-{'Counter'} = undef; return undef; } sub Prev { my $self = shift; my $args = { List = undef, @_, }; my $list = $self-__GetList( $args-{'List'} ); $list-{'Counter'}--; return $self-Current( List = $args-{'List'} ) if( $list-{'Counter'} = 0 ); $list-{'Counter'} = undef; return undef; } sub Count { my $self = shift; my $args = { List = undef, @_, }; my $list = $self-__GetList( $args-{'List'} ); return $list-{'Counter'}; } sub Push { my $self = shift; my $args = { List = undef, Element = undef, Mark = undef, @_, }; my $list = $self-__GetList( $args-{'List'} ); if ( $args-{'Mark'} ) { die 'Mark already exists' if (exists $list-{'Hash'}-{ $args-{'Mark'} }); $list-{'Hash'}-{ $args-{'Mark'} } = $list-{'Counter'}; } $list-{'Counter'}++; push( @{$list-{'List'}}, $args-{'Element'} ); return; } sub Pop { my $self = shift; my $args = { List = undef, @_, }; my $list = $self-__GetList( $args-{'List'} ); $list-{'Counter'}--; $list-{'Current'} = undef if ( $list-{'Current'} == $list-{'Counter'} ); # check for mark,
Re: [Class::MultiList] Need feedback for first distribution
Hello Ruslan, On Wed, Jan 21, 2004 at 01:59:35PM +0300, Ruslan U. Zakirov wrote: I realy realy sorry, I've forgot to attach file :( Was tired a lot. A quick glance over prompts my first question: what does this module provide that other Class generating modules, e.g., Class::MethodMaker, Class::MakeMethods, Class::Struct, do not? Mx.
Re: [Class::MultiList] Need feedback for first distribution
* david nicol [EMAIL PROTECTED] [2004-01-21 13:09]: I think its that as soon as you have AUTOLOAD defined, you can't inherit past that point, at least by the ISA mechanism: the AUTOLOAD function needs to know how to punt. You think wrong. $ perl -l package Foo; sub bar { print oy vey }; package Baz; our @ISA = qw(Foo); sub AUTOLOAD {}; package main; Baz-bar; ^D oy vey Cf. perldoc perlobj: If neither the current class, its named base classes, nor the UNIVERSAL class contains the requested method, these three places are searched all over again, this time looking for a method named AUTOLOAD(). -- Regards, Aristotle If you can't laugh at yourself, you don't take life seriously enough.
Mail::Outlook module
Dear Module Authors, Since my initial attempts to send mail via Outlook attached to an Exchange server, I now have a set of modules that can also read email items within Outlook Mail Folders. Initially this was a module for me to learn how to use the Win32 API via Perl, but wondered whether it might be useful to anyone else. My current name for the distribution is Mail-Outlook which includes the following modules: Mail::Outlook Mail::Outlook::Folder Mail::Outlook::Message Each uses an OO style interface, with the Mail::Outlook module being the master module that should be used to interface to the outside world. I have two questions 1) Is this a distribution worth uploading to CPAN? 2) Is Mail::Outlook a suitable namespace or are there better suggestions? The current version is available online [1] if anyone wanted to have a look at it. [1] http://birmingham.pm.org/modules/Mail-Outlook-0.05.tar.gz From studying the MailBox distribution, there are distributions on CPAN, which read the Outlook Express folders and email messages (although badly named IMO as they only refer to the DBX extension, but that's another story), but nothing that refers to Outlook specifically. All thoughts and suggestions gratefully accepted. Additional: DESCRIPTION This module was written to overcome the problem of sending mail messages, where Microsoft (R) Outlook (R) is the only mail application available. However, since it's inception the module has expanded to handle a range of Outlook mail functionality. Note that when sending messages, the module uses the named owner of the Outbox MAPI Folder in order to access the correct objects. Thus the From field of a new message is predetermined, and a read only property. If using the 'Win32::OLE::Const' constants, only the following are currently supported: olFolderInbox olFolderOutbox olFolderSentMail DEPENDENCIES The distribution requires the following modules: Win32::OLE Win32::OLE::Const For testing purposes, the following modules are desirable, but not essential: Test::Pod Barbie. -- Barbie (@missbarbell.co.uk) | Birmingham Perl Mongers | http://birmingham.pm.org/
Re: Mail::Outlook module
On Wed, Jan 21, 2004 at 02:00:40PM +, Barbie wrote: 1) Is this a distribution worth uploading to CPAN? Yes! There was a recent discussion at chicago.pm [1] at how to get at .pst files. 2) Is Mail::Outlook a suitable namespace or are there better suggestions? In an ideal world your module would plug into the Mail::Box framework somehow. But I'm not even sure if Outlook Express files are functionally different from vanilla Outlook files...exit stage left. //Ed [1] http://mail.pm.org/pipermail/chicago-talk/2004-January/000800.html
optional mix-in classes
Hi, I'm trying to find a good way for CAD::Drawing::IO to determine which of the CAD::Drawing::IO::* modules are available. Basically, there are save() and load() functions in IO.pm which allow an explicit type argument or the type can be derived from the file extension. Right now, the beginning of IO.pm is just like: use CAD::Drawing::IO::OpenDWG; use CAD::Drawing::IO::PostScript; use CAD::Drawing::IO::Image; use CAD::Drawing::IO::PgDB; use CAD::Drawing::IO::Circ; use CAD::Drawing::IO::Tk; our @ISA = qw( CAD::Drawing::IO::OpenDWG CAD::Drawing::IO::PostScript CAD::Drawing::IO::Image CAD::Drawing::IO::PgDB CAD::Drawing::IO::Circ CAD::Drawing::IO::Tk ); The main reason that I'd like to be able to make each module optional is that OpenDWG.pm requires DWGI.pm (a wrapper on a non-gpl toolkit which I cannot distribute with the module.) From looking at DBI.pm, available drivers are found by searching @INC when connect() is called, and then there is a require(). I don't think that I need anything as elaborate as the DBI setup, and I'd rather that this just be done at compile time (plus, searching @INC seems like a waste and IO.pm needs to know which save_type() and load_type() functions are available in order to redirect to the backend.) Since adding a backend requires changes to IO.pm, maybe it should just be part of the IO.pm install procedure? Or, I could look at entirely removing the type determination and redirects from IO.pm (allowing the backends to install themselves (and then looping through the list of available backends, calling an is_this_yours() function to allow the implicit filetype determination to come from the backend itself?)), but I'm not sure how to go about that. Currently, the redirection is like so: # (stripped from the middle of a longer function in IO.pm) elsif($type eq postscript) { ($action eq save) return($self-saveps($filename, $opt)); ($action eq load) return($self-loadps($filename, $opt)); } elsif($type eq image) { ($action eq save) return($self-saveimg($filename, $opt) ); ($action eq load) return($self-loadimg($filename, $opt) ); } Thanks in advance for any suggestions, Eric
Re: optional mix-in classes
* Eric Wilhelm ewilhelm at sbcglobal.net [2004/01/21 10:58]: I'm trying to find a good way for CAD::Drawing::IO to determine which of the CAD::Drawing::IO::* modules are available. It sounds like what you want is to ensure that the IO::* modules are polymorphic, i.e., to ensure that they all adhere to the same interface. Then, like DBI, CAD::Drawing::IO can be setup to load the appropriate implementation module (no groveling in @INC, that's what require is for), instantiate an object, and then call the appropriate methods on that object. Your main module (IO.pm) shouldn't need to have knowledge of the various IO::* modules. Basically, there are save() and load() functions in IO.pm which allow an explicit type argument or the type can be derived from the file extension. At this point, IO.pm should load the right module with require, and then defer to that module's methods to do the right thing for loading, saving, etc. (darren) -- You win again, gravity! pgp0.pgp Description: PGP signature
Re: [Class::MultiList] Need feedback for first distribution
Martyn J. Pearce wrote: Hello Ruslan, On Wed, Jan 21, 2004 at 01:59:35PM +0300, Ruslan U. Zakirov wrote: I realy realy sorry, I've forgot to attach file :( Was tired a lot. A quick glance over prompts my first question: what does this module provide that other Class generating modules, e.g., Class::MethodMaker, Class::MakeMethods, Class::Struct, do not? Hello, Martyn and other. Class::Struct - it's implementation of C like structs. Looking in source said that it does not like ISA at all so It's not OO style at all. This module has no similarity in reasons and goals of mine. I didn't find any similarity with Class::MakeMethods. I'm going in another direction. This module(distro) do much on get/set methods, but not iteration. I want next features: 1) Ordered container of any elements in its cells. 2) Support for many such containers in one object. For example: One Object have next lists: Attribute Child Sibling 3) It's something like 'bidirect lists' in C which have next and prev refs, but I want abstract this class from data in cells. Very good schem: my $attr = $Obj-Attrs-First; while ($attr-Next) { ... } but force me to change Attr object, so I've decided to use next style: $Obj-InitList(Name = 'Attr'); $Obj-PushAttr($attr); #or $Obj-Push(List = 'Attr', Element = $attr); while (my $attr = $Obj-NextAttr) { ... } 4) Main goal is walking through list(Next, Prev, First, Last) 5) I don't want to give public interface to internal array what other modules do. 6) I consider using of this module in OO enviorment so support for join method is something not normal. I hope I've spot more light with this letter. Best regards. Ruslan. Mx.
Re: optional mix-in (er, add-on?) classes
The following was supposedly scribed by darren chamberlain on Wednesday 21 January 2004 11:10 am: * Eric Wilhelm ewilhelm at sbcglobal.net [2004/01/21 10:58]: I'm trying to find a good way for CAD::Drawing::IO to determine which of the CAD::Drawing::IO::* modules are available. It sounds like what you want is to ensure that the IO::* modules are polymorphic, i.e., to ensure that they all adhere to the same interface. Then, like DBI, CAD::Drawing::IO can be setup to load the appropriate implementation module (no groveling in @INC, that's what require is for), instantiate an object, and then call the appropriate methods on that object. Not exactly. The IO::* modules do not have constructors, and neither does IO.pm. Maybe mix-in is the wrong term. Essentially, every function is inherited *by* CAD::Drawing, so basically, the entire set could simply be in the one package and file, but that's a lot of code. Maybe they are more like add-on packages? Functions from IO::* modules are inherited by IO.pm, which is inherited by Drawing.pm, so with $d = CAD::Drawing-new(), you could call $d-loaddwg(file.dwg, {}), but you are better served by $d-load(file.dwg) where load() is actually CAD::Drawing::IO::load() and loaddwg() is CAD::Drawing::IO::OpenDWG::loaddwg(). Your main module (IO.pm) shouldn't need to have knowledge of the various IO::* modules. It needs to have some knowledge of them in order to redirect the action request to handle the particular filetype. Also, I'm not sure that polymorphism is really helpful, since IO.pm is always getting a CAD::Drawing object, it is not like it has a classed object which will automagically go to the correct class for its method. With the current setup, the IO::* modules are really just packages. So, I guess my challenge is how to make IO.pm find all of the qualified, installed packages under CAD/Drawing/IO/*.pm and call the correct function based on either the explicit filetype or extension which is received by load() or save(). I can see doing this with a foreach() / require() / eval() setup or something where the packages contain code refs, but that doesn't seem very tidy, and with some Inline-based modules as backends, I'd like to have as much as possible happen at compile time so that C/XS errors are thrown at the beginning instead of the end. The primary goal is to make the module more easily installable (and possibly to eventually pass the CPAN autotesting), but I'd also like other authors to be able to write IO::* modules (for other geometric formats) without having to add code to IO.pm or make changes to your main program (e.g. if you give the program an argument of output.igs and an iges format save function is available, it just does the right thing.) With this model, your main program would not have use CAD::Drawing::IO::IGES;, but simply use CAD::Drawing;. Thanks, Eric
Re: optional mix-in (er, add-on?) classes
The following was supposedly scribed by darren chamberlain on Wednesday 21 January 2004 02:11 pm: * Eric Wilhelm ewilhelm at sbcglobal.net [2004/01/21 13:58]: The following was supposedly scribed by darren chamberlain on Wednesday 21 January 2004 11:10 am: Supposedly? Aren't you verifying signatures? ;) :) no. That's just the quote line. Well, if polymorphism isn't what you're after, couldn't you have CAD::Drawing::IO not define a load function, and have CAD::Drawing::IO::Foo declare package CAD::Drawing::IO and define a load? Then, the user need simply do: use CAD::Drawing::IO; use CAD::Drawing::IO::OpenDWG; And calling load(file.dwg) uses the load defined in ::IO::OpenDWG? But this still (essentially) leaves the user with hard-coded information about which format to use (making it impossible to do $drw-save($ARGV[0])). My goal is to make all of the file formats (smile floormats) transparent in the hope that they eventually go away completely. Without any knowledge of the implementation, however, it sounds like what you want is polymorphism, and you're jumping through hoops to avoid using it. What I'm going for is more like a big chain of elsif statements which is put together out of multiple packages. If the user were to use an object from CAD::Drawing::IO::OpenDWG to call the save() function, that would be polymorphism. I might be jumping through hoops at the module level, but I think this beats making the user jump through hoops at the main program level. With this model, your main program would not have use CAD::Drawing::IO::IGES;, but simply use CAD::Drawing;. This sounds exactly like DBI, which relies on DBDs being polymorphic. *shrug* It is a lot like the DBI::connect() function, but load() does not return an object the way connect() does. With DBI, once it gets hold of a classed object, DBI.pm is home-free and just has to return the blessed object, leaving the rest up to the polymorphism. My problem is more like DBI turned on its head. e.g. with DBI::connect() you are at the beginning of the program and you get your $dbh which is used to call $dbh-select*foo() and such. With CAD::Drawing, you get your $drw from CAD::Drawing-new() and from there you can either $drw-load(whatever.dwg|dxf) or just start adding entities to the drawing. At the end (or the middle, or as many times as you like), you can $drw-save(file.ps) $drw-save(file.dxf) $drw-save(file.png, {size = [640,480]}) $drw-save(file.dwg.gz). Now, if I could just get the string file.ps to kindly bless itself into CAD::Drawing::IO::PostScript, I'd be in the free and clear;-) Seriously, though. That is basically what I'm trying to do. Consider this, where you have a list of available packages in @found, and a $filename with (or without) an extension, and possibly an explicit $options{type} argument, and the following is part of the IO::save() function: foreach my $package (@found) { if(eval(\$ . $package . ::can_save(\$filename, \$options{type})) ) { my $r = eval($package . ::save_file(\$self, \$filename, \\%options)) return($r); } } Thanks, Eric -- A counterintuitive sansevieria trifasciata was once literalized guiltily. --Product of Artificial Intelligence
VERSION as (interface,revision) pair and CPAN++
On Wed, 2004-01-21 at 04:32, Fergal Daly wrote: A better (IMHO) alternative is to make the interface part of the version number as important as the name. This is equivalent to including it in the name except you don't lose information like you do when you just glue a number on the end of the name. You also get to use '.'s in the version number because you're not try to make a valid Perl module name. Then CPAN and other tools could understand the relationship between different versions of modules. Unfortunately, this is the bit I think will never happen, I don't think it would be possible to convince people that this is worthwhile, possibly because it's not worthwhile at this late stage. Q: was this suggestion made as a perl6 RFC and if so what did Larry think of it? Q2: Do you have a really clear idea of what split interface/version numbers would look like and is it light enough to try to get it into perl 5.9.1 ? (act fast) Could the situation be resolved by insisting that the first number in a version string is the interface version and following numbers are revision numbers, and rewriting Crequire in some way to respect that? For instance, require could take a regex as an argument and when that happens the version would have to match the regex, and the same with the VERSION element of Cuse If the VERSION argument is present between Module and LIST, then the use will call the VERSION method in class Module with the given version as an argument. The default VERSION method, inher ited from the UNIVERSAL class, croaks if the given version is larger than the value of the variable $Module::VERSION. So we're talking about altering the default VERSION method to recognize something other than a version string, that would trigger a different case. Such as, the major number has to match and the minor number has to be greater, or a PROVIDES method which defaults to the @{$Module::PROVIDES} array must include a version number with the same major number as the one we want. Or maybe we're talking about enlightened new versions of modules that present old interfaces when provided an old version number. This is IMO a non-starter, since it would require a lot of work that will not seem necessary by the people who would have to do the work. Or maybe we're talking about adding a bureaucratic layer to CPAN so it won't accept new versions of modules under the same name unless the new version passes the test suite from the old version, for modules on a restricted list that your module gets on once enough people rely on your module. The last suggestion would enforce interface compatability, after a fashion. It would need to be documented thoroughly so people don't go including test cases for things known to be broken in their modules, that verify that the modules are broken in the way that the modules are broken. (I have done this; DirDB 0.0.6 fails tests in DirDB 0.0.5 concerning croaking on unhandleable data types) Compliance might be part of a standard quality check before a module with a positive major version number is accepted; or CPAN might enforce quality ratings, which would be enforced to be nondecreasing, for a given module name. So if a module has a PRODUCTION quality rating, that means that the interface is guaranteed to remain stable into the future, under the given name. And that tests for things that are broken and you mean to fix in the future would have to be marked as such in the tests.pl. :) David Nicol -- david nicol In order to understand what another person is saying, you must assume that it is true and try to imagine what it could be true of. (George Miller; 1980.)