Writing a new Dancer serializer backend

Writing serializers in Dancer is so easy you can read this standing on your head!

In this entry we'll be writing a new serializer for Dancer and upload it to CPAN! Feel free to add your own once you finish reading this. :)

How do serializers work?

Serializers basically do two things:

  • Serialize a data structure

    Take a data structure as a parameter and return serialized data.

  • Deserialize serialized data

    Take serialized data as a parameter and return structured data.

Deciding on the serialization format

Serializes are so easy to write, Dancer already includes a few, such as:

  • JSON

    Using JSON.

  • XML

    Using XML::Simple.

  • YAML

    Using YAML.

  • Dumper

    Using Data::Dumper.

  • Mutable

    A self-adjusting serializer that uses the content type and accept headers.

Since these are already taken (unless you want to change one implementation with another, such as YAML for YAML::Tiny), there's not much point to write them again.

And since the recommended serialization formats are already mentioned above, it's a good reason to look into more nether regions, such as... UU encoding!

Of course, UU encoding isn't a format in its own, just an encoding so it isn't recursive and we need to encode an entire structure recursively. We'll cheat around it but using Storable's nfreeze and then encoding it.

(credit goes out to Apache::Session for the idea)

Module skeleton

This should be the simplest skeleton, including just the required modules:

package Dancer::Serializer::UUEncode;

use strict;
use warnings;

use Carp;
use Storable qw/ nfreeze thaw /;
use base 'Dancer::Serializer::Abstract';

# ...

1;

First, notice that we're using Dancer::Serializer::Abstract as a base, that's because we already implemented the Dancer integration logic under that base class. It promises us less work and more present and future portability. It also makes our module object oriented.

Methods

There are a three subroutines we should implement.

  • serialize

    The subroutine that does the serialization process itself.

    sub serialize {
        my ( $self, $entity ) = @_;
    
        return pack( 'u', nfreeze($entity) );
    }

    Yes, that's it!

  • deserialize

    The subroutine that does the deserialization process itself.

    sub deserialize {
        my ( $self, $content ) = @_;
        my $data = thaw( unpack( 'u', $content ) );
    
        defined $data or croak "Couldn't thaw unpacked content '$content'";
    
        return $data;
    }
  • content_type

    This subroutine simply returns the content type of our serialization. This is good practice for web applications so we'll implement it. If we don't, it will default to text/plain, which is good too.

    sub content_type {'text/uuencode'}

We pretty much finished the serializer.

Did we really finish it so quickly?

Yes, we did!

What else can we do?

helpers

You can add helpers that will make it easier to use the serialize/deserialize subroutines functionally outside of automatic serialization.

# helpers
sub from_uuencode {
    my ($uuencode) = @_;
    my $s = Dancer::Serializer::UUEncode->new;

    return $s->deserializer($uuencode);
}

sub to_uuencode {
    my ($data) = @_;
    my $s = Dancer::Serializer::UUEncode->new;

    return $s->serialize($data);
}

initializer

The init subroutine is run on initialize of our object, and helps you take care of initialize checks you might have.

sub init {
    # do some checks
}

loader

The loaded subroutine is not run by Dancer itself but it is common practice to separate your lazy module loading to this subroutine and then run it using the initializer.

If we would want to lazy load Storable, we could use these subroutines as such:

sub loaded {
    require Storable;
    Storable->import('nfreeze');
}

sub init {
    my ($self) = @_;
    $self->loaded;
}

CPAN, anyone?

While you've been reading this entry, I've taken the liberty to upload what we just wrote to CPAN and it should now be available as Dancer::Serializer::UUEncode. Nice, isn't it?

Feel free to send me your names so I could add you to the CREDITS section in the POD! :)

See also

  • The previous article on Dancer Internals
  • The following article on Writing a new Dancer logger backend
  • The article after that on Writing a new Dancer session backend

Author

This article has been written by Sawyer X <xsawyerx@cpan.org> for the Perl Dancer Advent Calendar 2010.