Sam Graham > Cache-CacheFactory-1.10 > Cache::CacheFactory

Download:
Cache-CacheFactory-1.10.tar.gz

Dependencies

Annotate this POD

View/Report Bugs
Module Version: 1.10   Source  

NAME ^

Cache::CacheFactory - Factory class for Cache::Cache and other modules.

SYNOPSIS ^

 use Cache::CacheFactory;

 my $cache = Cache::CacheFactory->new( storage => 'file' );

 $cache->set( 'customer', 'Fred' );
 ... Later ...
 print $cache->get( 'customer' );
 ... prints "Fred"

DESCRIPTION ^

Cache::CacheFactory is a drop-in replacement for the Cache::Cache subclasses allowing you to access a variety of caching policies from a single place, mixing and matching as you choose rather than having to search for the cache module that provides the exact combination you want.

In a nutshell you specify a policy for storage, for pruning and for validity checks and CacheFactory hooks you up with the right modules to provide that behaviour while providing you with the same API you're used to from Cache::Cache - the only thing you need to change is your call to the constructor.

More advanced use allows you to set multiple policies for pruning and validity checks, and even for storage although that's currently of limited use.

METHODS ^

$cache = Cache::CacheFactory->new( %options )
$cache = Cache::CacheFactory->new( $options )

Construct a new cache object with the specified options supplied as either a hash or a hashref.

Errors during construction are usually fatal and reported via die, some have nonfatal_* options to override this behaviour in which case an undef value will be returned from new().

See "OPTIONS" below for more details on possible options.

$cache->set( key => $key, data => $data, [ expires_in => $expires_in, %additional_args ] )
$cache->add( key => $key, data => $data, [ expires_in => $expires_in, %additional_args ] )
$cache->replace( key => $key, data => $data, [ expires_in => $expires_in, %additional_args ] )
$cache->set( $key, $data, [ $expires_in ] ) (only in compat-mode)
$cache->add( $key, $data, [ $expires_in ] ) (only in compat-mode)
$cache->replace( $key, $data, [ $expires_in ] ) (only in compat-mode)

Associates $data with $key in the cache.

$cache->add() is a special form of $cache->set() that will set the key if-and-only-if it doesn't already exist in the cache.

$cache->replace() is a special form of $cache->set() that will set the key if-and-only-if it does already exist in the cache.

Note: the existence test and set in $cache->add() and $cache->replace() is NOT an atomic operation, if you have a shared cache you will need to implement your own locking mechanism if you need to rely on this behaviour.

A deep copy of $data will automatically be taken if it is a reference, you can turn this behaviour off with the cache option no_deep_copy detailed in "OPTIONS" below.

$expires_in indicates the time in seconds until this data should be erased, or the constant $EXPIRES_NOW, or the constant $EXPIRES_NEVER. Defaults to $EXPIRES_NEVER. This variable can also be in the extended format of "[number] [unit]", e.g., "10 minutes". The valid units are s, second, seconds, sec, m, minute, minutes, min, h, hour, hours, d, day, days, w, week, weeks, M, month, months, y, year, and years. Additionally, $EXPIRES_NOW can be represented as "now" and $EXPIRES_NEVER can be represented as "never".

$expires_in is silently ignored (future versions may warn) if the cache didn't choose a 'time' pruning or validity policy at setup.

Any additional args will be passed on to the policies chosen at setup time (and documented by those policy modules.)

IMPORTANT: The positional args version of this method is only available if the compat flag positional_set was supplied as an option when the cache was created.

If positional_set is a true value but not set to 'auto' then the hash format is disabled and set() acts as if it is always given positional args - this will do unwanted things if you pass it hash args.

If positional_set was given 'auto' as a value then set() will attempt to auto-detect when you're supplying positional args and when you're supplying hash args, it does this by the rather-breakable means of asking if the first arg is called 'key', if so then it assumes you're passing a hash, otherwise it'll fall back to using positional args.

Examples:

  $cache->set(
      key        => 'customer',
      data       => 'Fred',
      expires_in => '10 minutes',
      );

  $created_at = time();
  $template = build_my_template( '/path/to/webpages/index.html' );
  $cache->set(
      key          => 'index',
      data         => $template,
      created_at   => $time,
      dependencies => [ '/path/to/webpages/index.html', ],
      );
$data = $cache->get( $key );

Gets the data associated with $key from the first storage policy that contains a fresh cached copy.

$cache->remove( $key );

Removes the data associated with $key from each of the storage policies in this cache.

$cache->delete( $key );

This is a convenience alias for $cache->remove( $key ).

$boolean = $cache->exists( $key );

Returns true if data associated with $key exists in the cache and false if there is no data associated with that key.

This method makes no assumption about the form of the data stored: if you store a value of undef you will still get a true return from $cache->exists().

$object = $cache->get_object( $key );

Returns the Cache::CacheFactory::Object used to store the underlying data associated with $key. This behaves much the same as the Cache::Object returned by Cache::Cache->get_object().

$cache->set_object( $key, $object );

Associates $key with Cache::CacheFactory::Object $object. If you supply a Cache::Object in $object instead, Cache::CacheFactory will create a new Cache::CacheFactory::Object instance as a copy before storing the copy.

@keys = $cache->get_keys();

Returns a list of all keys in this instance's namespace across all storage policies.

@keys = $cache->get_identifiers();

This method is deprecated. Behaves identically to $cache->get_keys(), use that instead. Provided only for backwards compatibility.

$cache->set_namespace( $namespace );

Sets the cache's namespace as per the namespace option. This does NOT move any existing cache contents over to the new namespace, it simply points the cache object at the new namespace.

$namespace = $cache->get_namespace();

Returns the current namespace as set either by $cache->set_namespace() or the namespace option.

$cache->Clear();

Clears all caches using each of the storage policies. This does not just clear caches with the exact same policies: it calls Clear() on each policy in turn.

$cache->clear();

Removes all cached data for this instance's namespace from each of the storage policies.

$cache->Purge();

COMPAT BUSTER: Purge() now does the same thing as purge() since it isn't clear quite what it should do with multiple caches with different pruning and storage policies. Its use is strongly deprecated.

$cache->purge();

Applies the pruning policy to all data in this namespace.

$size = $cache->Size();

Returns the total size of all objects in all caches with any of the storage policies of this cache.

$size = $cache->size();

Returns the total size of all objects in this namespace in any of the storage policies of this cache.

@namespaces = $cache->get_namespaces();

Returns a list of all namespaces in any of the storage policies of this cache.

$cache->set_positional_set( 0 | 1 | 'auto' );
$positional_set = $cache->get_positional_set();

These two methods allow you to alter the behaviour of the positional_set compatibility option.

See the documentation on $cache->set() or "OPTIONS" for more information on this setting.

$cache->set_default_expires_in( $expires_in );
$expires_in = $cache->get_default_expires_in();

These two methods allow you to alter the expires_in compatibility option.

See the documentation on $cache->set() or "OPTIONS" for more information on this setting.

Note that when you have both a pruning and validity policy of 'time' the default_expires_in of the validity policy is returned in preference to the pruning policy. Both will most likely be identical unless you're intentionally setting them differently via the new API, in which case: use the new API to get the value you want.

$cache->set_last_auto_purge( 0 | 'now' | $seconds_since_epoch );
$seconds_since_epoch = $cache->get_last_auto_purge();

Sets or gets the timestamp of the last auto-purge.

See the documention for last_auto_purge in "OPTIONS" for further details.

$cache->set_auto_purge_on_set( 0 | 1 );
$cache->set_auto_purge_on_get( 0 | 1 );
$boolean = $cache->get_auto_purge_on_set();
$boolean = $cache->get_auto_purge_on_get();

Turns auto-purging on/off for $cache->set() or $cache->get(), or returns the current state of auto-purging for each.

See the documention for auto_purge_on_set and auto_purge_on_get in "OPTIONS" for further details.

$cache->set_auto_purge_interval( $seconds );
$cache->set_auto_purge_on_set_interval( $seconds );
$cache->set_auto_purge_on_get_interval( $seconds );
$seconds = $cache->get_auto_purge_interval();
$seconds = $cache->get_auto_purge_on_set_interval();
$seconds = $cache->get_auto_purge_on_get_interval();

Set or get the appropriate auto-purge interval as per the auto_purge_interval, auto_purge_on_set_interval or auto_purge_on_get_interval options.

Look at "OPTIONS" for further details.

$cache->limit_size( $size );

Only available if a pruning policy of 'size' has been set, this method will allow you to perform a one-off prune of the storage policies to $size size or below.

This behaves like the limit_size() method of Cache::SizeAwareCache.

NON-OBJECT-ORIENTATED FUNCTIONS ^

$policy = best_available_storage_policy( @policies );
$policy = best_available_pruning_policy( @policies );
$policy = best_available_validity_policy( @policies );

These helper functions take a list of policies in the order you prefer them and returns the first one that is installed on the running system. This is useful if you don't know which packages are installed on the target system and have a list of alternatives you want to check against.

For example:

  use Cache::CacheFactory qw/:best_available/;

  my $cache = Cache::CacheFactory->new(
      storage => best_available_storage_policy( qw/sharedmemory memory file/ ),
      );

This would produce either: a shared-memory cache if Cache::SharedMemoryCache was available, failing that it would try a memory cache if Cache::MemoryCache was available, and finally it would try Cache::FileCache if the other two failed.

By default these functions are not exported, you will need to supply :best_available on the use line to import them.

CONSTANTS ^

You can export the following constants:

$NO_MAX_SIZE

You can export this with use Cache::CacheFactory qw/$NO_MAX_SIZE/; and supply it to the max_size option of a 'size' pruning policy.

This value of $NO_MAX_SIZE is compatible with that defined by Cache::SizeAwareCache, so you can use either source.

See Cache::CacheFactory::Expiry::Size for further details.

OPTIONS ^

The following options may be passed to the new() constructor:

storage => $storage_policy
storage => { $storage_policy1 => $policy1_options, $storage_policy2 => $policy2_options, ... }
storage => [ $storage_policy1 => $policy1_options, $storage_policy2 => $policy2_options, ... ]
pruning => $pruning_policy
pruning => { $pruning_policy1 => $policy1_options, $pruning_policy2 => $policy2_options, ... }
pruning => [ $pruning_policy1 => $policy1_options, $pruning_policy2 => $policy2_options, ... ]
validity => $validity_policy
validity => { $validity_policy1 => $policy1_options, $validity_policy2 => $policy2_options, ... }
validity => [ $validity_policy1 => $policy1_options, $validity_policy2 => $policy2_options, ... ]

Chooses a storage, pruning, or validity policy (or policies) possibly passing in a hashref of options to each policy.

Passing a hashref of policies is probably a bad idea since you have no control over the order in which policies are processed, if you supply them as an arrayref then they will be run in order.

See "POLICIES" below for more information on policies.

namespace => $namespace

The namespace associated with this cache. Defaults to "Default" if not explicitly set. All keys are unique within a given namespace, you will risk key-clashes with other applications if you use a persistent or shared storage policy and do not set a namespace to something unique to do with your application.

auto_purge_on_set => 0 | 1
auto_purge_on_get => 0 | 1

If set to a true value turns auto-purging on, if set to a false value turns auto-purging off.

auto_purge_on_set determines if calling $cache->set() can trigger an auto-purge, and auto_purge_on_get does the same for $cache->get().

Since a purge can be an expensive operation you will usually want to enable only auto_purge_on_set if you're in the usual read-often write-seldom environment, although see the example below in auto_purge_interval for an alternative strategy.

auto_purge_interval => $interval
auto_purge_on_set_interval => $interval
auto_purge_on_get_interval => $interval

Sets the interval between auto-purges to $interval seconds.

When checking whether an auto-purge should occur, the last purge time is compared to the current time, if it is more than $interval seconds in the past, a new purge() will be triggered.

By use of auto_purge_on_set_interval and auto_purge_on_get_interval you can tune the interval independently for each.

This may be useful in some situations:

  my $cache = Cache::CacheFactory->new(
    storage => 'memory',
    pruning => { 'time' => { default_prune_after => '1 m' } },
    auto_purge_on_set => 1,
    auto_purge_on_get => 1,
    auto_purge_on_set_interval => 5,
    auto_purge_on_get_interval => 30,
    );

This will set a cache that prunes items older than 1 minute and will auto-purge after a $cache->set() if there hasn't been an auto-purge in the past 5 seconds. It will also auto-purge after a $cache->get() if there hasn't been an auto-purge in the past 30 seconds.

This means that the expense of the auto-purge will usually be added to the (relatively) expensive set() most of the time, and only delay the usually cheap get() if there hasn't been a recent set() to trigger the auto-purge.

auto_purge_interval sets both auto_purge_on_set_interval and auto_purge_on_get_interval to the same value.

Note that for the auto-purge intervals to be used you will need to turn on either auto_purge_on_set or auto_purge_on_get.

default_expires_in => $expiry_time

This option is for backwards compatibility with Cache::Cache.

If set it is passed on to the 'time' pruning and/or validity policy if you have chosen either of them.

WARNING: if you do NOT have an pruning or validity policy of 'time', this option is silently ignored. This may raise a warning in future versions.

You can also manipulate this option via $cache->set_default_expires_in() and $cache->get_default_expires_in() after cache creation.

positional_set => 0 | 1 | 'auto'

This option is for backwards compatibility with Cache::Cache.

If set to a true value that isn't 'auto' it indicates that $cache->set() should behave exactly as that in Cache::Cache, accepting only positional parameters. If you set this option you will be unable to supply parameters to policies other than expires_in to the 'time' pruning or validity policy.

If set to a value of 'auto' Cache::CacheFactory will attempt to auto-detect whether you're supplying positional or named parameters to $cache->set(). This mechanism is not very robust: it simply looks to see if the first parameter is the value 'key', if so it assumes you're supplying named parameters.

The default behaviour, or if you set positional_set to a false value, is to assume that named parameters are being supplied.

Generally speaking, if you know for sure that all your code is using positional parameters you should set it to true, if you know all your code is using the new named parameters syntax you should set it false (or leave it undefined), and if you're uncertain or migrating from one to the other, you should set it to 'auto' and be careful that you always supply the key param first.

You can also manipulate this option via $cache->set_positional_set() and $cache->get_positional_set() after cache creation.

last_auto_purge => 0 | 'now' | $seconds_since_epoch

This option grants you initial control of when the cache should consider the most recent auto-purge to have occurred, by default this is set to 0 meaning no auto-purge has occurred and one should run as soon as it is triggered.

If you set it to 'now' then the cache will "pretend" that an auto-purge occurred at the same time as the cache creation and won't run another until the auto-purge interval has expired (auto_purge_interval, auto_purge_on_set_interval, or auto_purge_on_get_interval as appropriate).

You can also supply a number of seconds since the epoch, as returned by time(), if you want more precise control - such as if your application stores the last auto-purge time in some external manner.

nonfatal_missing_policies => 0 | 1
nonwarning_missing_policies => 0 | 1

Setting nonfatal_missing_policies to a true value will suppress the default die behaviour when a requested policy is missing and will instead generate a warn.

If you also set nonwarning_missing_policies to a true value, this warn will also be surpressed.

no_deep_clone => 0 | 1

Setting no_deep_clone to a true value will prevent the default behaviour of taking a deep clone of the data provided to $cache->set().

This can be a performance gain if you don't need to be paranoid about the cache sharing references with whatever handed them in, or if you want to handle the cloning yourself within your application.

Regretfully no_deep_clone on the cache can only act in an advisory capacity to storage policies, they may choose to disregard the flag and many of the Cache::Cache modules will do just this. (Not unreasonably: they predate Cache::CacheFactory considerably.) Cache::CacheFactory tries hard to convince them to avoid taking clones but may or may not succeed depending on precisely what you're attempting, you'll have to suck it and see I'm afraid.

Using this option with a storage policy of 'memory' will provide you with similar behaviour to Cache::FastMemoryCache, with the exception that, unavoidably, a deep clone is always created on $cache->get(). If this is undesirable, install Cache::FastMemoryCache and use a storage policy of 'fastmemory' in conjunction with setting no_deep_clone.

POLICIES ^

There are three types of policy you can set: storage, pruning and validity.

Storage determines what mechanism is used to store the data.

Pruning determines what mechanism is used to reap or prune the cache.

Validity determines what mechanism is used to determine if a cache entry is still up-to-date.

Storage Policies

Some common storage policies:

file

Implemented using Cache::FileCache, this provides on-disk caching.

memory

Implemented using Cache::MemoryCache, this provides per-process in-memory caching.

sharedmemory

Implemented using Cache::SharedMemoryCache, this provides in-memory caching with the cache shared between processes.

fastmemory

Implemented using Cache::FastMemoryCache, this provides in-memory caching like the 'memory' policy but with all the deep-copies of data stripped out, best used in conjunction with the no_deep_clone option set on the cache.

null

Implemented using Cache::NullCache, this cache is used to provide a fake cache that never stores anything.

Pruning and Validity Policies

All pruning and validity policies are interchangable, the difference between the two is when the policy is applied:

A pruning policy is applied when you purge() or periodically if auto_purge_on_set or autopurge_on_get is set, it removes all entries that fail the policy from the cache. Note that an item can be eligible to be pruned but still be in the cache and fetched successfully from the cache - it won't be removed until purge() is called either manually or automatically.

A validity policy is applied when an entry is retreived to ensure that it's still valid (or fresh or up-to-date if you prefer). If the entry isn't still valid then it's ignored as if it was never in the cache. Unlike pruning, validity always applies - you will never be able to fetch an item from the cache if it is invalid according to the policies you have chosen.

A handy shorthand is that pruning determines how long we keep the data lying around in case we need it again, validity determines whether we trust that it's still accurate.

time

This provides pruning and validity policies similar to those built into Cache::Cache using the expires_at param.

It allows you to check for entries that are over a certain age.

size

This policy prunes the cache to attempt to keep it under a supplied size, much like Cache::SizeAwareFileCache and the other Cache::SizeAware* modules.

This policy probably doesn't make much sense as a validity policy, although you can use it.

lastmodified

This policy compares the created date of the cache entry to the last-modified date of a list of file dependencies.

If the create date is older than any of the file last-modified dates the entry is pruned or regarded as invalid.

This is useful if you have data compiled or parsed from source data-files that may change, such as HTML templates or XML files.

forever

This debugging policy never regards items as invalid or prunable, it's implemented as the default behaviour in Cache::CacheFactory::Expiry::Base.

WRITING NEW POLICIES ^

It's possible to write custom policy modules of your own, all policies are constructed using the Cache::CacheFactory::Storage or Cache::CacheFactory::Expiry class factories. Storage provides the storage policies and Expiry provides both the pruning and validity policies.

New storage policies should conform to the Cache::Cache API, in particular they need to implement set_object and get_object.

New expiry policies (both pruning and validity) should follow the API defined by Cache::CacheFactory::Expiry::Base, ideally by subclassing it.

Once you've written your new policy module you'll need to register it with Cache::CacheFactory as documented in Class::Factory, probably by placing one of the the following lines (depending on type) somewhere in your module:

  Cache::CacheFactory::Storage->register_factory_type(
      mypolicyname => 'MyModules::MyPolicyName' );

  Cache::CacheFactory::Expiry->register_factory_type(
      mypolicyname => 'MyModules::MyPolicyName' );

Then you just need to make sure that your application has a

  use MyModules::MyPolicyName;

before you ask Cache::CacheFactory to create a cache with 'mypolicyname' as a policy.

INTERNAL METHODS ^

The following methods are mostly for internal use, but may be useful to redefine if you're subclassing Cache::CacheFactory for some reason.

$object = $cache->new_cache_entry_object();

Returns a new and uninitialized object to use for a cache entry, by default this object will be a Cache::CacheFactory::Object, if for some reason you want to overrule that decision you can return your own object.

$cache->set_policy( $policytype, $policies );

Used as part of the new() constructor, this sets the policy type $policytype to use the policies defined in $policies, this may do strange things if you do it to an already used cache instance.

$cache->set_storage_policies( $policies );
$cache->set_pruning_policies( $policies );
$cache->set_validity_policies( $policies );

Convenience wrappers around set_policy.

$cache->get_policy_driver( $policytype, $policy );

Gets the driver object instance for the matching $policytype and $policy, useful if it has non-standard extensions to the API that you can't access through Cache::CacheFactory.

$cache->get_policy_drivers( $policytype );

Returns a hashref of policies to driver object instances for policy type $policytype, you should probably use get_policy_driver() instead to get a specific driver though.

$cache->foreach_policy( $policytype, $closure );

Runs the closure/coderef $closure over each policy of type $policytype supplying args: Cache::CacheFactory instance, policy name, and policy driver.

The closure is run over each policy in order, or until the closure calls the last() method on the Cache::CacheFactory instance.

  use Data::Dumper;
  use Cache::CacheFactory;

  $cache = Cache::CacheFactory->new( ... );
  $cache->foreach_policy( 'storage',
      sub
      {
          my ( $cache, $policy, $driver ) = @_;

          print "Storage policy '$policy' has driver: ",
              Data::Dumper::Dumper( $driver ), "\n";
          return $cache->last() if $policy eq 'file';
      } );

This will print the policy name and driver object for each storage policy in turn until it encounters a 'file' policy.

$cache->foreach_driver( $policytype, $method, @args );

Much like foreach_policy() above, this method iterates over each policy, this time invoking method $method on the driver with the arguments specified in @args.

  $cache->foreach_driver( 'storage', 'do_something', 'with', 'args' );

will call:

  $driver->do_something( 'with', 'args' );

on each storage driver in turn.

The return value of the method called is discarded, if it's important to you then you should use foreach_policy and call the method on the driver arg provided, collating the results however you wish.

$cache->last();

Indicates that foreach_policy() or foreach_driver should exit at the end of the current iteration. last() does NOT exit your closure for you, if you want it to behave like perl's last construct you will want to do return $cache->last().

$cache->auto_purge( 'set' | 'get' );

Attempts an auto-purge according to the auto_purge_on_set and auto_purge_on_get settings and the $cache->get_last_auto_purge() value.

$cache->error( $error_message );

Raise a fatal error with message given by $error_message.

$cache->warning( $warning_message );

Raise a warning with message given by $warning_message.

KNOWN ISSUES AND BUGS ^

Pruning and validity policies are per-cache rather than per-storage

Pruning and validity policies are set on a per-cache basis rather than on a per-storage-policy basis, this makes multiple storage policies largely pointless for most purposes where you'd find it useful.

If you wanted the cache to transparently use a small fast memory cache first and fall back to a larger slower file cache as backup: you can't do it, because the size pruning policy would be the same for both storage policies.

About the only current use of multiple storage policies is to have a memory cache and a file cache so that processes that haven't pulled a copy into their memory cache yet will retreive it from the copy another process has placed in the file cache. This might be slightly more useful than a shared memory cache since the on-file cache will persist even if there's no running processes unlike the shared memory cache.

Per-storage pruning and validity settings may make it into a future version if they prove useful and won't over-complicate matters - for now it's best to create a wrapper module that internally creates the caches seperately but presents the Cache::Cache API externally.

Add/replace aren't atomic

The $cache->add/replace() methods aren't atomic, this mostly defeats their purpose in a shared-cache situation. This could be considered a bug.

Aren't there a million Cache::Cache replacements already?

At the time I started writing Cache::CacheFactory I'd been trying to find a caching solution that had the combination of features I needed, I had no luck in finding one.

Since then I've found a couple of other similar modules, and more have been written and released, you may or may not find them suiting your needs more closely, so I suggest taking a good look:

CHI - this module appears to have much the same motivation and strategy as Cache::CacheFactory in terms of storage policies, however, from what I can gather, it doesn't appear to split validity/pruning policies into seperate and/or combinable modules.

Cache - not sure how I missed this one when I was researching, it's a mature module that gives you flexibile validity/pruning policies but doesn't have such a wide range of storage policies available.

Please note that these descriptions are from my own imperfect understanding of the modules concerned, by no means take them as an authorative description of their functionality. Please feel free to contact me if I've included any inaccuracies. :)

SEE ALSO ^

Cache::Cache, Cache::CacheFactory::Object, Cache::CacheFactory::Expiry::Base, Cache::CacheFactory::Expiry::Time, Cache::CacheFactory::Expiry::Size, Cache::CacheFactory::Expiry::LastModified, Cache::FastMemoryCache

SUPPORT ^

You can find documentation for this module with the perldoc command.

    perldoc Cache::CacheFactory

You can also look for information at:

AUTHORS ^

Original author: Sam Graham <libcache-cachefactory-perl BLAHBLAH illusori.co.uk>

Last author: $Author: illusori $

ACKNOWLEDGEMENTS ^

DeWitt Clinton for the original Cache::Cache, most of the hard work is done by this module and its subclasses.

Chris Winters for Class::Factory, saving me the trouble of finding out what policy modules are or aren't available.

John Millaway for Cache::FastMemoryCache, which inspired the no_deep_clone option.

COPYRIGHT & LICENSE ^

Copyright 2008-2010 Sam Graham, all rights reserved.

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

syntax highlighting: