NAME

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

DESCRIPTION

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

PLUGINS

Plugins can modify any of the manager (Starch::Manager), state (Starch::State), and store (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 Starch::Plugin::CookieArgs for a basic example plugin. See "PLUGINS" in Starch for more existing plugins.

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

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

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 "set", "get", and "remove" methods and consume the 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 DBI or CHI.

The Starch::Store::CHI store is a good example store to use for building new store classes. See "STORES" in Starch for more existing stores.

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

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

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 "stringify_key" in Starch::Store.

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

set

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

get

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

remove

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

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 Starch::Plugin::LogStoreExceptions plugin.

See "THROWING EXCEPTIONS" for how to throw exceptions for known errors.

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, "can_reap_expired" in Starch::Store and "reap_expired" in Starch::Store.

    sub can_reap_expired { 1 }
    
    sub reap_expired {
        my ($self) = @_;
        ...
    }

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.

TESTING

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

THROWING EXCEPTIONS

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

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

EXTENDING BUILD

The 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 Starch::Manager class, the Starch::State class, and the Starch::Store role declare BUILD methods. Any plugin or store which wishes to extend the BUILD method should do so via an after method modifier as in:

    after BUILD => sub{
        ...
    };

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.

  1. 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.

  2. factory - The factory handles constructing anonymous classes from base classes and roles.

  3. 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.

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

SUPPORT

See "SUPPORT" in Starch.

AUTHORS

See "AUTHORS" in Starch.

COPYRIGHT AND LICENSE

See "COPYRIGHT AND LICENSE" in Starch.