.\" Automatically generated by Pod::Man 4.09 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .if !\nF .nr F 0 .if \nF>0 \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} .\} .\" ======================================================================== .\" .IX Title "CHI::Driver::Development 3pm" .TH CHI::Driver::Development 3pm "2017-07-31" "perl v5.26.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" CHI::Driver::Development \- Manual for developing new CHI drivers .SH "VERSION" .IX Header "VERSION" version 0.60 .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 4 \& package CHI::Driver::MyDriver; \& use Moo; \& use strict; \& use warnings; \& \& extends \*(AqCHI::Driver\*(Aq; \& \& has ...; \& \& sub fetch { \& my ( $self, $key ) = @_; \& \& } \& \& sub store { \& my ( $self, $key, $data[, $expires_in] ) = @_; \& \& } \& \& sub remove { \& my ( $self, $key ) = @_; \& \& } \& \& sub clear { \& my ($self) = @_; \& \& } \& \& sub get_keys { \& my ($self) = @_; \& \& } \& \& sub get_namespaces { \& my ($self) = @_; \& \& } .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This document describes how to implement a new \s-1CHI\s0 driver. .PP The easiest way to start is to look at existing drivers, such as CHI::Driver::Memory and CHI::Driver::FastMmap. .SH "NAMING" .IX Header "NAMING" If you are going to publicly release your driver, call it \&'CHI::Driver::\fIsomething\fR' so that users can create it with .PP .Vb 1 \& CHI\->new(driver => \*(AqI\*(Aq); .Ve .PP If it's an internal driver, you can call it whatever you like and create it like .PP .Vb 1 \& CHI\->new(driver => \*(Aq+My::Internal::CHI::Driver\*(Aq); .Ve .SH "MOO / MOOSE" .IX Header "MOO / MOOSE" \&\s-1CHI\s0 driver classes must be Moo or Moose based to be fully functional, since we use Moose roles to implement various features. For backward compatibility, non\-Moo/Moose drivers will still work at a basic level, but you will see an error if using any feature requiring a role. .PP All drivers must directly or indirectly extend CHI::Driver. .SH "NAMESPACE" .IX Header "NAMESPACE" All cache handles have an assigned namespace that you can access with \&\f(CW\*(C`$self\->namespace\*(C'\fR. You should use the namespace to partition your data store. That is, two cache objects with different namespaces should be able to access the same key without any collision. .PP Examples: .IP "\(bu" 4 The Memory driver uses a separate sub-hash inside its main memory hash for each namespace. .IP "\(bu" 4 The File driver uses a separate top-level directory for each namespace. .IP "\(bu" 4 The FastMmap driver uses a separate Cache::FastMmap file for each namespace. .SH "METHODS" .IX Header "METHODS" .SS "Required methods" .IX Subsection "Required methods" The following methods have no default implementation, and \s-1MUST\s0 be defined by your subclass: .ie n .IP "store ( $self, $key, $data[, $expires_in] )" 4 .el .IP "store ( \f(CW$self\fR, \f(CW$key\fR, \f(CW$data\fR[, \f(CW$expires_in\fR] )" 4 .IX Item "store ( $self, $key, $data[, $expires_in] )" Associate \fI\f(CI$data\fI\fR with \fI\f(CI$key\fI\fR in the namespace, overwriting any existing entry. Called by \*(L"set\*(R". \fI\f(CI$data\fI\fR will contain any necessary metadata, including expiration options, so you can just store it as a single block. .Sp \&\fI\f(CI$expires_in\fI\fR is optionally the number of seconds from now when the entry will expire. This will only be passed if \*(L"expires_on_backend\*(R" in \s-1CHI\s0 is set. If your driver does not support expiration, or if you'd rather just let \s-1CHI\s0 manage expiration, you can ignore this. .ie n .IP "fetch ( $self, $key )" 4 .el .IP "fetch ( \f(CW$self\fR, \f(CW$key\fR )" 4 .IX Item "fetch ( $self, $key )" Returns the data associated with \fI\f(CI$key\fI\fR in the namespace. Called by \*(L"get\*(R". The main CHI::Driver superclass will take care of extracting out metadata like expiration options and determining if the value has expired. .ie n .IP "remove ( $self, $key )" 4 .el .IP "remove ( \f(CW$self\fR, \f(CW$key\fR )" 4 .IX Item "remove ( $self, $key )" Remove the data associated with the \fI\f(CI$key\fI\fR in the namespace. .ie n .IP "clear ( $self )" 4 .el .IP "clear ( \f(CW$self\fR )" 4 .IX Item "clear ( $self )" Remove all data associated with the namespace. (Technically not required, but the default implementation, which iterates over all keys and calls \*(L"remove\*(R" for each, is very inefficient). .SS "Overridable methods" .IX Subsection "Overridable methods" The following methods have a default implementation, but \s-1MAY\s0 be overridden by your subclass: .ie n .IP "\s-1BUILD\s0 ( $self, $options )" 4 .el .IP "\s-1BUILD\s0 ( \f(CW$self\fR, \f(CW$options\fR )" 4 .IX Item "BUILD ( $self, $options )" Define the \s-1BUILD\s0 method if you want to process any options specific to your driver. This is a standard Moo/Moose feature. .ie n .IP "fetch_multi_hashref ( $keys )" 4 .el .IP "fetch_multi_hashref ( \f(CW$keys\fR )" 4 .IX Item "fetch_multi_hashref ( $keys )" Override this if you want to efficiently process multiple fetches. Return a hash reference from keys to fetched data. If a key is not available, it may be left out of the hash or paired with undef. The default method will iterate over \&\fI\f(CI$keys\fI\fR and call fetch for each. .Sp This method is called by get_multi_arrayref and get_multi_hashref. .ie n .IP "store_multi ( $key_data, $options )" 4 .el .IP "store_multi ( \f(CW$key_data\fR, \f(CW$options\fR )" 4 .IX Item "store_multi ( $key_data, $options )" Override this if you want to efficiently process multiple stores. \fI\f(CI$key_data\fI\fR is a hash of keys and data that should be stored. The default will iterate over \&\fI\f(CI$key_data\fI\fR and call store for each pair. .Sp This method is called by set_multi. .SS "Optional methods" .IX Subsection "Optional methods" The following methods have no default implementation, and \s-1MAY\s0 be defined by your subclass, but are not required for basic cache operations. .ie n .IP "get_keys ( $self )" 4 .el .IP "get_keys ( \f(CW$self\fR )" 4 .IX Item "get_keys ( $self )" Return all keys in the namespace. It is acceptable to either include or omit expired keys. .ie n .IP "get_namespaces ( $self )" 4 .el .IP "get_namespaces ( \f(CW$self\fR )" 4 .IX Item "get_namespaces ( $self )" Return namespaces associated with the cache. It is acceptable to either include or omit namespaces with no valid keys. .SH "DISCARD POLICIES" .IX Header "DISCARD POLICIES" You can create new discard policies for size aware caches, to choose items to discard when the cache gets full. For example, the Memory driver implements an \s-1LRU\s0 policy. .PP To implement a discard policy \fIfoo\fR, define a subroutine \&\fIdiscard_policy_foo\fR, which takes a driver object and returns a closure that returns one key each time it is called. The closure should maintain state so that each key is only returned once. .PP For example, here's the Memory driver's \s-1LRU\s0 implementation. It utilizes a hash containing the last used time for each key. .PP .Vb 2 \& sub discard_policy_lru { \& my ($self) = @_; \& \& my $last_used_time = $self\->{metadata_for_namespace}\->{last_used_time}; \& my @keys_in_lru_order = \& sort { $last_used_time\->{$a} <=> $last_used_time\->{$b} } $self\->get_keys; \& return sub { \& shift(@keys_in_lru_order); \& }; \& } .Ve .PP You can set the default discard policy for your driver by overriding default_discard_policy; otherwise the default is 'arbitrary'. .PP .Vb 1 \& sub default_discard_policy { \*(Aqlru\*(Aq } .Ve .SH "TESTING" .IX Header "TESTING" \&\s-1CHI\s0 has a standard set of unit tests that should be used to ensure your driver is fully implementing the \s-1CHI API.\s0 .PP To use \s-1CHI\s0's tests (replacing \fIMyDriver\fR with the name of your driver): .IP "\(bu" 4 Install Test::Class and add it to the build dependencies for your distribution. .IP "\(bu" 4 Add a module called \fICHI::Driver::MyDriver::t::CHIDriverTests\fR to your distribution containing: .Sp .Vb 5 \& package CHI::Driver::MyDriver::t::CHIDriverTests; \& use strict; \& use warnings; \& use CHI::Test; \& use base qw(CHI::t::Driver); \& \& sub testing_driver_class { \*(AqCHI::Driver::MyDriver\*(Aq } \& \& sub new_cache_options { \& my $self = shift; \& \& return ( \& $self\->SUPER::new_cache_options(), \& \& # Any necessary CHI\->new parameters for your test driver \& ); \& } \& \& 1; .Ve .IP "\(bu" 4 Add a test script called \fIt/CHI\-driver\-tests.t\fR to your distribution containing: .Sp .Vb 5 \& #!perl \-w \& use strict; \& use warnings; \& use CHI::Driver::MyDriver::t::CHIDriverTests; \& CHI::Driver::MyDriver::t::CHIDriverTests\->runtests; .Ve .IP "\(bu" 4 You may need to override other methods in \&\fICHI::Driver::MyDriver::t::CHIDriverTests\fR, e.g. to skip tests that do not apply to your driver. See CHI::t::Driver::Memory and CHI::t::Driver::File in this distribution for examples. .SS "Test cleanup" .IX Subsection "Test cleanup" You are responsible for cleaning up your datastore after tests are done. The easiest way to do this is to place your datastore wholly inside a temporary directory, or use a guard to remove it at process end. .PP For example, the File, FastMmap, and \s-1DBI\s0 tests place all data inside a tempdir that is automatically cleaned up at process end. .SH "SEE ALSO" .IX Header "SEE ALSO" \&\s-1CHI\s0 .SH "AUTHOR" .IX Header "AUTHOR" Jonathan Swartz .SH "COPYRIGHT AND LICENSE" .IX Header "COPYRIGHT AND LICENSE" This software is copyright (c) 2012 by Jonathan Swartz. .PP This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.