package Starch::Store;
our $VERSION = '0.14';

=encoding utf8

=head1 NAME

Starch::Store - Base role for Starch stores.


This role defines an interfaces for Starch store classes.  Starch store
classes are meant to be thin wrappers around the store implementations
(such as DBI, CHI, etc).

See L<Starch/STORES> for instructions on using stores and a list of
available Starch stores.

See L<Starch::Extending/STORES> for instructions on writing your own stores.

This role adds support for method proxies to consuming classes as
described in L<Starch/METHOD PROXIES>.


use Starch::Util qw( croak );
use Types::Common::Numeric -types;
use Types::Common::String -types;
use Types::Standard -types;

use Moo::Role;
use strictures 2;
use namespace::clean;

with 'Starch::Role::Log';
with 'MooX::MethodProxyArgs';

requires qw(

# Declare BUILD so roles can apply method modifiers to it.
sub BUILD { }

around set => sub{
    my ($orig, $self, $id, $keys, $data, $expires) = @_;

    # Short-circuit set operations if the data should not be stoed.
    return if $data->{ $self->manager->no_store_state_key() };

    $expires = $self->calculate_expires( $expires );

    return $self->$orig( $id, $keys, $data, $expires );


=head2 manager

The L<Starch::Manager> object which is used by stores to
access configuration and create sub-stores (such as the Layered
store's outer and inner stores).  This is automatically set when
the stores are built by L<Starch::Factory>.


has manager => (
    is       => 'ro',
    isa      => InstanceOf[ 'Starch::Manager' ],
    required => 1,
    weak_ref => 1,
    handles  => ['factory'],


=head2 max_expires

Set the per-store maximum expires which will override the state's expires
if the state's expires is larger.


has max_expires => (
    is  => 'ro',
    isa => (PositiveOrZeroInt) | Undef,

=head2 key_separator

Used by L</stringify_key> to combine the state namespace
and ID.  Defaults to C<:>.


has key_separator => (
    is      => 'ro',
    isa     => NonEmptySimpleStr,
    default => ':',


=head2 can_reap_expired

Return true if the stores supports the L</reap_expired> method.


sub can_reap_expired { 0 }

=head2 short_store_class_name

Returns L<Starch::Role::Log/short_class_name> with the
C<Store::> prefix remove.


sub short_store_class_name {
    my ($self) = @_;
    my $class = $self->short_class_name();
    $class =~ s{^Store::}{};
    return $class;

=head1 METHODS

=head2 new_sub_store

Builds a new store object.  Any arguments passed will be
combined with the L</sub_store_args>.


sub new_sub_store {
    my $self = shift;

    my $args = $self->sub_store_args( @_ );

    return $self->factory->new_store( $args );

=head2 sub_store_args

Returns the arguments needed to create a sub-store.  Any arguments
passed will be combined with the default arguments.  The default
arguments will be L</manager> and L</max_expires> (if set).  More
arguments may be present if any plugins extend this method.


sub sub_store_args {
    my $self = shift;

    my $args = $self->BUILDARGS( @_ );

    return {
        manager       => $self->manager(),
        max_expires   => $self->max_expires(),
        key_separator => $self->key_separator(),

=head2 calculate_expires

Given an expires value this will calculate the expires that this store
should use considering what L</max_expires> is set to.


sub calculate_expires {
    my ($self, $expires) = @_;

    my $max_expires = $self->max_expires();
    return $expires if !defined $max_expires;

    return $max_expires if $expires > $max_expires;

    return $expires;

=head2 stringify_key

    my $store_key = $starch->stringify_key(

This method is used by stores that store and lookup data by
a string (all of them at this time).  It combines the state
ID with the L<Starch::Manager/namespace> of the key data for the store


sub stringify_key {
    my ($self, $id, $namespace) = @_;
    return join(

=head2 reap_expired

This triggers the store to find and delete all expired states.
This is meant to be used in an offline process, such as a cronjob,
as finding and deleting the states could take hours depending
on the amount of data and the storage engine's speed.

By default this method will throw an exception if the store does
not define its own reap method.  You can check if a store supports
this method by calling L</can_reap_expired>.


sub reap_expired {
    my ($self) = @_;

    croak sprintf(
        '%s does not support expired state reaping',


=head1 SUPPORT

See L<Starch/SUPPORT>.

=head1 AUTHORS

See L<Starch/AUTHORS>.