
=pod

=head1 NAME

Starch::Extending - How to alter the behavior of Starch.

=head1 DESCRIPTION

Starch provides almost limitless ways to extend how it works.  This is
done by creating plugins and stores.

=head1 PLUGINS

Plugins can modify any of the manager (L<Starch::Manager>), state
(L<Starch::State>), and store (L<Starch::Store>) classes to extend
functionality.

A plugin may extend several of these classes, via plugin bundles, or just one.  A common
setup is a plugin bundle which adds arguments to the manager object and then adds logic
to the state objects which acts upon the arguments.

See L<Starch::Plugin::CookieArgs> for a basic example plugin.  See
L<Starch/PLUGINS> for more existing plugins.

See the SYNOPSIS in L<Starch::Plugin::Bundle>, L<Starch::Plugin::ForManager>,
L<Starch::Plugin::ForState>, and L<Starch::Plugin::ForStore> documentation
for some decent boilerplate for writing new plugins.

Plugins and plugin bundles are applied by L<Starch::Factory>, which itself
acts as a dynamic plugin bundle.

=head1 STORES

Stores provide the persistence layer for Starch by exposing a generic
interface on top of the driver code which communicates with the persistent
backend (such as a database).

A store must implement the L</set>, L</get>, and L</remove> methods and consume
the L<Starch::Store> role.  Writing new stores is generally a trivial process where
the store class does nothing more than glue those three methods with some underlying
implementation such as L<DBI> or L<CHI>.

The L<Starch::Store::CHI> store is a good example store to use for
building new store classes.  See L<Starch/STORES> for more existing stores.

Stores should be written so that the underlying driver object (the C<$dbh>
for a DBI store, for example) can be passed as an argument.   This allows
the user to utilize L<Starch/METHOD PROXIES> to build their own driver objects.

A state's expires duration is stored in the state data under the
L<Starch::Manager/expires_state_key>.  This should B<not> be considered
as anything meaningful to the store, since stores can have their
L<Starch::Store/max_expires> argument set which will automatically
change the value of the C<expiration> argument passed to C<set>.

=head2 Required Methods

Stores must implement three methods for setting, getting, and removing
state data.  These methods receive a state ID and a namespace array ref
as their first two arguments.  The combination of these two values should
identify a unique location in the store.  They can be combined to create
a single key string using L<Starch::Store/stringify_key>.

A more detailed description of the methods that a store must
implement:

=head3 set

Sets the data for the key.  The C<$expires> value will always be set and
will be either C<0> or a positive integer representing the number of seconds
in the future that this state data should be expired.  If C<0> then the
store may expire the data whenever it chooses.

=head3 get

Returns the data for the given key.  If the data was not found then
C<undef> is returned.

=head3 remove

Deletes the data for the key.  If the data does not exist then
this is just a no-op.

=head2 Loud Exceptions

Stores should detect issues and throw exceptions loudly.  If the user
would like to automatically turn store exceptions into log messages
they can use the L<Starch::Plugin::LogStoreExceptions> plugin.

See L</THROWING EXCEPTIONS> for how to throw exceptions for known errors.

=head2 Reaping Expired States

Stores may choose to support an interface for deleting old state data
suitable for a cronjob.  To do this two methods must be declared,
L</can_reap_expired> and L</reap_expires>.  See
L<Starch::Store::Amazon::DynamoDB> for an example of a store which
supports this feature.

The actual implementation of how to reap old state data is a per-store
and is something that will differ greatly between them.

Consider adding extra arguments to your store class to control how state
reaping functions.  For example, a DBI store may allow the user to reap
the states in batches, and a DynamoDB store may allow the user to specify
a secondary global index to do the scan on.



=head1 TESTING

All plugin and store modules should use L<Test::Starch> to ensure that
they have not broken the core functionality of Starch.

=head1 THROWING EXCEPTIONS

Exceptions for known issues should be thrown using L<Starch::Util/croak>
which automatically internalizes all installed C<Starch::> packages so that
the error will report the caller in the user's code.

    use Starch::Util qw( croak );
    ...
    croak 'foo';

=head1 EXTENDING BUILD

The L<Moo> BUILD method doesn't play well with roles.  If a role provides
a BUILD method and then the class which consumes the role also defines a
BUILD method the class will win and the role's BUILD method will
never be called.  To get around this the L<Starch::Manager> class, the
L<Starch::State> class, and the L<Starch::Store> role declare BUILD methods.
Any plugin or store which wishes to extend the BUILD method should do so
via an C<after> method modifier as in:

    after BUILD => sub{
        ...
    };

=head1 INTERNALS

                                                          +------+
                                                          v      |
    +------------------------+       +------------------------+  |
    |      1. manager        | ----> |       3. store         |--+
    |   (Starch::Manager)    | < - - |     (Starch::Store)    |
    +------------------------+       +------------------------+
                ^    |
                |    |
                |    +---------------------------+
                |                                V
    +------------------------+       +------------------------+
    |      4. state          |       |      2. factory        |
    |   (Starch::State)      |       |   (Starch::Factory)    |
    +------------------------+       +------------------------+

This diagram shows which objects hold references to other objects.  Solid
lines depict a hard reference while dashed lines depict a weak reference.
Weak references are used to avoid memory leaks.

=over

=item 1.

B<manager> - The manager is the entry point for configuration and retrieving
session objects.  It holds a strong reference to the factory and the store.
The manager doesn't have much logic in and of itself, as most of the compile-time
work is handled by the factory, and the runtime work by the sessions and store.

=item 2.

B<factory> - The factory handles constructing anonymous classes from base
classes and roles.

=item 3.

B<store> - The store does all of the backend heavy-lifting.  It holds a
weak reference to the manager so that it can get at configuration and
the factory object.  In the case of stores with sub-stores they may hold
references to other stores.

=item 4.

B<state> - The state provides the runtime public interface to the store
and other parts of starch.  It holds a strong reference to the manager.

=back

=head1 AUTHORS AND LICENSE

See L<Starch/AUTHOR>, L<Starch/CONTRIBUTORS>, and L<Starch/LICENSE>.

=cut

