Sunday, July 4, 2010

new required reading for programmers

starting now I'm putting this paper on my required reading list for all programmers I hire:

http://www.eecs.harvard.edu/~mdw/papers/seda-sosp01.pdf

thanks to the authors (obviously) and the guys at http://everythingsysadmin.com/ for pointing it out.

Saturday, July 3, 2010

dfs == stack overflow

As usual, xkcd nails it!

Watch out for those nasty, down-the-rabbit-hole depth first searches, they can be real productivity drains.

Wednesday, April 14, 2010

doctests for perl

There are several pod testing modules available on cpan.  However, none of them quite meets my needs.

For example, there is Test::Pod::Snippets which is really close, but considers all verbatim sections to be code (by default).

There is also Test::Inline, which allows tests to be in pod sections alongside code as well, but requires explicit testing of results.

Lastly, I found Test::Snippet, which does have a REPL loop but still wasn't quite as lightweight as I wanted.

All of the modules above required some additional non-core dependencies too, which I find irksome.  So, below is my crack at it.

package Test::Doctest;

use 5.005;
use strict;

require Exporter;
require Pod::Parser;
use vars qw(@ISA @EXPORT $VERSION);
@ISA = qw(Exporter Pod::Parser);
@EXPORT = qw(runtests);
$VERSION = '0.01';

use Carp;
use Test::Builder;
use File::Spec::Functions qw(devnull);

=head1 NAME

Test::Doctest - extract and evaluate tests from pod fragments

=head1 SYNOPSIS

  perl -MTest::Doctest -e 'runtests @ARGV' lib/Some/Module.pm

  - or -

  use Test::Doctest;
  runtests($filepath);

  - or -

  use Test::Doctest;
  my $p = Test::Doctest->new;
  $p->parse_from_filehandle(\*STDIN);
  $p->test;

=head1 DESCRIPTION

B<runtests> uses B<Pod::Parser> to extract pod text from the files
specified, evaluates each line begining with a prompt ($ by default),
and finally compares the results with the expected output using
B<is_eq> from B<Test::Builder>.

=head1 EXAMPLES

  $ 1 + 1
  2

  $ my @a = qw(2 3 4)
  3

  $ use Pod::Parser;
  $ my $p = Pod::Parser->new;
  $ ref $p;
  Pod::Parser

=head1 EXPORTS

=head2 B<runtests()>

Extract and run tests from pod for each file argument.

=begin runtests

  $ use Test::Doctest
  $ runtests
  0

=end

=cut

sub runtests {
  my ($total, $success, @tests) = (0, 0);
  my $test = Test::Builder->new;

  for (@_) {
    my $t = Test::Doctest->new;
    $t->parse_from_file($_, devnull);
    $total += @{$t->{tests}};
    push @tests, $t;
  }

  if (!$test->has_plan) {
    $test->plan(tests => $total);
  }

  for (@tests) {
    $success += $_->test == @{$_->{tests}}
  }

  return $success;
}

=head1 METHODS

=head2 B<initialize()>

Initialize this B<Test::Doctest> pod parser. This method is
not typically called directly, but rather, is called by
B<Pod::Parser::new> when creating a new parser.

=begin initialize

  $ use Test::Doctest
  $ my $t = Test::Doctest->new
  $ @{$t->{tests}}
  0

=end

=begin custom prompt

  $ use Test::Doctest
  $ my $t = Test::Doctest->new(prompt => 'abc')
  $ $t->{prompt}
  abc

=end

=cut

sub initialize {
  my ($self) = @_;
  $self->SUPER::initialize;
  $self->{tests} = [];
}

=head2 B<command()>

Override B<Pod::Parser::command> to save the name of the
current section which is used to name the tests.

=begin command

  $ use Test::Doctest
  $ my $t = Test::Doctest->new
  $ $t->command('head1', "EXAMPLES\nthese are examples", 1)
  $ $t->{name}
  EXAMPLES

=end

=cut

sub command {
  my ($self, $cmd, $par, $line) = @_;
  $self->{name} = (split /(?:\r|\n|\r\n)/, $par, 2)[0];
}

=head2 B<textblock()>

Override B<Pod::Parser::textblock> to ignore normal blocks of pod text.

=begin textblock

  $ use Test::Doctest
  $ my $t = Test::Doctest->new
  $ not defined $t->textblock
  1

=end

=cut

sub textblock { }

=head2 B<verbatim()>

Override B<Pod::Parser::verbatim> to search verbatim paragraphs for
doctest code blocks.  Each block found, along with information about
its location in the file and its expected output is appended to the
list of tests to be executed.

=begin verbatim

  $ use Test::Doctest
  $ my $t = Test::Doctest->new
  $ $t->verbatim("  \$ 1+1\n  2", 1)
  1

=end

=begin verbatim no prompt

  $ use Test::Doctest
  $ my $t = Test::Doctest->new
  $ $t->verbatim("abc", 1)
  0

=end

=begin verbatim custom prompt

  $ use Test::Doctest
  $ my $t = Test::Doctest->new(prompt => '#\s+')
  $ $t->verbatim("  # 1+1\n  2", 1)
  1

=end

=cut

sub verbatim {
  my ($self, $par, $line) = @_;
  my $prompt = $self->{prompt} ? $self->{prompt} : '\$\s+';
  my $name = $self->{name} ? $self->{name} : q{};
  my @lines = split /(?:\r|\n|\r\n)/, $par;
  my @code;

  for (@lines) {
    if (/^\s+$prompt(.+)/) {
      # capture code
      push @code, $1;
    } elsif (/^\s+(.+)/ and @code) {
      # on first non-code line, with valid code accumlated
      my $file = $self->input_file ? $self->input_file : 'stdin';
      push @{$self->{tests}}, [$name, $file, $line, $1, @code];
      @code = ();
    } elsif (/^=cut/) {
      # stop processing on =cut (even without a leading blank line)
      last;
    }
  }

  return @{$self->{tests}};
}

=head2 B<test()>

Evaluates each test discovered via parsing and compares the results
with the expected output using B<Test::Builder::is_eq>.

=begin test empty

  $ use Test::Doctest
  $ my $t = Test::Doctest->new
  $ $t->test
  0

=end

=begin test non-empty

  $ use Test::Doctest
  $ my $t = Test::Doctest->new
  $ $t->command('begin', 'test', 1)
  $ $t->verbatim("  \$ 1+1\n  2", 2)
  $ @{$t->{tests}}
  1

=end

=cut

sub test {
  my ($self) = @_;
  my @tests = @{$self->{tests}};
  my $run = 0;
  my $test = Test::Builder->new;

  if (!$test->has_plan) {
    $test->plan(tests => scalar @tests);
  }

  for (@{$self->{tests}}) {
    local $" = ';';
    my ($name, $file, $line, $expect, @code) = @{$_};
    my $result = eval "sub { @code }->()";
    if ($@) {
      croak $@;
    }
    $test->is_eq($result, $expect, "$name ($file, $line)");
    $run++;
  }

  return $run;
}

1;

__END__

=head1 HISTORY

=over 8

=item 0.01

Original version

=back

=head1 SEE ALSO

L<Pod::Parser>, L<Test::Builder>

B<Pod::Parser> defines the parser interface used to extract the tests.

B<Test::Builder> is used to plan the tests and determine the results.

=head1 AUTHOR

Bryan Cardillo E<lt>dillo@cpan.org<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2009 by Bryan Cardillo

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut

Saturday, November 28, 2009

django ssl decorator

I recently found myself needing a way to require some django views be accessed only via a secure channel, as in ssl. A quick search led me to this post. I was able to quickly get up and running with this snippet and a couple of standard view functions, but ran into problems when trying to decorate a FormWizard view. The problem lied in the fact that FormWizards are implemented as classes with overridden __call__ methods. Long story short, I reworked the decorator as a class to work with functions and classes implementing __call__.

try:
  from functools import update_wrapper
except ImportError:
  from django.utils.functional import update_wrapper

class ssl_required(object):
  '''Decorator class to force ssl'''
  def __init__(self, func):
    self.func = func
    update_wrapper(self, func)

  def __get__(self, obj, cls=None):
    func = self.func.__get__(obj, cls)
    return ssl_required(func)

  def __call__(self, req, *args, **kwargs):
    if req.is_secure() or \
        not getattr(settings, 'HTTPS_SUPPORT', True):
      return self.func(req, *args, **kwargs)
    url = req.build_absolute_uri(req.get_full_path())
    url = url.replace('http://', 'https://')
    return HttpResponseRedirect(url)

Wednesday, August 5, 2009

old school screencasting

Screencasting with GNU screen and ImageMagick.

Something I just stumbled over the other day while putting together some screencasts for a Linux class I teach is that between the ability of GNU screen to write hardcopies of the terminal and ImageMagick's text image format... terminal sessions can be screencast as simple animated gifs with a couple config tweeks and a command or two.

First, configure screen, you'll need a recent enough version to have the "idle" command.

hardcopy_append on
idle 1 hardcopy

This will configure screen to take a snapshot every time you pause for a second. Ok, I know, its annoying to have to stop for a second between each key to get the individual keys to show up one at a time. On the other hand, you get used to it and slowing down helps me to avoid typing mistakes while screencasting.

So, fire up screen and start demonstrating away. When you are done, you will have a rather large hardcopy (usually, hardcopy.0) your terminal at one second intervals. A quick sed script to strip out the line delimiter screen places between each hardcopy like so, pipe to split to make one file per capture (assuming your terminal is 24 lines).

sed '/^.==*.$/' hardcopy.0 | split -l 24
Ok, now with one file per second for our entire terminal session, it is time to fire up convert from the ImageMagick suite. convert has the ability to read plain text files, render the text in an image and then write that image in a variety of formats. Of those formats, gifs support adding multiple images to create an animation. So, take all those files created by split as input, write one animated gif as output.

convert -extent 576x320+38+36 -font LucidaCons text:x?? -negate -loop 0 screen.gif

And there you have it, and old school screencast that will make your vnc-recording, windows-installer-running, heavy-weight-video-editing friends jealous.

Sunday, July 26, 2009

recursive descent json parser in perl

Sure, there are real, fully featured, well tested json parsers out there, but for a brief moment I thought I might need to write my own (why is a different story altogether, but for now I'll just say that trimming dependencies was necessary).

Anyway, after a quick look at the spec (hint, its very simple) and a couple of hours coding, I can now present a reasonably functional (character escapes are missing, might be missing other features as well) recursive descent json parser implementation in perl. This is definitely not the best choice for adding json support to your next perl project, however it is a simple and practical example of recursive descent parsing that others might find useful.

Anecdotally, if eval'ing json input is not a concern (for security reason's it probably should be), you might just substitute arrows (=>) for colon's (:) while reading in json and eval it as an even simpler alternate solution...


#!/usr/bin/env perl
package json;

use 5.006;
use strict;
use warnings;

use Carp;

BEGIN {
for (qw(file buffer token line pos)) {
eval "sub $_ : lvalue { \$_[0]->{$_}=\$_[1] if \@_>1;\$_[0]->{$_} }";
croak $@ if $@;
}
}

sub new {
bless { file => *ARGV, line => 0, pos => 0 }, $_[0];
}

sub accept {
my ($self, $chrs) = @_;
for (split(//, $chrs)) {
return 0 unless $self->token eq $_;
$self->advance;
}
return 1;
}

sub expect {
my ($self, $chrs) = @_;
$self->accept($chrs) or confess $self->error;
$self;
}

sub error {
my ($self) = @_;
"unexpected token '", $self->token, "' at line ", $self->line, "\n";
}

sub advance {
my ($self, $ns) = @_;
for ($self->token = undef; not defined $self->token; $self->pos++) {
unless ($self->buffer and $self->pos < length($self->buffer)) {
defined($self->buffer = readline($self->file)) or return;
$self->line++;
$self->pos = 0;
}
$self->token = substr($self->buffer, $self->pos, 1)
if ($ns or substr($self->buffer, $self->pos, 1) !~ /[[:space:]]/);
}
}

sub object {
my ($self, $object, $key, $val) = @_;
$key = $self->expect('"')->string;
$val = $self->expect('"')->expect(':')->value;
$object->{$key} = $val;
$self->object($object) if $self->accept(',');
$object;
}

sub array {
my ($self, $array) = @_;
push @$array, $self->value;
$self->array($array) if $self->accept(',');
$array;
}

sub string {
my ($self, $str) = @_;
do {
$str .= $self->token;
$self->advance(1);
} while ($self->token ne '"');
$str;
}

sub digits {
my ($self, $d) = @_;
do {
$d .= $self->token;
$self->advance(1);
} while ($self->token =~ /[[:digit:]]/);
$d;
}

sub number {
my ($self, $n) = @_;
$n .= '-' if ($self->accept('-'));
$n .= $self->digits();
if ($self->accept('.')) {
$n .= '.';
$n .= $self->digits();
}
if ($self->accept('e') or $self->accept('E')) {
$n .= 'e';
if ($self->accept('+')) {
$n .= '+';
} elsif ($self->accept('-')) {
$n .= '-';
}
$n .= $self->digits();
}
$self->advance if $self->token =~ /[[:space:]]/;
$n+0;
}

sub value {
my ($self, $value) = @_;
$self->advance unless defined $self->token;
if ($self->accept('{')) {
$value = $self->object({});
$self->expect('}');
} elsif ($self->accept('[')) {
$value = $self->array([]);
$self->expect(']');
} elsif ($self->accept('"')) {
$value = $self->string;
$self->expect('"');
} elsif ($self->accept('null')) {
$value = undef;
} elsif ($self->accept('true')) {
$value = 1;
} elsif ($self->accept('false')) {
$value = 0;
} elsif ($self->token =~ /[[:digit:].-]/) {
$value = $self->number;
} else {
confess $self->error;
}
$value;
}

sub main {
use Data::Dumper;
print Data::Dumper->Dump([json->new->value], ['json']);
}

main unless caller;

1;

Monday, June 8, 2009

dependency injection in 100 lines (or less)

If you intend to develop software in a modular fashion, at some point you will need to swap out one implementation of an interface (or service) for another. Now, the naive approach, to simply find and replace will work for a while. However, at some point you will have too many instances or simply tire of switching back and forth (with all the recompiling that entails, I'm looking at you Java).

Dependency injection is a type of inversion of control (see Fowler), which means that instead of creating dependencies by having the code you write reference other code (either that you also wrote, or in libraries) directly at compile time, you switch things up and let someone else (the injector) determine how to satisfy your requirements at runtime.

For my object oriented java class we do a largish (for a class anyway) software project. For example, past projects have included peer to peer file sharing systems, web application server, and email gui's. The point is, something that is too big for a single person or group to complete in the time allotted and something that can be easily split into smaller pieces. We design the solution as a class, write the interfaces, and then each student (or sometimes group) is responsible for implementing one component of the complete system.

Of course, I supply bytecode solutions (but not the source) for all the components and mock classes for testing. And finally, I needed an easy way to sometimes use my implementations, sometimes use my mockups, and other times use my students solutions. So, given that background, you will find my solution below.

It is not an enterprise IoC container and it has only been tested in a classroom environment (it may very well cause your computer to burst into flames, you have been warned). However, it might serve as an instructive example precisely because, it is short and to the point.

import java.io.*;
import java.lang.reflect.*;
import java.util.*;

public class ObjectFactory
{
    private static String CONFIG = "ObjectFactory.properties";
    private static Properties config = new Properties();

    public static <T> T create(Class<T> cls)
    {
        Class rcls;
        Constructor<T> cn = null;
        Class<?>[] pt = null;
        Object[] params = null;
        T inst = null;
        String name, real;

        if (config.isEmpty()) {
            try {
                InputStream in = ClassLoader.getSystemResourceAsStream(CONFIG);
                if (in == null)
                    throw except("file not found");
                config.load(in);
            } catch (IOException e) {
                throw except(e.toString());
            }
        }

        name = cls.getName();

        if (cls.isInterface() && !config.containsKey(name))
            throw except("no configured implementation for %s", name);

        real = (String)config.get(name);
        try {
            rcls = real != null ? Class.forName(real) : cls;

            for (Constructor<T> c : rcls.getConstructors()) {
                if (cn == null || c.getParameterTypes().length < pt.length) {
                    cn = c;
                    pt = c.getParameterTypes();
                }
            }

            params = new Object[pt.length];
            for (int i = 0; i < params.length; i++)
                params[i] = create(pt[i]);

            inst = cls.cast(cn.newInstance(params));
        } catch (InvocationTargetException e) {
            throw except("invocation failed for %s is %s, " +
                "but %s not a subclass of %s", name, real, real, name);
        } catch (ClassCastException e) {
            throw except("the configured implementation of %s is %s, " +
                "but %s not a subclass of %s", name, real, real, name);
        } catch (ClassNotFoundException e) {
            throw except("the configured implementation of %s is %s, " +
                "but %s cannot be found", name, real, real);
        } catch (InstantiationException e) {
            throw except("the configured implementation of %s is %s, " +
                "but a new %s cannot be created", name, real, real);
        } catch (IllegalAccessException e) {
            throw except("the configured implementation of %s is %s, " +
                "but a new %s cannot be created", name, real, real);
        }

        return inst;
    }

    private static RuntimeException except(String msg, Object ... args)
    {
        return new RuntimeException(String.format(
            "configuration error in %s: " + msg, CONFIG, args));
    }
}

Configuration is simple (it's a standard Java properties file), just a file named ObjectFactory.properties, located via the classpath. You just map an interface or abstract class to its concrete implementation. See the example below.

Message=MockMessage
Folder=MockFolder

So there you have it, 76 lines, probably could use some comments (and still be under 100). On the other hand, I probably could have squeezed it more too, but this version has pretty reasonable errors.