I’m developing a web app right now and as some of you might know there is a very popular javascript command which is used for debugging. It’s invoked via console.log() and takes anything as an argument. If you use Firefox with Firebug or Safari 4 the value you pass to console.log will be printed and introspected via a debug window.
The problem is that I forget on a regular basis to remove those console.log statements. I wrote a very quick and dirty perl script which searches files with an .js extension for console.log. I put that script in the t/ folder and named it forbidden_words.t.
It works great so far and my test suite fails as soon as there is still a console.log around. I plan to extend this script to something like Test::ForbiddenWords or something where you can specify file extensions and strings (or regexes).
I couldn’t find anything like this on the CPAN, so give me a comment if you like the idea or know something better.
This script is in use for many years now and does a decent job. I set up a MySQL table which specifies the basic configuration options for an Apache2 virtual host:
CREATE TABLE `vhosts` ( `id` int(11) NOT NULL AUTO_INCREMENT, `DocumentRoot` text NOT NULL, `ServerName` varchar(255) NOT NULL DEFAULT '', `ServerAlias` varchar(255) NOT NULL DEFAULT '', `ScriptAlias` text NOT NULL, `CustomLog` text NOT NULL, PRIMARY KEY (`id`) ) ;
In /etc/apache2/conf.d/vhosts.perl.conf I’ve put this little script:
<Perl> use DBI; use File::Path; my $dbh = DBI->connect( "DBI:mysql:vhosts", "apache", "password" ); my $sth = $dbh->prepare("select * from vhosts"); $sth->execute(); while ( my $vhost = $sth->fetchrow_hashref() ) { unless ( -d ( split( / /, $vhost->{ScriptAlias} ) )[1] ) { mkpath( ( split( / /, $vhost->{ScriptAlias} ) )[1] ); } unless ( -d $vhost->{DocumentRoot} ) { mkpath( $vhost->{DocumentRoot} ); } unless ( -e ( split( / /, $vhost->{CustomLog} ) )[0] ) { my $dir = $vhost->{CustomLog}; $dir =~ s/(.*)\/.*?$/$1/; mkpath($dir); } undef( $vhost->{id} ); push( @VirtualHost, { "*" => $vhost } ); } $sth->finish(); $dbh->disconnect; </Perl>
As you can see the script uses all columns from the table to set up the virtual host. You can easily add more columns (e.g. ErrorLog) to the table as long as their name is known to the apache2 configuration.
After you added a new vhost to the database you need to restart the apache server so that the perl script is run and the vhost is set up.
This will create the directories of any vhost which has been added to the table “vhosts” and create a virtual host inside of apache. This is very handy if you need to maintain multiple sites which share the same layout and need basic functionality.
I managed to pack my work on code completion to a distribution and released it to the CPAN. The source code is managed on github. Use it, watch it, fork it, break it, fix it, patch it … you know the game!
Watch the demo for new features (no audio).
So what is missing?
It would be great if code completion works for chained method calls:
# DateTime my $dt = DateTime->new(year => 2009); $dt->add( months => 1)->_ # we are still a DateTime object! # in a DBIC environment my $rs = $schema->resultset('Foo')->_ # we get a MySchema::ResultSet::Foo
What we need is a way to define the return values of methods. Moose’s type system is not much of a help because you cannot introspect what class is behind a custom type.
I was thinking about an IntelliPerl profile which is stored in the home directory as well as in each project. Those two are merged and define method signatures and variable types.
Possible syntax:
# ~/.intelliperl $schema isa DBIx::Class::Schema; $rs isa DBIx::Class::ResultSet; $dt isa DateTime; method DBIx::Class::ResultSet::search returns DBIx::Class::ResultSet # ~/workspace/MyApp/.intelliperl $schema isa MyApp::Model::DBIC $rs isa MyApp::Schema::ResultSet
A different approach would be to add the method signature to the POD:
=head2 mymethod
=for intelliperl
method mymethod ($dt DateTime) returns DateTimeThis will make $dt inside mymethod a DateTime variable and the returned value is a DateTime object as well.
For now only TextMate is supported. See the documentation for details.
I’d love to see more Devel::IntelliPerl::Editor::s!
It is pretty hard to write a code completion script for perl since it’s hard to find out of which type a variable is. I use simple regexes and a comment to achieve this. Here is how it looks like:
It consists of a perl script which reads the current file from STDIN and gets the current line number as well as the cursor position in that line. The script is run if the cursor is behind a -> and the string before that looks like a class or a variable. If it’s a variable the script travels the lines up until it finds something like $var = Class->new, $var = new Class or # $var isa Class.
Class::MOP::Class loads that class and retrieves all method names. It also evaluates the prefix which has been entered behind the -> and displays those methods only which have the same prefix. Private methods are moved to the buttom as well as capitalized method names.
The second part of this script is embedded in TextMate. TextMate allows to define custom commands. You can define how the data shoud be rendered depending on the return value of the script. In this case STDERR is printed directly into the editor (if there is only one method left) and STDOUT is printed as tool tip.
I’d like to hear what you think about it and whether there are better / other approaches. I should probably use PPI instead of regexes to parse the document. This is one of the reasons why I did not release any code yet. Another shortcoming is speed. Especially for large classes like Catalyst or DateTime it takes a noticeable time until you get the results.
So long… I’ll keep you posted.
TextMate has a number of great bundles which help you develop in many many languages. The feature I miss most is formatting for JavaScript just like Perl Tidy does it for Perl.
I recently saw JavaScript::Beautifier in the “Recent” list on the CPAN and was wondering if I can bind it to TextMate. This is how I did it:
First of all, install it:
sudo cpan -i JavaScript::Beautifier
Next, add the command to TextMate. Open the Bundle Editor (Bundles/Bundle Editor/Show Bundle Editor).

Then select JavaScript and click on “New Command” (bottom left).
Name it “Beautifier” (or whatever you want) and paste the following in the command text field:
require_cmd js_beautify.pl 'sudo cpan -i JavaScript::Beautifier' js_beautify.pl $TM_FILEPATH
Set the rest of the options like this:
Now you are ready to go! Open up a JavaScript document and hit Shift+Crtl+H and the current document should be formatted.
