|Think about Loose Coupling|
A First CPAN Odysseyby skyknight (Hermit)
|on Jun 22, 2004 at 15:16 UTC||Need Help??|
At my job I'm currently working on a module for abstracting objects and relationships in a SQL database through (what I deem to be) a very clean and generalized object oriented interface. It is (I hope) DBD agnostic, using the DBI and allowing you to plug any DBD into it fairly seamlessly by sub-classing a configuration parser file. At present, I'm pretty happy with the organization of the class hierarchy and interactions, have written fairly extensive implementation and API documentation, and have automated tests that employ the Test::Unit framework. I have gotten the go ahead from my boss to release the module to CPAN, and will probably do so in a few weeks. However, I am presently concerned about "when in Rome" issues, and want very much for my module to conform to the various CPAN mores.
The first thing I'm wondering about is name spacing issues. Presently, the top level package name is just SQL, so I have sub-packages such as SQL::Object, SQL::Link, SQL::Statement, SQL::Object::ResultSet, SQL::Link::ResultSet, etc. I realize that this probably isn't a very good name, but I'm not sure what would be. The SQL name is nice and short, but not really all that descriptive of what the module is doing. What would be a sensible name? What is the best mechanism via which to receive feedback on naming issues?
With regards to testing, I'm fairly confident in my ability to write good tests as far as verifying correctness is concerned, but I also want to adhere to the various conventions for CPAN modules. Right now I am using the Test::Unit framework, and for each class Foo::Bar, I have a corresponding Foo::BarTest which is a subclass of Test::Unit::TestCase. Furthermore, in each directory of the library hierarchy, I have a package of the form Foo::TestSuite which subclasses Test::Unit::TestSuite, and contains all of the tests in that directory, as well as any TestSuite subclasses in subdirectories. This gives me a nice tree structure of tests that allows me both to focus on particular pieces of the code tree, while also allowing me to run all of the tests from a single command line invocation of the top level TestSuite. I realize, however, that while this is great for me, it might not meet the expectations of others... How much leeway do I have in this regard? Is it bad that I'm using Test::Unit which does not come standard with Perl? Should I be using something more mainstream and better maintained like Test::More? If I do that, am I going to have to scrap my current testing framework layout?
On a related thread, I'm curious as to how I should go about testing interactions with a database on a user's system. It would be very rude for the testing procedures of my module to carelessly stomp on the database residing on someone's machine. What precautions and paradigms should I employ so as both to thoroughly test my module on the user's machine and avoid trashing his environment? I've read horror stories of hapless users having rogue modules smash their data, and I'd rather not be a purveyor of such a module.
With regards to writing documentation, what are some tips for adhering to the Principle of Least Astonishment? I'm trying my best to avoid making the kind of assumptions that developers tend to make about code that they have written that results in unreadable documentation. I'll just have to hope that the world agrees. There are, however, more general issues... Is there any kind of de facto standard for laying out documentation? I've been writing POD in each library file, at the end, not mixing POD and code because I think that makes for hideously ugly source files. I have been using the following four sections: Name, Description, Synopsis, Methods. I guess I should probably also add "Bugs" and "See Also" sections. What kind of things should I be keeping in mind?
I'm also wondering about error handling, specifically how to trap and report errors to the user. I personally find it loathsome to have a script die deep within the bowels of DBI, leaving me with no indication as to what the root cause was. As such, I'm trying to trap third party errors by wrapping code in eval blocks, and handling errors when $@ is populated, re-throwing the exception with some contextual information of my own added to it. I'm not sure, though, how to fully go about this... Should I try to re-trap my own exceptions at successive levels, or would it be better just to call Carp::confess at the lowest level where I expect potential problems? Also, while I realize that for some errors it makes more sense to cite the error from the perspective of the caller, e.g. in the case of invalid subroutine parameters, I'm left with the the quandary of Carp::croak versus Carp::confess. It seems to me that confess is always more useful, with the only drawback being that it dumps a ton of information to the screen. It seems to me that the utility of croak quickly becomes diminished when invocation of the croaking method becomes deeply nested in library routines. What are good criteria for choosing one over the other, or should I always favor the stack trace of confess?
I'm sure there are many other important considerations when publishing a CPAN module, so please feel free to chime in about the things that I may have forgotten.