File-DataClass

 view release on metacpan or  search on metacpan

lib/File/DataClass/Storage.pm  view on Meta::CPAN

package File::DataClass::Storage;

use namespace::autoclean;

use Class::Null;
use English                    qw( -no_match_vars );
use File::Copy;
use File::DataClass::Constants qw( EXCEPTION_CLASS FALSE NUL TRUE );
use File::DataClass::Functions qw( is_stale merge_file_data
                                   merge_for_update throw );
use File::DataClass::Types     qw( Bool HashRef Object Str );
use Scalar::Util               qw( blessed );
use Try::Tiny;
use Unexpected::Functions      qw( RecordAlreadyExists PathNotFound
                                   NothingUpdated Unspecified );
use Moo;

has 'atomic_write'  => is => 'ro', isa => Bool, default => TRUE;

has 'backup'        => is => 'ro', isa => Str,  default => NUL;

has 'encoding'      => is => 'ro', isa => Str,  default => NUL;

has 'extn'          => is => 'ro', isa => Str,  default => NUL;

has 'read_options'  => is => 'ro', isa => HashRef, builder => sub { {} };

has 'schema'        => is => 'ro', isa => Object,
   handles          => { _cache => 'cache', _lock  => 'lock',
                         _log   => 'log',   _perms => 'perms', },
   required         => TRUE,  weak_ref => TRUE;

has 'write_options' => is => 'ro', isa => HashRef, builder => sub { {} };

has '_locks'        => is => 'ro', isa => HashRef, builder => sub { {} };

# Private functions
my $_get_src_attributes = sub {
   my ($cond, $src) = @_;

   return grep { not m{ \A _ }mx
                 and $_ ne 'id' and $_ ne 'name'
                 and $cond->( $_ ) } keys %{ $src };
};

my $_lock_set = sub {
   $_[ 0 ]->_lock->set( k => $_[ 1 ] ); $_[ 0 ]->_locks->{ $_[ 1 ] } = TRUE;
};

my $_lock_reset = sub {
   $_[ 0 ]->_lock->reset( k => $_[ 1 ] ); delete $_[ 0 ]->_locks->{ $_[ 1 ] };
};

my $_lock_reset_all = sub {
   my $self = shift;

   eval { $self->$_lock_reset( $_ ) } for (keys %{ $self->_locks });

   return;
};

# Public methods
sub create_or_update {
   my ($self, $path, $result, $updating, $cond) = @_;

   my $rsrc_name = $result->result_source->name;

   $self->validate_params( $path, $rsrc_name ); my $updated;

   my $data = ($self->read_file( $path, TRUE ))[ 0 ];

   try {
      my $filter = sub { $_get_src_attributes->( $cond, $_[ 0 ] ) };
      my $id     = $result->id; $data->{ $rsrc_name } //= {};

      not $updating and exists $data->{ $rsrc_name }->{ $id }
         and throw RecordAlreadyExists, [ $path, $id ], level => 2;

      $updated = merge_for_update
         ( \$data->{ $rsrc_name }->{ $id }, $result, $filter );
   }
   catch { $self->$_lock_reset( $path ); throw $_ };

   if ($updated) { $self->write_file( $path, $data, not $updating ) }
   else { $self->$_lock_reset( $path ) }

   return $updated ? $result : FALSE;
}

sub delete {
   my ($self, $path, $result) = @_;

   my $rsrc_name = $result->result_source->name;

   $self->validate_params( $path, $rsrc_name );



( run in 0.960 second using v1.01-cache-2.11-cpan-0bb4e1dffa6 )