
=pod

=head1 NAME

Starch::Manual - Implementation independent persistent statefulness.

=head1 DESCRIPTION

Welcome to Starch, an implementation agnostic state persistence
manager.  Starch exposes stateful buckets of arbitrary information
which are tracked using some implementation specific technology, such
as HTTP cookies.  While this module's uses extend far beyond simply making
HTTP requests stateful, this documentation uses the HTTP paradigm
to explain how Starch works.

Typically you'll be integrating Starch with a web framework such as
L<Catalyst> (see L</INTEGRATIONS> for other frameworks).  Whichever
integration you are using it will likely pass-through arguments to
the underlying L<Starch::Manager> object and provide a thin layer over
the L<Starch::State> objects.  This documentation will be using
the core Starch classes for examples.  Your integration layer may
change, or completely replace, the interface documented here.

Starch has several design philosophies:

=over

=item *

Is as fast as possible by limiting method calls, implementing
lazy-loading wherever it can be done, and using libraries which
exhibit run-time efficiencies which beat out their competitors.

=item *

Reduces data store reads and writes to just the most essential.

=item *

Is independent from any particular framework (such as Catalyst or
Plack).

=item *

Provides a straight-forward and powerful mechanism for customizing just
about any part of Starch via stores and plugin bundles.

=item *

Is easy to understand due to everything being well documented,
hyper-linked, and containing thorough examples and tests.

=back

There are many L</ALTERNATIVES> to Starch to choose from, all of which
Starch was inspired from and may be a superior choice to.

=head1 BASIC USAGE

When setting up you need to, at a minimum, define a store:

    my $starch = Starch->new(
        store => { class=>'::Memory' },
    );

A store is a hash ref of arguments which are used for constructing the
store object.  A store object implements a very simple interface for
setting, getting, and removing state data.  Beyond defining the
store you will not be interacting with it as the L<Starch::State>
objects do all the store interaction for you.

When defining the store you must specify at least the C<class>
argument which determines the store class to use.  This class name
can be relative to C<Starch::Store> so that if you specify
C<::Memory>, as in the example above, it will be resolved to the
L<Starch::Store::Memory> class.  An absolute store class name
may be used without the leading C<::> if you have a custom store in
a different namespace.

Calling the C<new> method on the C<Starch> package actually returns
a L<Starch::Manager> object, so refer to its documentation for details
on what arguments you can pass.

Now that you have the C<$starch> object you can create a state object:

    my $state = $starch->state();

This creates a new L<Starch::State> object which you can then
interact with:

    $state->data->{some_key} = 'some_value';

The L<Starch::State/data> attribute is a writeable hash ref
which can contain any data you want.  This is the data which will
be stored by, and retrieved from, the store.  Once you're done
making changes to the data, call save:

    $state->save();

This stores the state data in the store.

Each state gets assigned a state ID automatically which can be
used to retrieve the state data at a later time.  The state ID
is a randomly generated SHA-1 hex digest.

    my $id = $state->id();

To retrieve a previously saved state pass the state ID to the
L<Starch::Manager/state> method:

    my $state = $starch->state( $id );

And now you can access the data you previously saved:

    print $state->data->{some_key}; # "some_value"

Your framework integration, such as L<Catalyst::Plugin::Starch>,
will wrap up and hide away most of these details from you, but
it's still good to know what is happening behind the scenes.

=head1 EXPIRATION

Expiration can be specified globally, when instantiating the L<Starch::Manager>
object, per-state, and per-store.  The expires value has various properties
and behaviors that are important to understand:

=over

=item *

The C<expires> field is always specified as the number of seconds before
the state will expire.

=item *

The L<Starch::Manager> class accepts an C<expires> argument which is used
as the default expires for new state objects and used as the expiration
for cookies via L<Starch::Plugin::CookieArgs>.

=item *

States have a C<expires> argument which defaults to the value of
the global expires set in the L<Starch::Manager> object.  Each state
can then have their individual expire extended or reduced via the
L<Starch::State/set_expires> method.

=item *

Stores may have a C<max_expires> argument passed to them.  If the per-state
expires is larger than the store's max_expires then the state's expires will
be replaced with the store's max_expires when writing the data to the store.

=back

=head1 LOGGING

Starch has built-in logging facilities via L<Log::Any>.  By default,
nothing is logged.  Various plugins and stores do use logging, such
as the C<LogStoreExceptions> L<plugin|/PLUGINS>.

If you do not set up a log adapter then these log messages will disappear
into the void.  Read the L<Log::Any> documentation for instructions on
configuring an adapter to capture the log output.

The L<Starch::Plugin::Trace> plugin adds a bunch of additional
logging output useful for development.

=head1 METHOD PROXIES

The Starch manager (L<Starch::Manager>) and stores support method proxies
out of the box for all arguments passed to them.  A method proxy is
an array ref which is lightly inspired by JSON references.  This array
ref must have the string C<&proxy> as the first value, a package name
as the second value, a method name as the third value, and any number
of arguments to pass to the method after that:

    [ '&proxy', $package, $method, @args ]

Method proxies are really useful when you are configuring Starch from
static configuration where you cannot dynamically pass a value from Perl.

An example from L<Starch::Store::CHI> illustrates how this works:

    my $starch = Starch->new(
        store => {
            class => '::CHI',
            chi => ['&proxy', 'My::CHI::Builder', 'get_chi'],
        },
    );

This will cause C<My::CHI::Builder> to be loaded, if it hasn't already, and then
C<My::CHI::Builder-E<gt>get_chi()> will be called and the return value used as
the value for the C<chi> argument.

Another practical example of using this is with L<DBI> where normally
you would end up making a separate connection to your database for states.
If your state database is the same database as you use for other things
it may make sense to use the same C<$dbh> for both so that you do not
double the number of connections you are making to your database.

Method proxies can be used with the manager and store objects at any point in
their arguments.  For example, if you have Perl code that builds the Starch
configuration from the ground up you could:

    my $starch = Starch->new(
        [ '&proxy', 'My::Starch::Config', 'get_config' ],
    );

Which will call C<get_config> on the C<My::Starch::Config> package and use its
return value as the arguments for instantiating the Starch object.

=head1 PERFORMANCE

On a decently-specced developer laptop Starch adds, at most, one half of one
millisecond to every HTTP request.  This non-scientific benchmark was done using
the C<Memory> store and a contrived example of the typical use of a state as the
backend for an HTTP session.

Starch is meant to be as fast as possible while still being flexible.
Due to Starch avoiding dependencies, and having zero non-core XS dependencies,
there are still same areas which could be slightly faster.  At this time there
is one plugin which will provide a small performance gain L<Starch::Plugin::Sereal>.
Even then, the gain using this plugin will be in the order of a fraction of a
millisecond per each HTTP request.

Starch has gone through the wringer performance wise and there just are
not many performance gains to be eked out of Starch.  Instead you'll
likely find that your time in Starch is primarily spent in your store.
So, when setting up Starch, picking a store is the most important
decision you can make performance wise.

=head1 STORES

These stores are included with the C<Starch> distribution:

=over

=item *

L<Starch::Store::Layered>

=item *

L<Starch::Store::Memory>

=back

These stores are distributed separately on CPAN:

=over

=item *

L<Starch::Store::Amazon::DynamoDB>

=item *

L<Starch::Store::CHI> - This store is a meta-store which provides
access to many other stores such as L<CHI::Driver::Redis>,
L<CHI::Driver::BerkleyDB>, L<CHI::Driver::File>, L<CHI::Driver::FastMmap>,
L<CHI::Driver::Memcached>, and L<CHI::Driver::CacheCache>.

=item *

L<Starch::Store::DBI>

=item *

L<Starch::Store::DBIx::Connector>

=back

More third-party stores can be found on
L<meta::cpan|https://metacpan.org/search?q=Web%3A%3AStarch%3A%3AStore>.

=head1 PLUGINS

Plugins alter the behavior of the manager (L<Starch::Manager>),
state (L<Starch:State>), and store (L<Starch::Store>)
objects.  To use a plugin pass the C<plugins> argument when
creating your Starch object:

    my $starch = Starch->new(
        plugins => ['::Trace'],
        store => { ... },
        ...,
    );

These plugins are included with the C<Starch> distribution:

=over

=item *

L<Starch::Plugin::AlwaysLoad>

=item *

L<Starch::Plugin::CookieArgs>

=item *

L<Starch::Plugin::DisableStore>

=item *

L<Starch::Plugin::LogStoreExceptions>

=item *

L<Starch::Plugin::RenewExpiration>

=item *

L<Starch::Plugin::ThrottleStore>

=item *

L<Starch::Plugin::TimeoutStore>

=item *

L<Starch::Plugin::Trace>

=back

These plugins are distributed separately on CPAN:

=over

=item *

L<Starch::Plugin::Sereal>

=back

More third-party plugins can be found on
L<meta::cpan|https://metacpan.org/search?q=Web%3A%3AStarch%3A%3APlugin>.

=head1 INTEGRATIONS

The following Starch integrations are available:

=over

=item *

L<Catalyst::Plugin::Starch>

=back

Integrations for L<Plack>, L<Dancer2>, L<Mojolicious>, etc will
be developed as needed by the people that need them.

=head1 EXTENDING

Starch can be extended by plugins and stores.  See L<Starch::Extending>
for instructions on writing your own.

=head1 ALTERNATIVES

=over

=item *

L<CGI::Session>

=item *

L<Data::Session>

=item *

L<HTTP::Session>

=item *

L<Catalyst::Plugin::Session>

=item *

L<Plack::Middleware::Session>

=item *

L<Dancer::Session>

=item *

L<Mojolicious::Sessions>

=item *

L<MojoX::Session>

=back

=head1 DEPENDENCIES

The C<Starch> distribution is shipped with minimal dependencies
and with no non-core XS requirements.  This is important for many people.

=head1 SUPPORT

Please submit bugs and feature requests to the Starch GitHub issue tracker:

L<https://github.com/bluefeet/Starch/issues>

=cut

