Module-Generic
view release on metacpan or search on metacpan
lib/Module/Generic.pm view on Meta::CPAN
};
}
if( $@ )
{
CORE::warn( $@ ) unless( $SILENT_AUTOLOAD );
}
}
}
sub new
{
my $that = shift( @_ );
my $class = ref( $that ) || $that;
my $self = {};
no strict 'refs';
if( defined( ${ "${class}\::OBJECT_PERMS" } ) )
{
$that->_load_class( 'Module::Generic::Tie' ) || return( $that->pass_error );
my %hash = ();
my $obj = tie(
%hash,
'Module::Generic::Tie',
'pkg' => [ __PACKAGE__, $class ],
'perms' => ${ "${class}::OBJECT_PERMS" },
);
$self = \%hash;
}
bless( $self, $class );
if( defined( ${ "${class}\::LOG_DEBUG" } ) )
{
$self->{log_debug} = ${ "${class}::LOG_DEBUG" };
}
if( Wanted::want( 'OBJECT' ) )
{
return( $self->init( @_ ) );
}
my $new = $self->init( @_ );
# Returned undef; there was an error potentially
if( !defined( $new ) )
{
# If we are called on an object, we hand it the error so the caller can check it using the object:
# my $new = $old->new || die( $old->error );
if( $self->_is_object( $that ) && $that->can( 'pass_error' ) )
{
return( $that->pass_error( $self->error ) );
}
else
{
return( $self->pass_error );
}
};
return( $new );
}
sub new_glob
{
my $that = shift( @_ );
my $class = ref( $that ) || $that;
no warnings 'once';
my $self = bless( \do{ local *FH } => $class );
*$self = {};
no strict 'refs';
if( defined( ${ "${class}\::LOG_DEBUG" } ) )
{
*$self->{log_debug} = ${ "${class}::LOG_DEBUG" };
}
if( Wanted::want( 'OBJECT' ) )
{
return( $self->init( @_ ) );
}
my $new = $self->init( @_ );
if( !defined( $new ) )
{
# If we are called on an object, we hand it the error so the caller can check it using the object:
# my $new = $old->new || die( $old->error );
if( $self->_is_object( $that ) && $that->can( 'pass_error' ) )
{
return( $that->pass_error( $self->error ) );
}
else
{
return( $self->pass_error );
}
};
return( $new );
}
sub clear_error
{
my $self = shift( @_ );
my $class = ref( $self ) || $self;
my $this = $self->_obj2h;
no strict 'refs';
my $err_key = HAS_THREADS() ? join( ';', $class, $$, threads->tid ) : join( ';', $class, $$ );
$this->{error} = '';
my $repo = Module::Generic::Global->new( 'errors' => $class, key => $err_key );
$repo->remove;
${ $class . '::ERROR' } = '';
return( $self );
}
sub deserialise
{
my $self = shift( @_ );
my $data;
$data = shift( @_ ) if( scalar( @_ ) && ( @_ % 2 ) );
my $opts = $self->_get_args_as_hash( @_ );
$opts->{base64} //= '';
$opts->{data} = $data if( defined( $data ) && length( $data ) );
my $this = $self->_obj2h;
my $class = $opts->{serialiser} || $opts->{serializer} || $SERIALISER;
return( $self->error( "No serialiser class was provided nor set in \$Module::Generic::SERIALISER" ) ) if( !defined( $class ) || !length( $class ) );
# Well, nothing to do
if( ( !defined( $opts->{file} ) || !length( $opts->{file} ) ) &&
( !defined( $opts->{io} ) || !length( $opts->{io} ) ) &&
( !defined( $opts->{data} ) || !length( $opts->{data} ) ) )
{
return( '' );
lib/Module/Generic.pm view on Meta::CPAN
no strict 'refs';
if( $this->{verbose} || $this->{debug} || ${ $class . '::DEBUG' } )
{
my $level = ( $_[0] =~ /^\d+$/ ? shift( @_ ) : undef() );
my $opts = {};
if( scalar( @_ ) > 1 &&
ref( $_[-1] ) eq 'HASH' &&
(
CORE::exists( $_[-1]->{level} ) ||
CORE::exists( $_[-1]->{type} ) ||
CORE::exists( $_[-1]->{message} ) ||
CORE::exists( $_[-1]->{colour} )
) )
{
$opts = pop( @_ );
}
$level = $opts->{level} if( !defined( $level ) && CORE::exists( $opts->{level} ) );
my( $ref, $fmt );
if( $opts->{message} )
{
if( ref( $opts->{message} ) eq 'ARRAY' )
{
$ref = $opts->{message};
$fmt = shift( @$ref );
}
else
{
$fmt = $opts->{message};
$ref = \@_;
}
}
else
{
$ref = \@_;
$fmt = shift( @$ref );
}
my $txt = sprintf( $fmt, map( ( ref( $_ ) eq 'CODE' && !$this->{_msg_no_exec_sub} ) ? $_->() : $_, @$ref ) );
$txt = $self->colour_parse( $txt ) if( $opts->{colour} );
$opts->{message} = $txt;
$opts->{level} = $level if( defined( $level ) );
return( $self->_message( ( $level || 0 ), $opts ) );
}
return(1);
}
sub _obj2h
{
my $self = shift( @_ );
# The method that called message was itself called using the package name like My::Package->some_method
# We are going to check if global $DEBUG or $VERBOSE variables are set and create the related debug and verbose entry into the hash we return
no strict 'refs';
if( !ref( $self ) )
{
my $class = $self;
my $hash =
{
debug => ${ "${class}\::DEBUG" },
verbose => ${ "${class}\::VERBOSE" },
error => ${ "${class}\::ERROR" },
};
return( bless( $hash => $class ) );
}
elsif( ( Scalar::Util::reftype( $self ) // '' ) eq 'HASH' )
{
return( $self );
}
elsif( ( Scalar::Util::reftype( $self ) // '' ) eq 'GLOB' )
{
if( ref( *$self ) eq 'HASH' )
{
return( *$self );
}
else
{
return( \%{*$self} );
}
}
# Because object may be accessed as My::Package->method or My::Package::method
# there is not always an object available, so we need to fake it to avoid error
# This is primarly itended for generic methods error(), errstr() to work under any conditions.
else
{
return( {} );
}
}
sub _refaddr { return( Scalar::Util::refaddr( $_[1] ) ); }
sub _set_get
{
my $self = shift( @_ );
my $field = shift( @_ );
my $this = $self->_obj2h;
my $data = $this->{_data_repo} ? $this->{ $this->{_data_repo} } : $this;
my $callbacks = {};
my $check;
my $def = {};
if( ref( $field ) eq 'HASH' )
{
$def = $field;
if( CORE::exists( $def->{field} ) &&
defined( $def->{field} ) &&
CORE::length( $def->{field} ) )
{
$field = $def->{field};
}
else
{
return( $self->error( "No field name was provided." ) );
}
$callbacks = $def->{callbacks} if( CORE::exists( $def->{callbacks} ) && ref( $def->{callbacks} ) eq 'HASH' );
$check = $def->{check} if( CORE::exists( $def->{check} ) && ref( $def->{check} ) eq 'CODE' );
}
if( @_ )
{
my $val = ( @_ == 1 ) ? shift( @_ ) : [ @_ ];
if( defined( $check ) )
{
# try-catch
lib/Module/Generic.pm view on Meta::CPAN
return( defined( ${ *{$glob}{$type} } ) ? 1 : 0 );
}
else
{
return( defined( *{$glob}{$type} ) ? 1 : 0 );
}
}
else
{
return( $type eq 'CODE' ? 1 : 0 );
}
}
PERL
# NOTE: _implement_freeze_thaw()
_implement_freeze_thaw => <<'PERL',
sub _implement_freeze_thaw
{
my $self = shift( @_ );
my @classes = @_;
foreach my $class ( @classes )
{
unless( defined( &{"${class}\::STORABLE_freeze"} ) )
{
no warnings 'once';
*{"${class}\::STORABLE_freeze"} = sub
{
my $self = CORE::shift( @_ );
my $serialiser = CORE::shift( @_ ) // '';
my $class = CORE::ref( $self );
my %hash = %$self;
# Return an array reference rather than a list so this works with Sereal and CBOR
CORE::return( [$class, \%hash] ) if( $serialiser eq 'Sereal' || $serialiser eq 'CBOR' );
# But Storable/Storable::Improved want a list with the first element being the serialised element
CORE::return( $class, \%hash );
};
}
unless( defined( &{"${class}\::STORABLE_thaw"} ) )
{
no warnings 'once';
*{"${class}\::STORABLE_thaw"} = sub
{
# STORABLE_thaw would issue $cloning as the 2nd argument, while CBOR would issue
# 'CBOR' as the second value.
my( $self, undef, @args ) = @_;
my $ref = ( CORE::scalar( @args ) == 1 && CORE::ref( $args[0] ) eq 'ARRAY' ) ? CORE::shift( @args ) : \@args;
my $class = ( CORE::defined( $ref ) && CORE::ref( $ref ) eq 'ARRAY' && CORE::scalar( @$ref ) > 1 ) ? CORE::shift( @$ref ) : ( CORE::ref( $self ) || $self );
my $hash = CORE::ref( $ref ) eq 'ARRAY' ? CORE::shift( @$ref ) : {};
my $new;
# Storable pattern requires to modify the object it created rather than returning a new one
if( CORE::ref( $self ) )
{
foreach( CORE::keys( %$hash ) )
{
$self->{ $_ } = CORE::delete( $hash->{ $_ } );
}
$new = $self;
}
else
{
$new = CORE::bless( $hash => $class );
}
CORE::return( $new );
};
}
unless( defined( &{"${class}\::FREEZE"} ) )
{
no warnings 'once';
*{"${class}::FREEZE"} = sub
{
my $self = CORE::shift( @_ );
my $serialiser = CORE::shift( @_ ) // '';
my @args = &{"${class}::STORABLE_freeze"}( $self );
CORE::return( \@args ) if( $serialiser eq 'Sereal' || $serialiser eq 'CBOR' );
CORE::return( @args );
};
}
unless( defined( &{"${class}::THAW"} ) )
{
no warnings 'once';
*{"${class}::THAW"} = sub
{
my( $self, undef, @args ) = @_;
my $ref = ( CORE::scalar( @args ) == 1 && CORE::ref( $args[0] ) eq 'ARRAY' ) ? CORE::shift( @args ) : \@args;
$self = bless( {} => $self ) unless( ref( $self ) );
CORE::return( &{"${class}::STORABLE_thaw"}( $self, undef, @$ref ) );
};
}
}
}
PERL
# NOTE: _is_empty()
_is_empty => <<'PERL',
sub _is_empty
{
my $self = shift( @_ );
return(1) if( !@_ );
return(1) if( !defined( $_[0] ) );
if( (
( Scalar::Util::reftype( $_[0] ) // '' ) eq 'SCALAR' &&
!CORE::length( ${$_[0]} // '' )
) ||
(
!ref( $_[0] ) &&
!CORE::length( $_[0] // '' )
) )
{
return(1);
}
elsif( ( Scalar::Util::reftype( $_[0] ) // '' ) eq 'ARRAY' &&
!scalar( @{$_[0]} ) )
{
return(1);
}
elsif( ( Scalar::Util::reftype( $_[0] ) // '' ) eq 'HASH' &&
Scalar::Util::blessed( $_[0] ) &&
$_[0]->can( 'is_empty' ) &&
( $_[0]->is_empty ? 1 : 0 ) )
{
lib/Module/Generic.pm view on Meta::CPAN
sub DEBUG
{
my $self = shift( @_ );
my $pkg = ref( $self ) || $self;
my $this = $self->_obj2h;
no strict 'refs';
return( ${ $pkg . '::DEBUG' } );
}
# NOTE: Works with CBOR and Sereal <https://metacpan.org/pod/Sereal::Encoder#FREEZE/THAW-CALLBACK-MECHANISM>
sub FREEZE
{
my $self = CORE::shift( @_ );
my $serialiser = CORE::shift( @_ ) // '';
my $class = CORE::ref( $self );
my $ref = $self->_obj2h;
my %hash = %$ref;
$hash{_is_glob} = ( Scalar::Util::reftype( $self ) // '' ) eq 'GLOB' ? 1 : 0;
# Return an array reference rather than a list so this works with Sereal and CBOR
# On or before Sereal version 4.023, Sereal did not support multiple values returned
CORE::return( [$class, \%hash] ) if( $serialiser eq 'Sereal' && Sereal::Encoder->VERSION <= version->parse( '4.023' ) );
# But Storable want a list with the first element being the serialised element
CORE::return( $class, \%hash );
}
# sub STORABLE_freeze { CORE::return( CORE::shift->FREEZE( @_ ) ); }
# sub STORABLE_thaw { CORE::return( CORE::shift->THAW( @_ ) ); }
# NOTE: Works with CBOR and Sereal <https://metacpan.org/pod/Sereal::Encoder#FREEZE/THAW-CALLBACK-MECHANISM>
sub THAW
{
my( $self, undef, @args ) = @_;
my $ref = ( CORE::scalar( @args ) == 1 && CORE::ref( $args[0] ) eq 'ARRAY' ) ? CORE::shift( @args ) : \@args;
my $class = ( CORE::defined( $ref ) && CORE::ref( $ref ) eq 'ARRAY' && CORE::scalar( @$ref ) > 1 ) ? CORE::shift( @$ref ) : ( CORE::ref( $self ) || $self );
my $hash = CORE::ref( $ref ) eq 'ARRAY' ? CORE::shift( @$ref ) : {};
my $is_glob = CORE::delete( $hash->{_is_glob} );
my $new;
if( $is_glob )
{
$new = CORE::ref( $self ) ? $self : $class->new_glob;
foreach( CORE::keys( %$hash ) )
{
*$new->{ $_ } = CORE::delete( $hash->{ $_ } );
}
}
else
{
# Storable pattern requires to modify the object it created rather than returning a new one
if( CORE::ref( $self ) )
{
foreach( CORE::keys( %$hash ) )
{
$self->{ $_ } = CORE::delete( $hash->{ $_ } );
}
$new = $self;
}
else
{
$new = bless( $hash => $class );
}
}
CORE::return( $new );
}
# NOTE: class function
# Used internally also in Module::Generic::Exception
sub UNIVERSAL::create_class
{
my $class = shift( @_ );
my $self = __PACKAGE__;
return( $self->error( "No class name was provided." ) ) if( $self->_is_empty( $class ) );
my $opts = $self->_get_args_as_hash( @_ );
my $parent = $opts->{extends} || $self;
my $parents = $self->_is_array( $parent ) ? $parent : [$parent];
for( my $i = 0; $i < scalar( @$parents ); $i++ )
{
my $this = $parents->[$i];
if( ref( $this ) )
{
return( $self->error( "The parent value provided (", overload::StrVal( $this // 'undef' ), ") is a reference, but not an object. You should provide a parent class as a string, or a blessed object." ) ) if( !Scalar::Util::blessed( $this ) ...
$parents->[$i] = ref( $this );
}
}
my $meths = CORE::delete( $opts->{method} ) // CORE::delete( $opts->{methods} );
my $is_loaded = $self->_is_class_loaded( $class );
if( $is_loaded )
{
# Make sure the parent is in the ISA
unless( scalar( grep( ( $_ // '' ) eq $parent, @{"${class}::ISA"} ) ) )
{
no strict 'refs';
unshift( @{"${class}::ISA"}, $parent );
}
return( $class );
}
my $map =
{
array => '_set_get_array',
# Alias for 'array_as_object'
array_object => '_set_get_array_as_object',
array_as_object => '_set_get_array_as_object',
boolean => '_set_get_boolean',
class => '_set_get_class',
class_array => '_set_get_class_array',
class_array_object => '_set_get_class_array_object',
code => '_set_get_code',
datetime => '_set_get_datetime',
decimal => '_set_get_number',
file => '_set_get_file',
float => '_set_get_number',
glob => '_set_get_glob',
hash => '_set_get_hash',
hash_as_object => '_set_get_hash_as_mix_object',
integer => '_set_get_number',
ip => '_set_get_ip',
long => '_set_get_number',
number => '_set_get_number',
object => '_set_get_object',
object_no_init => '_set_get_object_without_init',
( run in 0.346 second using v1.01-cache-2.11-cpan-0f795438458 )