Yo Dawg, I Heard You Liked Perl…
If you're going to eval that string...
If you're going to eval a string, you might as well at least sort of try to get it right. On one hand, I try very hard to never have to use
eval STRING, but on the other hand, quite a lot of the code that I release to the CPAN has been generated by a computer program, which means I still have the problem of trying to build valid Perl in a program and then run it without giving it a close inspection. (Why is it computer-generated? It's because Dist:Zilla is mucking with the source to add things like
$VERSION to it.)
Because I was going to be screwing around with hunks of Perl, I wanted a way to try and limit the mistakes I could make to the particularly weird, and not to the kind of stupid mistakes I make eighty-six times already every day. For example, did you notice that I forgot one of the colons in Dist::Zilla, above? I do that all the time, and never learn.
Or, well, maybe I do learn, because I ended up doing what I always do when I realize that I'm not going to get any better. I wrote more code to keep me in check. Type check. (Sorry.)
I use Moose all the time, and it has a set of interfaces for type constraints. Type constraints are, at the heart of it, just little data validators that look at a scalar and say "yes, it's what you asked for" or "no, it's no good." They're most often seen constraining the values that can be stored in an instance attribute, but you can use them all over the place. I use them for validating user input and subroutine parameters, for example, and I usually use the ones whose names start with MooseX::Types.
For example, I could do this:
Yeah, it's contrived, but you get the picture. You can use these things everywhere – and since they're the only things you can use for attribute validation, get used to using them. The more type constraints you have in your arsenal, the more effectively you can validate your data and catch errors before they manifest in mysterious ways, much later.
To help prevent my constant stupid errors in package names (and some other things) I wrote MooseX::Types::Perl. It's just a set of type constraints that require datas fall into fairly strict interpretations of common Perl langauge tokens.
For example, Dist::Zilla is going to build a dist, it makes sure that its name is a
DistName and that all the modules in it are
ModuleNames. These are pretty similar – the latter expects dashes and the former double-colons. It's worth noting, too, that neither of them allows the use of apostrophes for package separators. In fact, there are a lot of things that Perl will accept that these types will not. Here's a quote from a recent bug report to Perl.
This is apparently a valid package name: _#@*$!@*^( But this is not: #@*$!@*^(_
Yeah, I'm just not going to support that. Neither, too, are packages like
Rÿche::Queen supported, with that pesky umlaut. These work, in many places, but they're not really reliable, and you end up getting into lots of twitchy differences with things like normalization and filesystem encoding, and … well, it's just not going to make your life a lot of fun, unless you're into that sort of thing. Me? I just want my code to work, so I make my types very, very strict.
This means that MooseX::Types::Perl's Identifier type limits you to the kind of variable names that will not get you glowered at by your co-workers: alpha-or-underscore followed by zero or more alnum-or-underscore – and that's all ASCII, people!
There are also types for version strings, and I am a big fan of requiring things conform to StrictVersionStr. Keep your weird version strings to yourself! It is only because I want to avoid funny looks that I don't just make all my versions conform to
MooseX::Types::Perl is really simple in concept, and also in its implementation. It shows how easy it is to build your own type libraries for dealing with all your favored value domains and catching your screw-ups early with a nice, perfectly clear 47-line stack trace.