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

=encoding utf8

=head1 NAME

Starch::Manager - Entry point for accessing Starch state objects.


See L<Starch>.


This module provides a generic interface to managing the storage of
state data.

Typically you will be using the L<Starch> module to create this

This class supports method proxies as described in


use Digest::SHA qw( sha1_hex );
use Scalar::Util qw( refaddr );
use Starch::State;
use Starch::Util qw( croak );
use Storable qw( freeze dclone );
use Types::Common::Numeric -types;
use Types::Common::String -types;
use Types::Standard -types;

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

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

# Declare BUILD so roles can apply method modifiers to it.
sub BUILD {
    my ($self) = @_;

    # Get this built as early as possible.



=head2 store

The L<Starch::Store> storage backend to use for persisting the state
data.  A hashref must be passed and it is expected to contain at least a
C<class> key and will be converted into a store object automatically.

The C<class> can be fully qualified, or relative to the C<Starch::Store>
namespace.  A leading C<::> signifies that the store's package name is relative.

More information about stores can be found at L<Starch/STORES>.


has _store_arg => (
    is       => 'ro',
    isa      => HashRef,
    required => 1,
    init_arg => 'store',

has store => (
    is       => 'lazy',
    isa      => ConsumerOf[ 'Starch::Store' ],
    init_arg => undef,
sub _build_store {
    my ($self) = @_;

    my $store = $self->_store_arg();

    return $self->factory->new_store(
        manager => $self,


=head2 expires

How long, in seconds, a state should live after the last time it was
modified.  Defaults to C<60 * 60 * 2> (2 hours).

See L<Starch/EXPIRATION> for more information.


has expires => (
    is       => 'ro',
    isa      => PositiveOrZeroInt,
    default => 60 * 60 * 2, # 2 hours

=head2 plugins

Which plugins to apply to the Starch objects, specified as an array
ref of plugin names.  The plugin names can be fully qualified, or
relative to the C<Starch::Plugin> namespace.  A leading C<::> signifies
that the plugin's package name is relative.

Plugins can modify nearly any functionality in Starch.  More information
about plugins, as well as which plugins are available, can be found at


# This is a "virtual" argument of sorts handled in Starch->new.
# The plugins end up being stored in the factory object, not here.

=head2 namespace

The root array ref namespace to put starch data in.  In most cases this is
just prepended to the state ID and used as the key for storing the state
data.  Defaults to C<['starch-state']>.

If you are using the same store for independent application states you
may want to namespace them so that you can easly identify which application
a particular state belongs to when looking in the store.


has namespace => (
    is      => 'ro',
    isa     => ArrayRef[ NonEmptySimpleStr ],
    default => sub{ ['starch-state'] },

=head2 expires_state_key

The state key to store the L<Starch::State/expires>
value in.  Defaults to C<__STARCH_EXPIRES__>.


has expires_state_key => (
    is      => 'ro',
    isa     => NonEmptySimpleStr,
    default => '__STARCH_EXPIRES__',

=head2 modified_state_key

The state key to store the L<Starch::State/modified>
value in.  Defaults to C<__STARCH_MODIFIED__>.


has modified_state_key => (
    is      => 'ro',
    isa     => NonEmptySimpleStr,
    default => '__STARCH_MODIFIED__',

=head2 created_state_key

The state key to store the L<Starch::State/created>
value in.  Defaults to C<__STARCH_CREATED__>.


has created_state_key => (
    is      => 'ro',
    isa     => NonEmptySimpleStr,
    default => '__STARCH_CREATED__',

=head2 no_store_state_key

This key is used by stores to mark state data as not to be
stored.  Defaults to C<__STARCH_NO_STORE__>.

This is used by the L<Starch::Plugin::LogStoreExceptions> and
L<Starch::Plugin::ThrottleStore> plugins to avoid losing state
data in the store when errors or throttling is encountered.


has no_store_state_key => (
    is      => 'ro',
    isa     => NonEmptySimpleStr,
    default => '__STARCH_NO_STORE__',

=head2 dirty_state_key

This key is used to artificially mark as state as dirty by incrementing
the value of this key.  Used by L<Starch::State/mark_dirty>.


has dirty_state_key => (
    is      => 'ro',
    isa     => NonEmptySimpleStr,
    default => '__STARCH_DIRTY__',


=head2 factory

The L<Starch::Factory> object which applies plugins and handles the
construction of the manager, state, and store objects.


# This argument is always set by Starch->new().  So, to the end-user,
# this is an attribute not a required argument.
has factory => (
    is       => 'ro',
    isa      => InstanceOf[ 'Starch::Factory' ],
    required => 1,

=head2 state_id_type

The L<Type::Tiny> object to validate the state ID when L</state>
is called.  Defaults to L<NonEmptySimpleStr>.


sub state_id_type { NonEmptySimpleStr }

=head1 METHODS

=head2 state

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

Returns a new L<Starch::State> (or whatever L<Starch::Factory/state_class>
returns) object for the specified state ID.

If no ID is specified, or is undef, then an ID will be automatically generated.

Additional arguments can be passed after the ID argument.  These extra
arguments will be passed to the state object constructor.


sub state {
    my $self = shift;
    my $id = shift;

    croak 'Invalid Starch State ID: ' . $self->state_id_type->get_message( $id )
        if defined($id) and !$self->state_id_type->check( $id );

    my $class = $self->factory->state_class();

    my $extra_args = $class->BUILDARGS( @_ );

    return $class->new(
        manager => $self,
        defined($id) ? (id => $id) : (),

=head2 state_id_seed

Returns a fairly unique string used for seeding L<Starch::State/id>.


my $counter = 0;
sub state_id_seed {
    my ($self) = @_;
    return join( '', ++$counter, time, rand, $$, {}, refaddr($self) )

=head2 generate_state_id

Generates and returns a new state ID which is a SHA-1 hex
digest of calling L</state_id_seed>.


sub generate_state_id {
    my ($self) = @_;
    return sha1_hex( $self->state_id_seed() );

=head2 clone_data

Clones complex perl data structures.  Used internally to build
L<Starch::State/data> from L<Starch::State/original_data>.


sub clone_data {
    my ($self, $data) = @_;
    return dclone( $data );

=head2 is_data_diff

Given two bits of data (scalar, array ref, or hash ref) this returns
true if the data is different.  Used internally by L<Starch::State/is_dirty>.


sub is_data_diff {
    my ($self, $old, $new) = @_;

    local $Storable::canonical = 1;

    $old = freeze( $old );
    $new = freeze( $new );

    return 0 if $new eq $old;
    return 1;


=head1 SUPPORT

See L<Starch/SUPPORT>.

=head1 AUTHORS

See L<Starch/AUTHORS>.