The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

jQuery::File::Upload - Server-side solution for the jQuery File Upload plugin.

SYNOPSIS

  use jQuery::File::Upload;

  #simplest implementation
  my $j_fu = jQuery::File::Upload->new;
  $j_fu->handle_request;
  $j_fu->print_response;

  #alternatively you can call $j_fu->handle_request(1) and this will call print_response for you.
  my $j_fu = jQuery::File::Upload->new;
  $j_fu->handle_request(1);

The above example is the simplest one possible, however it assumes a lot of defaults.

Assumptions

default upload directory

It is assumed that your files are being uploaded to the current directory of the script that's running, plus '/files'. So if your script is in /home/user/public_html, your files will be uploaded to /home/user/public_html/files.

default url

It is also assumed that the files will be hosted one directory above the running script, plus '/files'. So if the script is located at the url http://www.mydomain.com/upload.cgi, then all files will be assumed to be at the url http://www.mydomain.com/files/file_name.

default filename

Uploaded files are given a name at run time unless you specifically set the filename in the jQuery::File::Upload object.

same server upload

By default, jQuery::File::Upload also assumes that you're meaning to have the uploaded file uploaded to the same server that the script is running on. jQuery::File::Upload also has the ability to SCP files to remote servers, but this is not the default.

CGI environment

By default, jQuery::File::Upload assumes that the script is running in a regular CGI-type environment. However, jQuery::File::Upload also has the ability to work with Catalyst by being passed the context object.

all files

This implementation accepts all types of files.

A more complicated example

  use jQuery::File::Upload;

  my $j_fu = jQuery::File::Upload->new(
                scp => [{
                        user => 'user', #remote user
                        public_key => '/home/user/.ssh/id_rsa.pub',     #also possible to use password instead of keys
                        private_key => '/home/user/.ssh/id_rsa',
                        host => 'mydomain.com',
                        upload_dir => '/var/www/html/files', #directory that files will be uploaded to
                }],

                #user validation specifications
                max_file_size => 5242880, #max file size is 5mb
                min_file_size => 1,
                accept_file_types => ['image/jpeg','image/png','image/gif','text/html'], #uploads restricted to these filetypes
                max_width => 40000, #max width for images
                max_height => 50000, #max height for images
                min_width => 1,
                min_height => 1,
                max_number_of_files => 40, #maximum number of files we can have in our upload directory
          );


  $j_fu->handle_request;
  $j_fu->print_response;

Getting fancy

  use jQuery::File::Upload;

  my $j_fu = jQuery::File::Upload->new(
                        pre_get => sub {
                                my $j = shift; #jQuery::File::Upload object for current request
                                #any code in here will be executed before any get requests are handled
                                #jQuery File Upload makes Get request when the page first loads, so this
                                #can be useful for prefilling jQuery File Upload with files if you're using
                                #jQuery File upload with saved data to view/delete/upload more

                                #generate starting files for jQuery File Upload
                                $j->generate_output(
                                        [
                                                {
                                                        size => 500000,
                                                        filename =>     'my_image.jpeg',
                                                        image => 'y', #need to let jQuery::File::Upload know this is an image
                                                                      #or else thumbnails won't be deleted
                                                },
                                                {
                                                        size => 500000,
                                                        filename =>     'my_other_image.jpeg',
                                                        image => 'y',
                                                },
                                        ]
                                );

                                #The above makes assumptions yet again. It generates the url based on the defaults, unless
                                #you provide below the upload_url_base.
                        },
                        pre_delete => sub {
                                my $j = shift;

                                #here you can do something with the information in the params of the delete_url
                                #you can set your own meaningful delete_params (see below)
                                #NOTE: delete_urls always have the 'filename' param, so that might be enough for your needs
                                my $id = param->('id')

                                #DELETE FROM table WHERE id=$id
                                #etc.
                        },
                        post_post => sub {
                                my $j = shift;
                                #do some stuff here after post (image is uploaded)
                                #possibly save information about image in a database to keep track of it?
                                #you can call any methods now to get useful info:

                                #INSERT INTO table (name,is_image,width,height) VALUES($j->filename,$j->is_image,$j->final_width,$j->final_height)
                                #etc
                        },
                        delete_params => ['key1','val1','key2','val2'],
                                                            #this will add these key value pairs as
                                                            #params on the delete_url that is generated
                                                            #for each image. This could be useful if you
                                                            #kept track of these files in a database and wanted to
                                                            #delete them from the database when they are deleted.
                                                            #then delete_params could be ['id',unique_db_identifier]
                                                            #then you could check for the param 'id' in pre_delete
                                                            #and delete the file from your DB
                );


  #N.B. All of the above can also be set with the getter/setter methods

  $j_fu->handle_request(1); #when passed a one, will call print_response for you

DESCRIPTION

jQuery::File::Upload makes integrating server-side with the jQuery File Upload plugin simple. It provides many features, such as:

  1. the ability to SCP file uploads to remote servers

  2. the ability to provide your own functions to add to how each request is handled before the request and after the request

  3. options to validate the uploaded files server-side

  4. automatically generates thumbnails if the file is an image

  5. see below for everything you can do with jQuery::File::Upload

The location of the script should be where jQuery File Upload is told to upload to.

METHODS

Getters/Setters

new

Any of the below getters/setters can be passed into new as options.

  my $j_fu = jQuery::File::Upload->new(option=>val,option2=>val2,...);

upload_dir

  $j_fu->upload_dir('/home/user/public_html/files');

Sets the upload directory if saving files locally. Should not end with a slash. The default is the current directory of the running script with '/files' added to the end:

  /home/user/public_html/upload.cgi

yields:

  /home/user/public_html/files

When using jQuery::File::Upload under normal CGI, it should have no problem generating this default upload directory if that's what you want. However, if you are using Catalyst, depending on how you're running Catalyst (i.e. mod_perl, fastcgi, etc.) the generated default might be kind of strange. So if you are using Catalyst and you want to upload to the same server that jQuery::File::Upload is running on, it's best to just manually set this. Make sure that the user running your script can write to the directory you specify.

thumbnail_upload_dir

  $j_fu->thumbnail_upload_dir('/home/user/public_html/files/thumbs');

This can be used to set the upload directory form thumbnails. The default is upload_dir. If you change this, that will make thumbnails have a different base url than upload_url_base. Make sure to change thumbnail_url_base to match this accordingly. If you would like images and thumbnails to have the same name but just be in different directories, make sure you set thumbnail_prefix to ''. This should not end with a slash. Make sure that the user running your script can write to the directory you specify.

upload_url_base

  $j_fu->upload_url_base('http://www.mydomain.com/files');

Sets the url base for files. Should not end with a slash. The default is the current directory of the running script with '/files' added to the end:

  http://www.mydomain.com/upload.cgi

yields:

  http://www.mydomain.com/files

Which means that a file url would look like this:

  http://www.mydomain.com/files/file.txt

thumbnail_url_base

  $j_fu->thumbnail_url_base('http://www.mydomain.com/files/thumbs');

Sets the url base for thumbnails. Should not end with a slash. The default is upload_url_base. Resulting thumbnail urls would look like:

  http://www.mydomain.com/files/thumbs/thumb_image.jpg

However, if thumbnail_relative_url_base is set, the default will be the current url with the thumbnail relative base at the end.

relative_url_path

  $j_fu->relative_url_path('/files');

This sets the relative url path for your files relative to the directory your script is currently running in. For example:

  http://www.mydomain.com/upload.cgi

yields:

  http://www.mydomain.com/files

and then all files will go after /files. The default for this is /files, which is why upload_url_base has the default /files at the end. If your location for the images is not relative, i.e. it is located at a different domain, then just set upload_url_base to get the url_base you want. There should not be a slash at the end.

thumbnail_relative_url_path

  $j_fu->thumbnail_relative_url_path('/files/thumbs');

This sets the thumbnail relative url path for your files relative to the directory your script is currently running in. For example:

  http://www.mydomain.com/upload.cgi

yields:

  http://www.mydomain.com/files/thumbs

and then all thumbnails will go after /files/thumbs. The default for this is nothing, so then the thumbnail_url will just fall back on whatever the value of upload_url_base is. If your location for thumbnail images is not relative, i.e. it is located at a different domain, then just set thumbnail_url_base to get the url_base you want. There should not be a slash at the end.

relative_to_host

  $j_fu->relative_to_host(1);

If set to 1, this will make relative_url_path and thumbnail_relative_url_path be relative to the host of the script url. For example:

  http://www.mydomain.com/folder/upload.cgi

With a relative_url_path '/files' would yield:

  http://www.mydomain.com/files

Whereas by default relative_url_path and thumbnail_relative_url_path are relative to the folder the upload script is running in.

If you use this option, make sure to set upload_dir (and/or thumbnail_upload_dir if necessary) since jQuery::File::Upload can no longer do a relative path for saving the file.

Default is undef.

field_name

  $j_fu->field_name('files[]');

This is the name of the jQuery File Uploader client side. The default is files[], as this is the jQuery File Upload plugin's default.

ctx

  $j_fu->ctx($c);

This is meant to set the Catalyst context object if you are using this plugin with Catalyst. The default is to not use this.

cgi

  $j_fu->cgi(CGI->new);

This should be used mostly internally by jQuery::File::Upload (assuming you haven't passed in ctx). It is just the CGI object that the module uses, however if you already have one you could pass it in.

should_delete

  $j_fu->should_delete(1)

This is used to decide whether to actually delete the files when jQuery::File::Upload receives a DELETE request. The default is to delete, however this could be useful if you wanted to maybe just mark the field as deleted in your database (using pre_delete) and then actually physically remove it with your own clean up script later. The benefit to this could be that if you are SCPing the files to a remote server, perhaps issuing the remote commands to delete these files is something that seems to costly to you.

scp

  $j_fu->scp([{
                        host => 'media.mydomain.com',
                        user => 'user',
                        public_key => '/home/user/.ssh/id_rsa.pub',
                        private_key => '/home/user/.ssh/id_rsa',
                        password => 'pass', #if keys are present, you do not need password
                        upload_dir => '/my/remote/dir',
                }]);

This method takes in an arrayref of hashrefs, where each hashref is a remote host you would like to SCP the files to. SCPing the uploaded files to remote hosts could be useful if say you hosted your images on a different server than the one doing the uploading.

SCP OPTIONS

  • host (REQUIRED) - the remote host you want to scp the files to, i.e. 127.0.0.1 or media.mydomain.com

  • user (REQUIRED) - used to identify the user to remote server

  • public_key & private_key - used to make secure connection. Not needed if password is given.

  • password - used along with user to authenticate with remote server. Not needed if keys are supplied.

  • upload_dir (REQUIRED) - the directory you want to scp to on the remote server. Should not end with a slash

  • thumbnail_upload_dir - Will default to upload_dir. You only need to provide this if your thumbnails are stored in a different directory than regular images. Should not end with a slash

You can check Net::SSH2 for more information on connecting to the remote server.

max_file_size

  $j_fu->max_file_size(1024);

Sets the max file size in bytes. By default there is no max file size.

min_file_size

  $j_fu->min_file_size(1);

Sets the minimum file size in bytes. Default minimum is 1 byte. to disable a minimum file size, you can set this to undef or 0.

accept_file_types

  $j_fu->accept_file_types(['image/jpeg','image/png','image/gif','text/html']);

Sets what file types are allowed to be uploaded. By default, all file types are allowed. File types should be in the format of the Content-Type header sent on requests.

reject_file_types

  #None of these types are allowed.
  $j_fu->reject_file_types(['image/jpeg','image/png','image/gif','text/html']);

Sets what file types are NOT allowed to be uploaded. By default, all file types are allowed. File types should be in the format of the Content-Type header sent on requests.

require_image

  $j_fu->require_image(1);

If set to 1, it requires that all uploads must be an image. Setting this is equivalent to calling:

  $j_fu->accept_file_types(['image/jpeg','image/jpg','image/png','image/gif']);

Default is undef.

delete_params

  $j_fu->delete_params(['key1','val1','key2','val2']);

Sets the keys and values of the params added to the delete_url. This can be useful when used with pre_delete, because if you are keeping track of these files in a database, you can add unique identifiers to the params so that in pre_delete you can get these unique identifiers and use them to remove or edit the file in the databse. By default filename will also be a param unless you set the delete_url manually.

delete_url

  $j_fu->delete_url('http://www.mydomain.com/upload.cgi?filename=file.jpg');

This can be used to set the delete_url that will be requested when a user deletes a file. However, it is recommended that you do not set this manually and rather use delete_params if you want to add your own params to the delete_url.

thumbnail_width

  $j_fu->thumbnail_width(80);

This sets the width for the thumbnail that will be created if the file is an image. Default is 80.

thumbnail_height

  $j_fu->thumbnail_height(80);

This sets the height for the thumbnail that will be created if the file is an image. Default is 80.

thumbnail_quality

  $j_fu->thumbnail_quality(70);

This sets the quality of the thumbnail image. Default is 70 and it can be on a scale of 0-100. See Image::Magick for more information.

thumbnail_format

  $j_fu->thumbnail_format('jpg');

Sets the format for the generated thumbnail. Can be jpg, png, or gif. See Image::Magick for more information. Defaults to jpg.

thumbnail_density

  $j_fu->thumbnail_density('80x80');

Sets the density for the generated thumbnail. Default is width x height. See Image::Magick for more information.

thumbnail_prefix

  $j_fu->thumbnail_prefix('thumb_');

Added before the image filename to create the thumbnail unique filename. Default is 'thumb_'.

thumbnail_postfix

  $j_fu->thumbnail_postfix('_thumb');

Added after the image filename to create the thumbnail unique filename. Default is ''.

thumbnail_final_width

  my $final_width = $j_fu->thumbnail_final_width;

Because the thumbnails are scaled proportionally, the thumbnail width may not be what you orignally suggested. This gets you the final width.

thumbnail_final_height

  my $final_height = $j_fu->thumbnail_final_height;

Because the thumbnails are scaled proportionally, the thumbnail height may not be what you orignally suggested. This gets you the final height.

quality

  $j_fu->quality(70);

This sets the quality of the uploaded image. Default is 70 and it can be on a scale of 0-100. See Image::Magick for more information.

process_images

  $j_fu->process_images(1);

Create thumbnails for uploaded image files when set to 1. When set to undef, jQuery::File::Upload will skip creating thumbnails even when the uploaded file is an image. Images are simply treated like any other file. The default is 1.

format

  $j_fu->format('jpg');

Sets the format for the generated thumbnail. Can be jpg,png, or gif. See Image::Magick for more information. Defaults to jpg.

final_width

  my $final_width = $j_fu->final_width;

Returns the final width of the uploaded image.

final_height

  my $final_height = $j_fu->final_height;

Returns the final height of the uploaded image.

max_width

  $j_fu->max_width(10000);

Sets the maximum width of uploaded images. Will return an error to browser if not valid. Default is any width.

max_height

  $j_fu->max_height(10000);

Sets the maximum height of uploaded images. Will return an error to browser if not valid. Default is any height.

min_width

  $j_fu->min_width(10000);

Sets the minimum width of uploaded images. Will return an error to browser if not valid. Default is 1.

min_height

  $j_fu->min_height(10000);

Sets the minimum height of uploaded images. Will return an error to browser if not valid. Default is 1.

max_number_of_files

  $j_fu->max_number_of_files(20);

Sets the maximum number of files the upload directory can contain. Returns an error to the browser if number is reached. Default is any number of files. If you have listed multiple remote directories, the maximum file count out of all of these directories is what will be used.

filename

  my $filename = $j_fu->filename;

Returns the resulting filename after processing the request.

  $j_fu->filename('my_name.txt');

You can also set the filename to use for this request before you call handle_request. However, unless you're sure that you are going to give the file a unique name, you should just let jQuery::File::Upload generate the filename. Please note that if you choose your own filename, you do have to manually set thumbnail_filename

absolute_filename

  my $absolute_filename = $j_fu->absolute_filename;

Returns the absolute filename of the file on the server. You can also set this manually if you would like, or jQuery::File::Upload will generate it for you.

thumbnail_filename

  $j_fu->filename('my_name.txt');

You can also set the thumbnail_filename to use for this request before you call handle_request. However, unless you're sure that you are going to give the file a unique name, you should just let jQuery::File::Upload generate the filename.

absolute_thumbnail_filename

  my $absolute_filename = $j_fu->absolute_thumbnail_filename;

Returns the absolute filename of the thumbnail image on the server. You can also set this manually if you would like, or jQuery::File::Upload will generate it for you.

client_filename

  my $client_filename = $j_fu->client_filename;

Returns the filename of the file as it was named by the user.

show_client_filename

  $j_fu->show_client_filename(1);

This can be used to set whether jQuery::File::Upload shows the user the name of the file as it looked when they uploaded, or the new name of the file. When set to true, the user will see the file as it was named on their computer. The default is true, and this is recommended because typically the user's filename will look better than the unique one that jQuery::File::Upload generates for you.

use_client_filename

  $j_fu->use_client_filename(0);

If this is set to true, jQuery::File::Upload will use the user's name for the file when saving it. However, this is not recommended because the user could have two files named the same thing that could overwrite one another, and same scenario between two different users. It is best to let jQuery::File::Upload generate the filenames to save with because these are much more likely to be unique. Another reason not to use client filenames is that it is possible that they could have invalid characters in them such as spaces which will prevent a url from loading.

filename_salt

  $j_fu->filename_salt('_i_love_the_circus');

Anything added here will be appended to the end of the filename. This is meant to be used if you want to guarantee uniqueness of image names, i.e. you could use a user id at the end to greatly lessen the chance of duplicate filenames. Default is nothing.

copy_file

  $j_fu->copy_file(undef);

Performs a copy instead of a link from the temporary directory to the upload directory. This might be useful if you are using Windows share that can't handle links. The default is undef and thus jQuery::File::Upload will use links.

tmp_dir

  $j_fu->tmp_dir('/tmp');

The provided directory will be used to store temporary files such as images. Make sure that the user the script is running under has permission to create and write to files in the tmp_dir. Also, there should be no slash at the end. Default is /tmp.

script_url

  $j_fu->script_url('http://www.mydomain.com/upload.cgi');

This can be used to set the url of the script that jQuery::File::Upload is running under. jQuery::File::Upload then uses this value to generate other parts of the output. jQuery::File::Upload in most cases is able to figure this out on its own, however if you are experiencing issues with things such as url generation, try setting this manually.

data

  $j_fu->data({
            dbh => $dbh,
            my_var = $var,
            arr = [],
            self => $self, #maybe useful for Catalyst
        });

This method can be populated with whatever you like. Its purpose is if you need to get access to other data in one of your "PRE/POST REQUEST METHODS". This way you can access any outside data you need by calling data on the jQuery::File::Upload object that you are passed. However, keep in mind that if you are using Catalyst, you will have access to the context object via the jQuery::File::Upload object that is passed in, and this would be an equally good place to store/retrieve data that you need.

JUST GETTERS

output

  my $output = $j_fu->output;

Returns the JSON output that will be printed to the browser. Unless you really feel you need the JSON, it's usually just easier to call print_response as this prints out the header and the JSON for you (or alternatively call handle_response and pass it a 1 so that it will call print_response for you.

url

  my $file_url = $j_fu->url;

This returns the resulting url of the file.

thumbnail_url

  my $thumbnail_url = $j_fu->thumbnail_url;

This returns the resulting thumbnail url of the image.

is_image

  my $is_image = $j_fu->is_image;

Returns whether or not the uploaded file was an image. This should be called after handle_request or in post_post.

size

  my $size = $j_fu->size;

Returns the size of the uploaded file. This should be called after handle_request or in post_post.

OTHER METHODS

  $j_fu->print_response;

Should be called after handle_request. Prints out header and JSON back to browser. Called for convenience by handle_request if handle_request is passed a 1.

handle_request

  $j_fu->handle_request;

Called to handle one of 'GET','POST', or 'DELETE' requests. If passed a 1, will also call print_response after it's finished.

generate_output

  $j_fu->generate_output([{
                        image => 'y', #or 'n'
                        filename => 'my_cool_pic.jpg',
                        size => 1024,
                  }]);

This should be used in conjuction with pre_get to populate jQuery File Upload with files on page load. It takes in an arrayref of hashrefs, where each hashref is a file. After this method is called, you will need to call print_response or handle_request with a 1 to print out the JSON.

GENERATE_OUTPUT OPTIONS

  • filename (REQUIRED) - name of the file

  • size (REQUIRED) - size in bytes

  • image - 'y' or 'n'. Necessary if file is image and you would like thumbnail to be deleted with file. Also, needed if you want thumbnail to be displayed by jQuery File Upload

  • name - name that will be displayed to client as the filename. If not provided, defaults to filename. Can be used well with client_filename to make filename's look prettier client-side.

  • thumbnail_filename - filename for thumbnail. jQuery::File::Upload will generate the thumbnail_filename based on the filename and other factors (such as upload_url_base) if you don't set this.

  • url - url used for file. If not provided, will be generated with filename and other defaults.

  • thumbnail_url - url used for thumbnail. If not provided, will be generated with other defaults.

  • delete_url - url that will be called by jQuery File Upload to delete the file. It's better to just let jQuery::File::Upload generate this and use delete_params if you want to set your own parameters for the delete url.

  • delete_params - The format of this is just like delete_params. It takes [key,value] pairs. Any values here will be added in addition to any global delete_params that you set.

  • error - can be used to supply an error for a file (although I don't really know why you would use this...)

Note that jQuery::File::Upload will generate urls and such based upon things given here (like filename) and other options such as upload_url_base.

PRE/POST REQUEST METHODS

N.B. The following functions are all passed a jQuery::File::Upload object. And they can be passed into new as options.

Also, note that since all of these user-defined methods are passed the jQuery::File::Upload object, if you are using Catalyst you can just call the ctx method to get anything stored via your context object. For Catalyst users, this makes this a practical (and possibly better) alternative to the provided data method.

pre_delete

  $j_fu->pre_delete(sub { my $j_fu = shift });

or

  $j_fu->pre_delete(\&mysub);

pre_delete will be called before a delete request is handled. This can be useful if you want to mark a file as deleted in your database. Also, you can use this along with delete_params to set unique identifiers (such as an id for the file or the primary key) so that you can find the file in your database easier to perform whatever operations you want to on it. Note: This will be called even if should_delete is set to false. If your pre_delete returns a value, this will be interpreted as an error message and the delete call will be terminated and will return the error. For example:

  $j_fu->pre_delete(sub {
    return 'You cannot delete this file.'; #file will not be deleted
  });

post_delete

  $j_fu->post_delete(sub { my $j_fu = shift });

or

  $j_fu->post_delete(\&mysub);

post_delete will be called after a delete request is handled. Note: This will not be called if should_delete is set to false.

pre_post

  $j_fu->pre_post(sub { my $j_fu = shift });

or

  $j_fu->pre_post(\&mysub);

pre_post will be called before a post request is handled. POST requests are what happen when jQuery File Upload uploads your file. If your pre_post returns a value, this will be interpreted as an error message and the post call will be terminated and will return the error. For example:

  $j_fu->pre_post(sub {
    return 'You have too many files.'; #file will not be uploaded
  });

post_post

  $j_fu->post_post(sub { my $j_fu = shift });

or

  $j_fu->post_post(\&mysub);

post_post will be called after a post request is handled. This can be useful if you want to keep track of the file that was just uploaded by recording it in a database. You can use the jQuery::File::Upload object that is passed in to get information about the file that you would like to store in the databse. Later on you can use this stored information about the files to prepopulate a jQuery File Upload form with files you already have by preloading the form by using pre_get.

pre_get

  $j_fu->pre_get(sub { my $j_fu = shift });

or

  $j_fu->pre_get(\&mysub);

pre_get will be called before a get request is handled. Get requests happen on page load to see if there are any files to prepopulate the form with. This method can be useful to prepopulate the jQuery File Upload form by combining saved information about the files you want to load and using generate_output to prepare the output that you would like to send to the jQuery File Upload form.

post_get

  $j_fu->post_get(sub { my $j_fu = shift });

or

  $j_fu->post_get(\&mysub);

post_get will be called after a get request is handled.

EXPORT

None by default.

Catalyst Performance - Persistent jQuery::File::Upload

A jQuery::File::Upload object shouldn't be too expensive to create, however if you'd like to only create the object once you could create it as an Moose attribute to the class:

  use jQuery::File::Upload;
  has 'j_uf' => (isa => 'jQuery::File::Upload', is => 'rw',
                  lazy => 0, default => sub { jQuery::File::Upload->new } );

However, if you do this it is possible that you could run into issues with values of the jQuery::File::Upload object that were not cleared messing with the current request. The _clear method is called before every handle_request which clears the values of the jQuery::File::Upload object, but it's possible I may have missed something.

SEE ALSO

AUTHOR

Adam Hopkins, <srchulo@cpan.org>

Bugs

I haven't tested this too thoroughly beyond my needs, so it is possible that I have missed something. If I have, please feel free to submit a bug to the bug tracker, and you can send me an email letting me know that you submitted a bug if you want me to see it sooner :)

COPYRIGHT AND LICENSE

Copyright (C) 2013 by Adam Hopkins

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.8 or, at your option, any later version of Perl 5 you may have available.