Module::Generic(3)
π¦ Module::Generic π¦
Okay bestie, welcome to the user-contributed Perl documentation page for Module::Generic. The manual name is folded right in up here because we're skipping the boring header line. This module is literally designed for you to inherit from, and it's giving "I brought snacks and also a whole framework" energy. π
πΈ NAME πΈ
Module::Generic β Generic Module to inherit from
β¨ SYNOPSIS β¨
Lowkey the whole vibe is: make your own package, inherit from Module::Generic, and let it handle the repetitive bits. Here's the starter pack, exactly as the page gives it:
package MyModule;
BEGIN
{
use strict;
use warnings;
use parent qw( Module::Generic );
};
sub init
{
my $self = shift( @_ );
# Requires parameters provided to have their equivalent method
$self->{_init_strict_use_sub} = 1;
# Smartly accepts key-value pairs as list or hash reference
$self->SUPER::init( @_ );
# This won't be affected by parameters provided during instantiation
$self->{_private_param} = 'some value';
return( $self );
}
sub active { return( shift->_set_get_boolean( 'active', @_ ) ); }
sub address { return( shift->_set_get_object( 'address', 'My::Address', @_ ) ); }
sub age { return( shift->_set_get_number( 'age', @_ ) ); }
sub name { return( shift->_set_get_scalar( 'name', @_ ) ); }
sub uuid { return( shift->_set_get_uuid( 'uuid', @_ ) ); }
sub remote_addr { return( shift->_set_get_ip( 'remote_addr', @_ ) ); }
sub discount
{
return( shift->_set_get_class_array( 'discount',
{
amount => { type => 'number' },
discount => { type => 'object', class => 'My::Discount' },
}, @_ ) );
}
sub settings
{
return( shift->_set_get_class( 'settings',
{
# Will create a Module::Generic::Array array object of objects of class MY::Item
items => { type => 'object_array_object', class => 'My::Item' },
notify => { type => 'boolean' },
resumes_at => { type => 'datetime' },
timeout => { type => 'integer' },
customer => {
definition => {
billing_address => { package => "My::Address", type => "object" },
email => { type => "scalar" },
name => { type => "scalar" },
shipping_address => { package => "My::Address", type => "object" },
},
type => "class",
},
}, @_ ) );
}
Quick way to create a class with feature-rich methods:
create_class My::Package extends => 'Other::Package';
# or, maybe
create_class My::Package extends => 'Other::Package', method =>
{
since => 'datetime',
uri => 'uri',
tags => 'array_object',
meta => 'hash',
active => 'boolean',
callback => 'code',
config => 'file',
allowed_from => 'ip',
total => 'number',
id => 'uuid',
version => 'version',
filehandle => 'glob',
object => { type => 'object', class => 'Some::Class' },
customer =>
{
type => 'class',
def =>
{
id => 'uuid',
since => 'datetime',
name => 'scalar_as_object',
age => 'decimal',
}
}
};
my $obj = My::Package->new;
my $cust = $obj->customer(
name => 'John Doe',
age => 32,
since => 'now',
);
$obj->customer->id( '44d9a3ab-32e2-4c46-b6c9-8b307f273d47' );
π VERSION π
v1.1.3
π DESCRIPTION π
Module::Generic, as the name says it all, is a generic module to inherit from. It's designed to be fast and to provide a useful framework, so you can speed up coding and debugging. It contains standard and support methods that may be superseded by your module. Slay.
It also contains an AUTOLOAD that transforms any hash object key into dynamic methods, and it also recognizes the dynamic routine Γ la AutoLoader. The reason? While "AutoLoader" gives the user a convenient AUTOLOAD, the author wanted a way to also keep the functionality of Module::Generic AUTOLOAD that wasn't included in "AutoLoader". So the only solution was a merger. Iconic, honestly. β¨
π οΈ METHODS π οΈ
π¦ import
import() is used for the AutoLoader mechanism and hence is not a public method. It's just mentioned here for info only.
π± new
new will create a new object for the package, pass any argument it might receive to the special standard routine init that must exist. Then it returns whatever init returns.
To protect object inner content from sneaking by third party, you can declare the package global variable OBJECT_PERMS and give it a Unix permission, but only 1 digit. It will then work just like Unix permission. That is, if permission is 7, then only the module who generated the object may read/write content of the object. However, if you set 5, then other may look into the content of the object, but may not modify it. 7, as you would have guessed, allows other to modify the content of an object. If OBJECT_PERMS is not defined, the permissions system is not activated and hence anyone may access and possibly modify the content of your object. Guard that object, bestie. π
If the module runs under mod_perl, and assuming you have set the variable "GlobalRequest" in your Apache configuration, it is recognised and a clean up registered routine is declared to Apache to clean up the content of the object.
This method calls init, which does all the work of setting object properties and calling methods to that effect.
π§ as_hash
This will recursively transform the object into a hash suitable to be encoded in JSON.
It does this by calling each method of the object and building a hash reference with the method name as the key and the method's returned value as the value.
If the method's returned value is an object, it will call its as_hash method if it supports it.
It returns the hash reference built. π«Ά
π§½ clear
Alias for clear_error.
π§Ό clear_error
Clear all error from the object and from the available global variable $ERROR.
This is a handy method to use at the beginning of other methods of the calling package, so the end user may do a test such as:
$obj->some_method( 'some arguments' );
die( $obj->error() ) if( $obj->error() );
# some_method() would then contain something like:
sub some_method
{
my $self = shift( @_ );
## Clear all previous error, so we may set our own later one eventually
$self->clear_error();
# ...
}
This way the end user may be sure that if $obj->error() returns true, something wrong has occurred.
Note that all helper methods such as _set_get_* use this method when used as a mutator. This means that if those methods are used to set some value, and they do so successfully, they will reset any previous error. When used as an accessor, any previous error set will remain.
π¦ clone
Clone the current object if it is of type hash or array reference. It returns an error if the type is neither.
It returns the clone.
π colour_close
The marker to be used to set the closing of a command line colour sequence.
Defaults to ">".
π¨ colour_closest
Provided with a colour, this returns the closest standard one supported by the terminal.
A colour provided can be a colour name, or a 9 digits RGB value, or a hexadecimal value.
π colour_format
Provided with a hash reference of parameters, this will return a string properly formatted to display colours on the command line.
Parameters are:
"text"ormessageβ this is the text to be formatted in colour."bgcolour"orbgcolororbg_colourorbg_colorβ the value for the background colour."colour"orcolororfg_colourorfg_colororfgcolourorfgcolorβ the value for the foreground colour.- Valid value can be a colour name, an RGB value like
255255255, an RGB annotation like"rgb(255, 255, 255)", or an RGBA annotation like"rgba(255,255,255,0.5)". - A colour can be preceded by the words
"light"or"bright"to provide a slightly lighter colour where supported. - Similarly, if an RGBA value is provided, and the opacity is less than
1, this is equivalent to using the keyword"light". - It returns the text properly formatted to be outputted in a terminal.
- Valid value can be a colour name, an RGB value like
"style"β the possible values are:bold,italic,underline,blink,reverse,conceal,strike.
π colour_max_depth
Set or get the maximum level of recursion when processing colourised debugging message with message_colour.
π colour_open
The marker to be used to set the opening of a command line colour sequence.
Defaults to "<".
πͺ colour_parse
$self->colour_parse( "And {bold light red on white}what about{/} {underline yellow}me too{/} ?" );
$self->colour_parse( "And {style => 'i|b', color => green}what about{/} {style => 'blink', color => yellow}me{/} ?" );
Parses a string or list of strings containing colour and style formatting tags and returns the string with appropriate ANSI escape codes applied for terminal display. The method supports nested formatting, custom delimiters, and both named colours (e.g., "red", "light blue") and RGB/RGBA formats. It is designed to work in both TTY (terminal) and non-TTY environments, with formatting stripped in non-TTY contexts.
If multiple strings are provided, they are concatenated with no separator before processing.
For the purpose of this section, a "delimiter" is one or more characters that mark the start or end of a "tag", such as "{", "}", "[", or "]". A "tag" is a container of instructions for colour formatting. There are opening and closing tags, such as "{/}".
For example:
$self->colour_parse( "And {style => 'i|b', color => green}what about{/} {style => 'blink', color => yellow}me{/} ?" );
would result with the words "what about" in italic, bold and green colour, and the word "me" in yellow colour blinking (if supported).
Another way is:
$self->colour_parse( "And {bold light red on white}what about{/} {underline yellow}me too{/} ?" );
would return a string with the words "what about" in light red bold text on a white background, and the words "me too" in yellow with an underline.
$self->colour_parse( "Hello {bold red on white}everyone! This is {underline rgb(0,0,255)}embedded{/}{/} text..." );
would return a string with the words "everyone! This is" in bold red characters on a white background, and the word "embedded" in underlined blue colour.
The default prospective delimiters are "{", "}", "<", and ">". Those default delimiters can be overridden by setting alternate ones using the methods colour_open and colour_close.
okay so delimiters: they have to be non-empty scalar strings, and ngl they should not be identical to each other or things get ambiguous (you'll get a warning if you do π). Once a delimiter pair is picked for a string, it's picked β it gets used consistently throughout parsing, and every other delimiter candidate is ignored for that parse run. Mixed delimiters, like { and < together, are treated as literal content unless they match the current delimiter pair. periodt π
Tags β the ones made of an opening and a closing delimiter β are how you specify a colouring style, like bold, underline, and colours like red or rgb(255,255,255). And as shown up there βοΈ, colour_parse supports two syntaxes for colour parameters:
- natural β a concise format with styles and colours separated by spaces, optionally including a background colour with
on.
{bold light red on white}
- hash-like β Perl-like key-value pairs to specify styles and colours.
{style => 'bold', color => 'red'}
Only a subset of plain scalar values is allowed here, bestie. Attempting to use Perl expressions is not supported π ββοΈ. Invalid or unsafe input is treated as literal.
Tags must start with an alphabetic character (a style or colour name) immediately following the opening delimiter β no leading whitespace allowed. So {bold red} is valid, but { bold red} is just literal text, sorry:
{ bold red} # INVALID, literal
{bold red} # VALID
The method supports nested tags up to a maximum default depth of 10, but you can change that with the method colour_max_depth. It handles malformed tags gracefully by either ignoring them or treating them as literal text. And in non-TTY environments (influenced by force_tty or terminal detection with _is_tty), formatting tags get stripped and only the content is returned β¨
Perl variables like ${variable} are preserved as literal text, because the method makes sure that a { or [ preceded by a $ is never treated as a formatting tag. iconic behaviour honestly π«Ά
πΈ Supported Styles πΈ
The following styles are supported:
bold(orb,strong) β bold text (ANSI:\e[1m) πͺitalic(ori) β italic text (ANSI:\e[3m)underline(oru,underlined) β underlined text (ANSI:\e[4m)blinkβ blinking text (ANSI:\e[5m)reverse(orr,reversed) β reverse video (ANSI:\e[7m)conceal(orc,concealed) β concealed text (ANSI:\e[8m)strike(orstriked,striken) β strikethrough text (ANSI:\e[9m)
Multiple styles can be combined in key-value syntax using |, such as style => 'bold|italic'. slay β¨
π Supported colours π
Colours can be specified as:
- Named colours β e.g.
red,light blue,bright greenπ - RGB β e.g.
rgb(255,255,255)or rgb(255255255) (comma-less) - RGBA β e.g.
rgba(255,0,0,0.5)for 50% opacity, blended with the background (default: white) π¨
Both 8-bit (38;5;NNN or 48;5;NNN) and 24-bit (38;2;R;G;B or 48;2;R;G;B) ANSI codes are generated for compatibility. the way it does both ππ
π Delimiter Rules π
- Default delimiters are
{and}or<and>unless you override them withcolour_openandcolour_close. - Tags must start with a letter, such as
bold,red, or be a closing tag ({/}). - A
$prefix before{or[(e.g.${variable}) prevents interpretation as a tag. - Custom delimiters must be non-empty scalars and are used consistently within a single string.
- Mixed delimiters (e.g.
{bold red}A <underline green"/>) are treated as literal content within the chosen delimiter pair.
π Error Handling π
- Invalid tags (e.g.
{in valid}) are ignored, and their content is returned without formatting. - Stray closing tags (e.g.
{/}) are treated as literal text. - Unclosed tags (e.g.
{bold red}text) apply formatting to the remaining text. - Excessive nesting (beyond 10 levels) results in partial formatting up to the limit, with the remaining tags treated as literal.
- Out-of-range RGB values (e.g.
rgb(300,-1,260)) are ignored, and the tag is treated as literal.
π Examples π
my $m = Module::Generic->new;
- Basic formatting with key-value syntax π
$m->colour_parse( "Hello {style => 'bold', color => 'red'}world{/}" );
# Returns: "Hello \e[38;5;224;1m\e[38;2;255;0;0;1mworld\e[m"
- Compact syntax with background colour ποΈ
$m->colour_parse( "Text {bold light red on white}coloured{/} here" );
# Returns: "Text \e[38;5;224;48;5;255;1m\e[38;2;255;0;0;48;2;255;255;255;1mcoloured\e[m here"
- Nested formatting πͺ
$m->colour_parse( "Outer {bold red}inner {underline green}green{/} text{/}" );
# Returns: "Outer \e[38;5;224;1m\e[38;2;255;0;0;1minner \e[38;5;28;4m\e[38;2;0;255;0;4mgreen\e[m text\e[m"
- RGB and RGBA colours π
$m->colour_parse( "Blue {underline rgb(0,0,255)}text{/}" );
# Returns: "Blue \e[38;5;3;4m\e[38;2;0;0;255;4mtext\e[m"
$m->colour_parse( "Red {bold rgba(255,0,0,0.5)}text{/}" );
# Returns: "Red \e[38;5;237;1m\e[38;2;255;128;128;1mtext\e[m" (blended with white background)
- Preserving Perl variables and literal braces π§
$m->colour_parse( 'Code ${variable} here' );
# Returns: "Code ${variable} here"
$m->colour_parse( "{bold red}code { x: 1 } here{/}" );
# Returns: "\e[38;5;224;1m\e[38;2;255;0;0;1mcode { x: 1 } here\e[m"
- Malformed tags π
$m->colour_parse( "{in valid}text{/}" );
# Returns: "text"
$m->colour_parse( "{bold red}missing close" );
# Returns: "\e[38;5;224;1m\e[38;2;255;0;0;1mmissing close"
β οΈ bestie be careful: in this last example, if the colour formatting is not closed, the active colour formatting will affect any subsequent output lines. it just keeps going π
- Deep nesting π
$m->colour_parse( "{bold red}{underline green}{underline green}deep{/}{/}{/}" );
# Returns: "\e[38;5;224;1m\e[38;2;255;0;0;1m\e[38;5;28;4m\e[38;2;0;255;0;4m\e[38;5;28;4m\e[38;2;0;255;0;4mdeep\e[m\e[m\e[m"
- Custom delimiters π―
$m->colour_open( '[');
$m->colour_close(']]' );
$m->colour_parse( "Text [[bold red]]coloured[[/]]" );
# Returns: "Text \e[38;5;224;1m\e[38;2;255;0;0;1mcoloured\e[m"
- Non-TTY environment (formatting stripped) π«₯
my $m = Module::Generic->new( force_tty => 0 );
$m->colour_parse( "{bold red}text{/}" );
# Returns: "text"
Note that this method uses ANSI escape codes for formatting, which may not be supported by all terminals. not every terminal is built for this π
And, in non-TTY environments, all formatting is stripped, and only the content within tags is returned. This is particularly necessary if, instead of displaying those messages in a terminal, you redirect the output to a file. very "know your audience" of it β¨
This method returns a string with ANSI escape codes applied for valid formatting tags, or the content with tags stripped in non-TTY environments. Malformed or unrecognised tags are either ignored or treated as literal text.
See also Term::ANSIColor for ANSI escape code details. π
πΊ colour_to_rgb πΊ
Convert a human colour keyword like red, green into a rgb equivalent. so you can say the word instead of doing math in your head, we love that π
π coloured π
You give it a colouring preference expressed as the first argument as a string, followed by 1 or more arguments that get concatenated to form the text string to format. For example:
print( $o->coloured( 'bold white on red', "Hello it's me!\n" ) );
A colour can be expressed as a rgb, such as:
print( $o->coloured( 'underline rgb( 0, 0, 255 ) on white', "Hello everyone!" ), "\n" );
rgb can also be rgba, with the last decimal β normally an opacity β used here to set a light colour if the value is less than 1. For example:
print( $o->coloured( 'underline rgba(255, 0, 0, 0.5)', "Hello everyone!" ), "\n" );
β¨ debug β¨
Set or get the debug level. This takes and returns an integer.
Based on the value, message will or will not print out messages. For example:
$self->debug( 2 );
$self->message( 2, "Debugging message here." );
Since 2 used in message is equal to the debug value, the debugging message gets printed. π
If the debug value is switched to 1, the message will be silenced. π€«
π deserialise π
my $ref = $self->deserialise( %hash_of_options );
my $ref = $self->deserialise( $hash_reference_of_options );
my $ref = $self->deserialise( $serialised_data, %hash_of_options );
my $ref = $self->deserialise( $serialised_data, $hash_reference_of_options );
This method uses a specified serialiser class and deserialises the given data β either directly from a specified file or provided to it β and returns the perl data. πͺ
The serialisers currently supported are: CBOR::Free, CBOR::XS, JSON, Sereal and Storable::Improved (or the legacy Storable). They are not required by Module::Generic, so you have to install them yourself, bestie. If the serialiser you chose isn't installed, this sets an error and returns undef π
It takes a hash or hash reference of options. You can also provide the data to deserialise as the first argument, followed by a hash or hash reference of options.
It can then:
- retrieve data directly from File π
- retrieve data from a file handle (only with
Storable) - Return the deserialised data π
The supported options are:
base64β This can be set to a true value like1, or to your preferred base64 encoder/decoder, or to an array reference containing 2 code references β the first one for encoding and the second one for decoding. If it's simply set to a true value,deserialisewill call_has_base64to find out any installed base64 modules. Currently the ones supported are:Crypt::MiscandMIME::Base64. Of course, you need one of those modules installed first before it can be used. If this option is set and no appropriate module could be found,deserialisewill return an error. π ββοΈdataβ Data to be deserialised. π¦fileβ Provides a file path from which to read the serialised data. πioβ A file handle. This is used when the serialiser isStorableto call its functionstore_fdin Storable::Improved andfd_retrievein Storable::Improved. π«Άlockβ Boolean. If true, this will lock the file before reading from it. This works only in conjunction withfileand the serialiserStorable::Improved. πserialiserβ Specify the class name of the serialiser to use. Supported serialiser can either beCBORorCBOR::XS,SerealandStorable. π§¬
If the serialiser is CBOR::XS, these extra options are also supported: max_depth, max_size, allow_unknown, allow_sharing, allow_cycles, forbid_objects, pack_strings, text_keys, text_strings, validate_utf8, filter. Peep CBOR::XS for the deets on those. π
If the serialiser is Sereal, these extra options are supported too: refuse_snappy, refuse_objects, no_bless_objects, validate_utf8, max_recursion_depth, max_num_hash_entries, max_num_array_entries, max_string_length, max_uncompressed_size, incremental, alias_smallint, alias_varint_under, use_undef, set_readonly, set_readonly_scalars. Peep Sereal for the deets on those. πΈ
If an error happens, this sets an error and returns undef. π
deserialize
Just an alias for deserialise. Same vibes, different spelling. β¨
dump
You hand it some data, it hands you back a string representation of that data formatted by Data::Printer. Iconic. π
dump_hex
Returns a hexadecimal dump of the data you give it.
This needs the module Devel::Hexdump β if that module isn't found, it returns undef and sets an error. π
dump_print
Give it a file to write to plus some data, and it formats the string representation of the data with Data::Printer and saves it to that file. π«Ά
dumper
Give it some data, and optionally a hash reference of parameters as the last argument, and it builds a string representation of the data using Data::Dumper and returns it.
It sets Data::Dumper to be terse, to indent, to use qq, and optionally to not exceed a maximum depth if you passed one in the argument hash reference. π¦
dumpto
Alias for dumpto_dumper. π
printer
Same as dumper, except it uses Data::Printer to format the data. Lowkey the glow-up version. β¨
dumpto_printer
Same as dump_print above β which is literally an alias of this method. π
dumpto_dumper
Same as dumpto_printer above, but using Data::Dumper.
errno
Sets or gets an error number. That's it, that's the whole thing. π
error
my $o = Foo::Bar->new;
$o->do_something || return( $self->error( "Some error", "message." ) );
# or
$o->do_something || return( $self->error({
message => "Some error message.",
# will be loaded if necessary
class => 'My::Exception::Class',
# by default 'object' only
# it could also be simply 'all' to imply all the ones below
want => [qw( array code glob hash object scalar )],
debug => 4,
# code to execute upon error
callback => sub
{
# do some cleanup
$dbh->rollback if( $dbh->transaction );
},
# make it fatal
fatal => 1,
# When used inside an lvalue method
# lvalue => 1,
# assign => 1,
}) );
Okay so bestie, here's the deal: you give it a list of strings, or a hash reference of parameters, and it sets the current error by creating a Module::Generic::Exception object, calls warn in perlfunc β or $r-warn> if you're running under Apache2 modperl β and returns undef() in scalar context or an empty list in list context:
if( $some_condition )
{
return( $self->error( "Some error." ) );
}
And no, you do not have to stress about a trailing line feed sequence β error handles that for you. π«Ά
A script calling your module could write calls to your module methods like this:
my $cust_name = $object->customer->name ||
die( "Got an error in file ", $object->error->file, " at line ", $object->error->line, ": ", $object->error->trace, "\n" );
# or simply:
my $cust_name = $object->customer->name ||
die( "Got an error: ", $object->error, "\n" );
If you'd rather use a hash reference, you can pass the following parameters. Any other parameters get passed straight through to the exception class. π
assignβ Boolean. Set it to a true value if this is called inside an assign method, like one using lvalue.callbackβ Give it a code reference, like a reference to a subroutine. It's designed to be called when an error hits, so you can do some cleanup for example.classβ The package name or class used to instantiate the error object. By default it uses theModule::Generic::Exceptionclass, or whatever you set with the object property_exception_class.
$self->do_something_bad ||
return( $self->error({
code => 500,
message => "Oopsie",
class => "My::NoWayException",
}) );
my $exception = $self->error; # an My::NoWayException object
Heads up though: if that class you specified can't be loaded for whatever reason, error in Module::Generic will die, because that would be an error within another error. And that's an infinity loop of pain nobody asked for. π
debugβ Integer. Set the debugging value for this exception.fatalβ Boolean. Set it true to make this error fatal, which means instead of issuing awarn, it will die. πlvalueβ Boolean. Set it to a true value if this is called inside an assign method, like one using lvalue.messageβ A string for the error message.
The error message. (Yes, it's listed like that. We keep it real here.)
wantβ An array reference of data types that you allow this method to return when that data type is what the original caller was expecting.
The supported data types are: ARRAY, CODE, GLOB, HASH, OBJECT, SCALAR.
And ngl, the data type you give is actually case insensitive. β¨
Like, say you have a method that returns an array, but an error happens and it returns undef instead:
sub your_method
{
my $self = shift( @_ );
return( $self->error( "Something is wrong" ) ) if( $self->something_is_missing );
return( $self->{array} );
}
my $array = $obj->your_method; # array is undef
If the user then does:
$obj->your_method->[0]; # perl error occurs
...perl throws a "Can't use an undefined value as an ARRAY reference" error. Which is fine if that's genuinely what you want! But if you'd rather make sure the user doesn't get an error and instead gets an empty array, you could write your method your_method like this instead, passing the want parameter:
sub your_method
{
my $self = shift( @_ );
return( $self->error( { message => "Something is wrong", want => [qw( array )] ) ) if( $self->something_is_missing );
return( $self->{array} );
}
Then if the user calls this method in array context and an error happens, it returns an empty array instead. π
my $array = $obj->your_method->[0]; # undef
And fyi, by default the object call context is always activated, so you don't have to specify that one yourself. Freebie. π¦
Also note: calling error does not clear the current error. You gotta call clear_error explicitly for that.
And when an error gets set, the global variable ERROR in the inheriting package gets set accordingly. This is especially useful when you're initiating an object and an error occurs β at that point the object couldn't be initiated, so the end user can't use the object to grab the error message, but they can grab it from the global module variable ERROR, like:
my $obj = Some::Package->new ||
die( $Some::Package::ERROR, "\n" );
If the caller disabled warnings with the pragma no warnings, error respects that and won't call warn. Calling warn can also be silenced if the object has a property quiet set to true. π€«
The error message can be split across multiple arguments β error concatenates each argument to form one complete string. An argument can even be a reference to a subroutine and it'll get called to get the resulting string, unless the object property _msg_no_exec_sub is set to false. This can be switched off with the method noexec.
If perl is running under Apache2 modperl and an error handler is set with error_handler, this calls the error handler with the error string. π
If an Apache2 modperl log handler has been set, that also gets called to log the error.
If the object property fatal is set to true, this calls die instead of warn in perlfunc. π
Last but not least: since error returns undef in scalar context or an empty list in list context, if the method that triggered the error is chained, it would normally generate a perl error saying the next method can't be called on an undefined value. To solve that, when an object is expected, error returns a special object from the module Module::Generic::Null that lets all the chained methods still be performed and returns the error when it's requested. For example:
my $o = My::Package->new;
my $total $o->get_customer(10)->products->total || die( $o->error, "\n" );
Assuming get_customer here returns an error, the chaining continues anyway, but produces nothing and ultimately returns undef. Main character energy: falls gracefully instead of crashing. π
error_handler
Sets or gets a code reference that will be called to handle errors that got triggered when calling error. π«Ά
errors
Used by error() to store the error sent to it, for history's sake.
It returns an array of all errors that have occurred in list context, and the last error in scalar context.
errstr
Set/get the error string, period. It does not produce any warning like error would. Chill and quiet. πΈ
fatal
Boolean. If enabled, any error calls die in perlfunc instead of returning undef in perlfunc and setting an error.
Defaults to false.
You can enable it in your own package by initialising it in your own init method like this:
sub init
{
my $self = shift( @_ );
$self->{fatal} = 1;
return( $self->SUPER::init( @_ ) );
}
force_tty
Set or get this flag that specifies whether the environment is running under TTY or not. Normally this is derived automatically with the method _is_tty, but you can use this method to force your code. π
It's undef by default. Set it to false to say your code is not running under a TTY, and a true value otherwise.
This is used in colour_parse. π
get
Used to get an object data key value:
$obj->set( 'verbose' => 1, 'debug' => 0 );
## ...
my $verbose = $obj->get( 'verbose' );
my @vals = $obj->get( qw( verbose debug ) );
print( $out "Verbose level is $vals[ 0 ] and debug level is $vals[ 1 ]\n" );
This is no longer needed, since it's been more conveniently bypassed by the AUTOLOAD generic routine, with which you may say:
$obj->verbose(1);
$obj->debug(0);
## ...
my $verbose = $obj->verbose();
Like, SO much better, right? β¨ One line per thing, your future self reading the code at 2am will literally thank you. π«Ά
init
Okay so init is the real MVP here β it's the package object initializer, the one new calls to actually set up all the parameters you handed over in that hash-y fashion:
my $obj My::Module->new( 'verbose' => 1, 'debug' => 0 );
You're fully allowed to superseed init in your own package if you want it to do your bidding. π
The one rule: init has to give you back the object it got in the first place β or return an error if something went sideways, like:
sub init
{
my $self = shift( @_ );
my $dbh = DB::Object->connect() ||
return( $self->error( "Unable to connect to database server." ) );
$self->{dbh} = $dbh;
return( $self );
}
See what's happening in this example? Using error sets the global variable $ERROR that holds the error, so your user can just go:
my $obj = My::Module->new() || die( $My::Module::ERROR );
And here's a cute little auto-magic bit: if the global variables VERBOSE, DEBUG, VERSION are defined in the module and they don't already exist as an object key, they get set automatically according to those global variables. Slay. π
Now, the supported data type of the object generated by the new method is either a hash reference or a glob reference. Those supported types may very well get extended to an array reference in the near future β so watch this space, bestie. π
When you give it a hash reference, and the object property _init_strict_use_sub is set to true, init will call each method corresponding to the key name, pass it the key value, and it will set an error and skip it if that corresponding method doesn't exist. Otherwise, it calls each corresponding method, passes it whatever value was provided, and checks that method's return value. If the return value is undef and the value provided is not itself undef, then it issues a warning and returns the error that's assumed to have been set by that method. π«
Otherwise β if the object property _init_strict is set to true β it'll check the object property matching the hash key for the default value type and set an error and return undef if it doesn't match. For example, init in your module could be like this:
sub init
{
my $self = shift( @_ );
$self->{_init_strict} = 1;
$self->{products} = [];
return( $self->SUPER::init( @_ ) );
}
Then if init gets called like this:
$object->init({ products => $some_string_but_not_array }) || die( $object->error, "\n" );
your script is gonna die, because products value is a string and not an array reference. Periodt. π
But if none of those special object properties are set? Then init just creates an object property matching the key of the hash and sets its value accordingly. Like:
sub init
{
my $self = shift( @_ );
return( $self->SUPER::init( @_ ) );
}
And then if init is called like this:
$object->init( products => $array_ref, first_name => 'John', last_name => 'Doe' });
the object would then contain the properties products, first_name and last_name, and you can grab them as methods, like:
my $fname = $object->first_name;
You can also alter the way init processes the parameters it received using the following properties, which you set in your own init method. For example:
sub init
{
my $self = shift( @_ );
# Set the order in which the parameters are processed, because some methods may rely on other methods' value
$self->{_init_params_order} [qw( method1 method2 )];
# Enable strict sub, which means the corresponding method must exist for the parameter provided
$self->{_init_strict_use_sub} = 1;
# Set the class name of the exception to use in error()
# Here My::Package::Exception should inherit from Module::Generic::Exception or some other Exception package
$self->{_exception_class} = 'My::Package::Exception';
$self->SUPER::init( @_ ) || return( $self->pass_error );
return( $self );
}
You can also specify a default exception class that error will use to create exception objects, by setting the object property _exception_class:
sub init
{
my $self = shift( @_ );
$self->{name} = 'default_name';
# For any key-value pairs to be matched by a corresponding method
$self->{_init_strict_use_sub} = 1;
$self->{_exception_class} = 'My::Exception';
return( $self->SUPER::init( @_ ) );
}
log_handler
Give this a reference to a sub routine or an anonymous sub routine and it'll set the handler that message calls. π
It returns the current value that's set.
message
message() is your bestie for displaying verbose/debug output. It'll display something to the extent that either verbose or debug are toggled on.
When they are, every debugging message gets prepended by ## by default β or by whatever prefix string you specified with the prefix option β so everyone can tell at a glance that this is a debugging message. π
Bonus: if you pass a number as the first argument to message(), it gets treated as the minimum required level of debugness. So if the current debug state level is not equal or superior to the one you provided as first argument, the message will not be displayed. Vibe check, basically. β¨
For example:
# Set debugness to 3
$obj->debug( 3 );
# This message will not be printed
$obj->message( 4, "Some detailed debugging stuff that we might not want." );
# This will be displayed
$obj->message( 2, "Some more common message we want the user to see." );
Now, why debug and not verbose level? Well, because mostly the verbose level only needs to be true β that is, equal to 1 β to be efficient. You don't really need a verbose level greater than 1. The debug level, though, usually has various levels. Levels upon levels, iykyk. π
Also, the text you provide can be separated by commas, and can even be a code reference, like:
$self->message( 2, "I have found", "something weird here:", sub{ $self->dumper( $data ) } );
If the object has a property _msg_no_exec_sub set to true, then a code reference won't get called β it'll just be added to the string as is. Which you can do simply like this:
$self->noexec->message( 2, "I have found", "something weird here:", sub{ $self->dumper( $data ) } );
message also takes an optional hash reference as the last parameter, with these recognised options:
caller_infoβ a boolean value, true by default. When true, this prepends the debug message with information about the caller ofmessage. πlevelβ an integer. Debugging level.messageβ the text of the debugging message. This one's optional since it can be provided as the first or as consecutive arguments like in a list, as demonstrated in the example above. Which lets you do something like:
$self->message( 2, { message => "Some debug message here", prefix => ">>" });
or
$self->message( { message => "Some debug message here", prefix => ">>", level => 2 });
no_encodingβ boolean value. If true, and when the debugging is set to be printed to a file, this will not set the binmode toutf-8.prefixβ by default this is set to##. This value is used as the prefix in debugging output.typeβ type of debugging.
message_check
This is called by message. π¦
Provided with a list of arguments, this method checks if the first argument is an integer and figures out whether a debug message should be printed out or not. It returns the list of arguments as an array reference.
message_color
Alias for message_colour. Same energy, different spelling. π
message_colour
This is the same as message, except it checks for colour formatting β which message does not do. For example:
$self->message_colour( 3, "And {bold light white on red}what about{/} {underline green}me again{/} ?" );
message_colour can also be called as message_color.
See also colour_format and colour_parse. π
message_frame
Return the optional hash reference of parameters, if any, that can be provided as the last argument to message.
messagec
This is an alias for message_colour.
messagef
This works like sprintf from perlfunc, so give it a format and a list of arguments and it prints out the message. For example:
$self->messagef( 1, "Customer name is %s", $cust->name );
Where 1 is the debug level set with debug. π
messagef_colour
This method is the same as message_colour and messagef combined.
It enables you to pass sprintf-like parameters while enabling colours. Iconic, honestly. π
message_log
This is called from message. π«Ά
Provided with a message to log, this checks if message_log_io returns a valid file handler β presumably pointing to a log file β and if so, it prints the message to it.
If no file handle is set, this returns undef; otherwise it returns the value from $io->print.
message_log_io
Set or get the message log file handle. If it's set, message_log will use it to print messages received from message.
If no argument is provided but your module has a global variable LOG_DEBUG set to true and a global variable DEB_LOG set, presumably to the file path of a log file, then this attempts to open in write mode that log file. π
It returns the current log file handle, if any.
new_array
Instantiate a new Module::Generic::Array object. If any arguments are provided, it passes them to new in Module::Generic::Array and returns the object. β¨
new_datetime
Provided with some optional arguments, this instantiates a new Module::Generic::DateTime object, passing it whatever argument was provided.
Example:
my $dt = DateTime->now( time_zone => 'Asia/Tokyo' );
# Returns a new Module::Generic::DateTime object
my $d = $o->new_datetime( $dt );
# Returns a new Module::Generic::DateTime object with DateTime initiated automatically
# to now with time zone set by default to UTC
my $d = $o->new_datetime;
new_file
Instantiate a new Module::Generic::File object. If any arguments are provided, it passes them to new in Module::Generic::File and returns the object. π
new_glob
This method gets called instead of new in your package for GLOB type modules.
It sets a hash of options provided, calls init, and returns the newly instantiated object on success β or undef on error. π§
new_hash
Instantiate a new Module::Generic::Hash object. If any arguments are provided, it passes them to new in Module::Generic::Hash and returns the object.
new_json
This method tries to load JSON and create a new object. β
By default it enables the following JSON object properties:
"allow_blessed" in JSON"allow_nonref" in JSON"convert_blessed" in JSON"relaxed" in JSON
Extra options you can pass in, on top of literally every option JSON itself supports, are these bestie:
allow_blessedβ Boolean. Turn this on and it won't throw an error the second it bumps into a blessed reference that JSON can't convert anyway. Instead it encodes a JSONnullin place of the object. Very "we don't do drama here" of it. π«Άallow_nonrefβ Boolean. Turn this on and a non-reference gets converted into its matching string, number or null JSON value. On by default, no cap. β¨allow_tagsβ Boolean. Turn this on and, when it meets a blessed object, it checks whether theFREEZEmethod exists on that object's class. If it's there, that method gets used to serialise the object into a nonstandard tagged JSON value β and heads up, normal JSON decoders cannot decode that. πallow_unknownβ Boolean. Turn this on and it won't error out when JSON runs into values it simply can't represent in JSON (filehandles, for example) β it just encodes a JSONnulland moves on with its life. πasciiβ Boolean. Turn this on and it will not generate any characters outside the code range0..127(that's ASCII, babe).canonicalororderedβ Boolean. If true, the JSON data comes out ordered. But know that it's slower, especially on a big ol' pile of data. βconvert_blessedβ Boolean. Turn this on and, when it meets a blessed object, JSON checks whether theTO_JSONmethod exists on the object's class. If it does, it gets called in scalar context and the resulting scalar gets encoded instead of the object. Iconic. πindentβ Boolean. Turn this on and you get multiline output β every array member or object/hash key-value pair on its own line, indented all properly. So pretty. πlatin1β Boolean. Turn this on and the resulting JSON text gets encoded as latin1 (or iso-8859-1),max_depthβ Integer. Sets the maximum nesting level (default512) accepted while encoding or decoding. Hit the limit and it returns an error. β οΈprettyβ Boolean. If true, the JSON data is generated in a human readable format. Note that this takes considerably more space. πΈspace_afterβ Boolean. Turn this on and you get an extra optional space after the:that separates keys from values.space_beforeβ Boolean. Turn this on and you get an extra optional space before the:that separates keys from values.utf8β Boolean. This option is ignored, because the JSON data are saved to file using UTF-8 and double encoding would produce mojibake. So like, don't bother. π
new_json_safe
Same as new_json, except it uses Module::Generic::JSON, which is a thin, and reliable wrapper around JSON. And the glow-up? Module::Generic::JSON never dies. It sets an error object instead, and returns undef, or an empty list depending on the caller context. Emotional stability, we love to see it. π§
new_null
Returns a null value based on the expectations of the caller, so it doesn't break the caller's call flow. Literally the most accommodating method in the building. π
You can also optionally provide an hash or hash reference containing the option type with a value being either ARRAY, CODE, HASH, OBJECT or SCALARREF to force new_null to return the corresponding data without using the caller's context.
So here's the rundown, depending on what your caller wants:
- If the caller wants an hash reference, it returns an empty hash reference.
- If the caller wants an array reference, it returns an empty array reference.
- If the caller wants a code reference, it returns an anonymous subroutine that returns
undefor an empty list. - If the caller is calling another method right after, that means this is an object context and
new_nullwill instantiate a newModule::Generic::Nullobject. If any arguments were provided tonew_null, they get passed along tonewinModule::Generic::Nulland the shiny new object gets returned. - In any other context,
undefis returned or an empty list.
Without using new_null, if you return simply undef, like:
my $val = $object->return_false->[0];
sub return_false { return }
The above would trigger an error that the value returned by return_false is not an array reference. π± So instead of checking on the recipient end what kind of returned value was returned, the caller only needs to check if it's defined or not β no matter the context in which it's called. Main character energy for the caller, honestly.
For example:
my $this = My::Object->new;
my $val = $this->call1;
# return undef)
# object context
$val = $this->call1->call_again;
# $val is undefined
# hash reference context
$val = $this->call1->fake->{name};
# $val is undefined
# array reference context
$val = $this->call1->fake->[0];
# $val is undefined
# code reference context
$val = $this->call1->fake->();
# $val is undefined
# scalar reference context
$val = ${$this->call1->fake};
# $val is undefined
# simple scalar
$val = $this->call1->fake;
# $val is undefined
package My::Object;
use parent qw( Module::Generic );
sub call1
{
return( shift->call2 );
}
sub call2 { return( shift->new_null ); }
sub call_again
{
my $self = shift( @_ );
print( "Got here in call_again\n" );
return( $self );
}
This technique is also used by error to set an error object and return undef but still allow chaining beyond the error. See error and Module::Generic::Exception for more. π₯
new_number
Instantiate a new Module::Generic::Number object. If any arguments are provided, it passes them to new in Module::Generic::Number and returns the object. Slay. π’
new_scalar
Instantiate a new Module::Generic::Scalar object. If any arguments are provided, it passes them to new in Module::Generic::Scalar and returns the object. β¨
new_tempdir
Returns a new temporary directory by calling tempdir in Module::Generic::File
new_tempfile
Returns a new temporary directory by calling tempfile in Module::Generic::File
new_version
Provided with a version and this will return a new version object.
If the value provided is not a suitable version, this will set an error and return undef. Rude but fair. π
noexec
Sets the module property _msg_no_exec_sub to true, so that any call to message whose arguments include a reference to a sub routine will not try to execute the code. Very "we are NOT running that today" of it.
For example, imagine you have a sub routine such as:
sub hello
{
return( "Hello !" );
}
And in your code, you write:
$self->message( 2, "Someone said: ", \&hello );
If _msg_no_exec_sub is set to false (which is the default), then the above would print out the following message:
Someone said Hello !
But if _msg_no_exec_sub is set to true, then the same would rather produce the following:
Someone said CODE(0x7f9103801700)
iykyk π«
pass_error
Provided with an error β typically a Module::Generic::Exception object, but honestly it could be anything as long as it is an object, hopefully an exception object β this sets the error value to the error provided, and without issuing any new warning nor creating a new Module::Generic::Exception object.
It makes it possible to pass the error along so the caller can retrieve it later. This is typically used by a method calling another one in another module that produced an error. For example:
sub getCustomerInfo
{
my $self = shift( @_ );
# Maybe a LWP::UserAgent sub class?
my $client = $self->lwp_client_object;
my $res = $client->get( $remote_api_endpoint ) ||
return( $self->pass_error( $client->error ) );
}
Then:
my $client_info = $object->getCustomerInfo || die( $object->error, "\n" );
Which would return the http client error that has been passed along. π
You can optionally provide an hash of parameters as the last argument, such as:
return( $self->pass_error( $obj->error, { class => 'My::Exception', code => 400 } ) );
Or, you could also pass all parameters as an hash reference, such as:
return( $self->pass_error({
error => $obj->error,
class => 'My::Exception',
code => 400,
}) );
Supported options are:
callbackβ A code reference, such as a subroutine reference or an anonymous code that gets executed. This is designed to be used to do some cleanup. π§Ήclassβ The name of a class name to re-bless the error object provided.codeβ The error code to set in the error object being passed.errorβ The error object to be passed on.
If this is not provided, it will get it with the object error method, or the class global variable $ERROR. π
quiet
Set or get the object property quiet to true or false. If this is true, no warning will be issued when error is called. Shhh. π€«
save
Provided with some data and a file path, or alternatively an hash reference of options with the properties data, encoding and file, this will write to the given file the provided data using the encoding encoding.
This is designed to simplify the tedious task of write to files. Lowkey a lifesaver. π«Ά
If it cannot open the file in write mode, or cannot print to it, this will set an error and return undef. Otherwise this returns the size of the file in bytes. β
serialise
This method uses a specified serialiser class and serialises the given data either by returning it or by saving it directly to a given file.
The serialisers currently supported are: CBOR::Free, CBOR::XS, JSON, Sereal and Storable::Improved (or the legacy version Storable). They are not required by Module::Generic, so you must install them yourself. If the serialiser chosen is not installed, this will set an error and return undef. Install your own dependencies, bestie, I'm not your mom. π
This method takes some data and an optional hash or hash reference of parameters. It can then:
- save data directly to File
- save data to a file handle (only with
Storable::Improved/Storable) - Return the serialised data
The supported parameters are:
appendβ Boolean. If true, the serialised data gets appended to the given file. This works only in conjunction with file. π₯base64
...
That whole base64 thing? You can set it to a true value like 1, or to the base64 encoder/decoder of your choice, or to an array reference holding exactly 2 code references β first one encodes, second one decodes π
If you just set it to a true value, serialise calls _has_base64 to sniff out whatever base64 modules you've got installed. Right now the supported ones are: Crypt::Misc and MIME::Base64. And obvi you need one of those installed first before any of this works π
And if you set this option but no appropriate module can be found, serialise comes back with an error. No silent failure, bestie π
fileβ String. A file path where the serialised data gets stored ποΈioβ A file handle. This one's used when the serialiser is Storable::Improved / Storable, so it can callstore_fdin Storable::Improved andfd_retrievein Storable::Improved π«Άlockβ Boolean. If true, it locks the file before writing to it. Heads up: this only works in conjunction withfileand the serialiser Storable::Improved πserialiserorserializerβ A string that is the class of the serialiser to use. It can only be either Sereal or Storable::Improved. That's it, those are the two β
On top of all that, these next options are supported and passed straight through to each serialiser:
- CBOR::Free:
canonical,string_encode_mode,preserve_references,scalar_references - CBOR:
max_depth,max_size,allow_unknown,allow_sharing,allow_cycles,forbid_objects,pack_strings,text_keys,text_strings,validate_utf8,filter - JSON:
allow_blessedallow_nonrefallow_unknownallow_tagsasciiboolean_valuescanonicalconvert_blessedfilter_json_objectfilter_json_single_key_objectindentlatin1max_depthmax_sizeprettyrelaxedspace_afterspace_beforeutf8 encodein Sereal::Decoder if the serialiser is Sereal:aliased_dedupe_strings,canonical,canonical_refs,compress,compress_level,compress_threshold,croak_on_bless,dedupe_strings,freeze_callbacks,max_recursion_depth,no_bless_objects,no_shared_hashkeys,protocol_version,snappy,snappy_incr,snappy_threshold,sort_keys,stringify_unknown,undef_unknown,use_protocol_v1,warn_unknown- Storable::Improved / Storable: no option available. Nothing. Zero. That's the whole list π«
If an error occurs, this sets an error and returns undef.
serialize
Alias for serialise. Same thing, different spelling vibes β¨
set
set() sets the object's inner data type and takes arguments in a hash-like fashion π
$obj->set( 'verbose' => 1, 'debug' => 0 );
subclasses
Give it a CLASS value and this method tries to guess all the existing sub classes of the CLASS you handed over π΅οΈββοΈ
If you don't provide CLASS, it uses the class the calling object was blessed into instead. Convenient, honestly.
It returns an array of subclasses in list context, and a reference to an array of those subclasses in scalar context.
If an error occurred, undef comes back and an error gets set accordingly. You can grab that with the error method π
true
Returns a "true" variable from Module::Generic::Boolean β
false
Returns a "false" variable from Module::Generic::Boolean β
verbose
Set or get the verbosity level with an integer. That's it, that's the method π£οΈ
will
This tries to figure out if an object supports a given method call, and returns the code reference to it β or undef if none is found π
AUTOLOAD
The special AUTOLOAD() routine gets called by perl whenever no matching routine was found in the module. So basically perl is like "girl idk this one" and AUTOLOAD steps in π
AUTOLOAD() then tries hard to process the request. Say, for example, we have a routine foo.
First up, it checks whether an equivalent entry for the routine name that was called exists in the hash reference of the object. If it does, and more than one argument was passed to this non-existing routine, those arguments get stored as a reference to an array as the value of that key in the object. Otherwise, the single argument simply gets stored as the value of the key of the object. Simple as π
Then, if called in list context, it returns an array if the value of the key entry was an array reference, or a hash list if the value of the key entry was a hash reference, or finally just the value of the key entry.
If this non-existing routine that got called is actually defined, the routine gets redeclared and the arguments get passed to it.
If that fails too, it tries checking for an AutoLoadable file at auto/PackageName/routine_name.al π
If the file exists, it gets required, the routine name gets linked into the package name space, and finally it's called with the arguments.
But if the require process failed, or if that AutoLoadable routine file didn't exist, AUTOLOAD() checks whether the special routine EXTRA_AUTOLOAD() exists in the module. If it does, it calls it and passes it the arguments π«Ά Otherwise, AUTOLOAD dies with a message explaining that the called routine didn't exist and couldn't be found in the current class. Rip π
π SUPPORT METHODS π
These methods are designed to be called from the package inheriting from Module::Generic, so they can perform various functions and speed up development. They're the behind-the-scenes besties π§
__create_class
Give it an object property name and a hash reference representing a dictionary, and this produces a dynamically created class/module. Iconic, honestly π
If a property _class exists in the dictionary, it gets used as the class/package name. Otherwise, a name gets derived from the calling object class and the object property name. For example, in your module:
sub products { return( 'products', shift->_set_get_class(
{
name => { type => 'scalar' },
customer => { type => 'object', class => 'My::Customer' },
orders => { type => 'array_as_object' },
active => { type => 'boolean' },
created => { type => 'datetime' },
metadata => { type => 'hash' },
stock => { type => 'number' },
url => { type => 'uri' },
}, @_ ) ); }
Then calling your module method products like this:
my $prod = $object->products({
name => 'Cool product',
customer => { first_name => 'John', last_name => 'Doe', email => 'john.doe@example.com' },
orders => [qw( 123 987 456 654 )],
active => 1,
metadata => { transaction_id => 123, api_call_id => 456 },
stock => 10,
uri => 'https://example.com/p/20'
});
Using the resulting object $prod, we can access this dynamically created class/module like so:
printf( <<EOT, $prod->name, $prod->orders->length, $prod->customer->last_name,, $prod->url->path )
Product name: %s
No of orders: %d
Customer name: %s
Product page path: %s
EOT
__instantiate_object
my $o = $self->__instantiate_object( 'emails', 'Some::Module', @_ );
# or, with a callback
my $o = $self->__instantiate_object({ field => 'emails', callback => sub
{
my( $class, $args ) = @_;
return( $class->parse_bare_address( $args->[0] ) );
}}, 'Email::Address::XS', @_ );
Give it an object property name and a class/package name, and this attempts to load the module if it isn't already loaded. It does that using load_class in Class::Load. Once loaded, it inits an object, passing it the other arguments it received. It returns the instantiated object on success, or undef and sets an "error" π€
This is a support method used by _instantiate_object.
Alternatively, instead of the object property name you can pass a hash reference with these properties:
fieldβ Mandatory. The object property name.callbackβ Optional. A code reference, like an anonymous subroutine, that gets called with the class and an array reference of values provided, but possibly empty.
The current object is accessible in the callback as the special variable $_ β¨
This is a useful callback when the module instantiation either doesn't use the new method or doesn't simply take one or multiple arguments β like when the instantiation method would require a hash of parameters, such as Email::Address::XS.
_instantiate_object
This does the same thing as __instantiate_object, and the purpose is for this method to be potentially superseded in your own module. In your own module, you'd call __instantiate_object π
_can
Given a value and a method name, this returns true if the value provided is an object that can in UNIVERSAL perform the specified method, or false otherwise.
You can also hand it an array of method names to check instead of just one method name. In that case, all the method names provided must be supported by the object, otherwise it returns false. All or nothing, bestie π
This makes it way more convenient to write:
if( $self->_can( $obj, 'some_method' ) )
{
# ...
}
or
if( $self->_can( $obj, [qw(some_method other_method )] ) )
{
# ...
}
than to write:
if( Scalar::Util::bless( $obj ) && $obj->can( 'some_method' )
{
# ...
}
_can_overload
my $rv = $self->_can_overload( undef, '""' ); # false
my $rv = $self->_can_overload( '', '""' ); # false
my $rv = $self->_can_overload( $some_object_not_overloaded, '""' ); # false
# In this example, it would return false, because, although it is an overloaded value provided, that object has no support for the operators specified.
my $rv = $self->_can_overload( $some_object_overloaded, '""' ); # false
my $rv = $self->_can_overload( $some_good_object_overloaded, '""' ); # true
my $rv = $self->_can_overload( $some_good_object_overloaded, [ '""', 'bool' ] ); # true
Given some value and a string representing an operator β or an array reference of operators β this returns true if the value is an object that has the specified operator, or operators in the case of an array reference of operators being provided, overloaded π₯
It returns false otherwise. That's the whole vibe check.
_get_args_as_array
Given arguments, this support method returns the arguments provided as an array reference, regardless of whether they were initially provided as an array reference or a simple array. It just handles it for you π«Ά
For example:
my $array = $self->_get_args_as_array(qw( those are arguments ));
# returns an array reference containing: 'those', 'are', 'arguments'
my $array = $self->_get_args_as_array( [qw( those are arguments )] );
# same result as previous example
my $array = $self->_get_args_as_array(); # no args provided
# returns an empty array reference
_get_args_as_hash
okay so this one is a little shapeshifter bestie π¦ β you hand it arguments, and it hands them back to you as a hash reference, and it genuinely does not care whether you originally passed a hash reference or just a plain old hash (you know, a list of key/value pairs). same energy either way π
In list context, it returns a hash reference and an array reference containing the order of the properties you provided. iconic.
For example:
my $ref = $self->_get_args_as_hash( first => 'John', last => 'Doe' );
# returns hash reference { first => 'John', last => 'Doe' }
my $ref = $self->_get_args_as_hash({ first => 'John', last => 'Doe' });
# same result as previous example
my $ref = $self->_get_args_as_hash(); # no args provided
# returns an empty hash reference
my( $ref, $keys ) = $self->_get_args_as_hash( first => 'John', last => 'Doe' );
In that last example, $keys is an array object containing the list of properties passed in, in the order they were provided β so, first and last. β¨
But if the properties were provided as a hash reference, the $keys you get back is the sorted list of properties, like:
my( $ref, $keys ) = $self->_get_args_as_hash({ last => 'Doe', first => 'John' });
Here $keys is sorted and contains the properties in alphabetical order. no cap.
However β and this is the plot twist π β this returns empty:
my $ref = $self->_get_args_as_hash( { age => 42, city => 'Tokyo' }, some_other => 'parameter' );
Yep, empty hash reference. Why? Because even though the first parameter is a hash reference, there's more than one parameter. rude but fair.
As of version v0.24.0, this utility method got an upgrade and allows for more advanced use β it permits embedding parameters among the arguments, removes them from the list, and returns them. main character behavior honestly π
For example, assuming @_ contains: foo bar debug 4 baz
my $ref = $self->_get_args_as_hash( @_, args_list => [qw( debug )] );
This will set $ref with debug only. slay.
Even the special parameter args_list doesn't have to be at the end β it could be literally anywhere:
my $ref = $self->_get_args_as_hash( 'foo', 'bar', args_list => [qw( debug )], 'debug', 4, 'baz' );
And if you want to modify @_ because you need its content without any params, just pass @_ as an array reference:
my $ref = $self->_get_args_as_hash( \@_, args_list => [qw( debug )] );
say "@_";
$ref is a hash reference that would contain debug, and @_ only contains foo bar baz. π
You can also simply pass @_ as a reference to save memory. Assuming @_ is foo bar baz 3 debug 4:
my $ref = $self->_get_args_as_hash( \@_ );
This would set $ref to be a hash reference with keys foo baz debug.
_get_symbol
my $obj = My::Class->new;
my $sym = $obj->_get_symbol( '$VERSION' );
my $sym = $obj->_get_symbol( 'Other::Class' => '$VERSION' );
This returns the symbol for the given variable in the current package, or, if a package is explicitly specified, in that package. π
Variables can be scalar with $, array with @, hash with %, or code with &.
It returns a reference if found β otherwise, if it's not found, undef in scalar context or an empty list in list context.
And if an error occurs? It sets an error object and returns undef in scalar context or an empty list in list context. check your return values bestie π
_get_stack_trace
This will return a Devel::StackTrace object initiated with the following options set:
indent1 β this will set an initial indent tabskip_frames1 β this is set to1so this very method is not included in the frames stack
so the trace is cute and tidy and doesn't include itself. self-aware queen πΈ
_has_base64
Provided with a value, and this returns an array reference containing 2 code references: one for encoding and one for decoding. π
The value you pass can be a simple true value, such as 1 β and then _has_base64 will check if Crypt::Misc and MIME::Base64 are installed on the system, and will use in priority MIME::Base64.
It can also be an array reference that already contains 2 code references β in which case that value is simply returned. Nothing more is done. honestly so valid.
Finally, the value provided can be a module class name. _has_base64 knows only of Crypt::Misc and MIME::Base64, so if you want to use any other one, arrange yourself to pass to _has_base64 an array reference of 2 code references as explained above. iykyk π§
_has_symbol
my $obj = My::Class->new;
my $bool = $obj->_has_symbol( '$VERSION' );
my $bool = $obj->_has_symbol( 'Other::Class' => '$VERSION' );
This returns true (1) if the specified variable exists in the current package, or, if a package is explicitly specified, in that package. It returns false (0) if the package does not have that variable. β
Variables can be scalar with $, array with @, hash with %, or code with &.
If an error occurs, it sets an error object and returns undef in scalar context or an empty list in list context.
_implement_freeze_thaw
Provided with a list of package names, and this method will implement in each of them the subroutines necessary to handle Storable::Improved (or the legacy Storable), CBOR and Sereal serialisation. π₯
In effect, it will check that the subroutines FREEZE, THAW, STORABLE_freeze and STORABLE_thaw exist, or sets up simple ones if they are not defined.
This works for packages that use hash-based objects. However β heads up babes β you need to make sure there is no specific package requirements, and if there is, you might need to customise those subroutines by yourself. > don't just assume it'll work for your weird custom package, fr fr π
_is_a
Provided with an object and a package name, and this will return true if the object is a blessed object from this package name (or a sub package of it), or false if not. β¨
The value of this is to reduce the burden of having to check whether the object actually exists, i.e. is not null or undef, if it is an object, and if it is from that class. This allows you to do it in just one method call, like this:
if( $self->_is_a( $obj, 'My::Package' ) )
{
# Do something
}
Of course, if you are sure the object is actually an object, then you can directly do:
if( $obj->isa( 'My::Package' ) )
{
# Do something
}
_is_array
Provided with some data, this checks if the data is of type array β even if it is an object. π¦
This uses reftype in Scalar::Util to achieve that purpose. So for example, an object such as:
package My::Module;
sub new
{
return( bless( [] => ( ref( $_[0] ) || $_[0] ) ) );
}
This would produce an object like:
My::Module=ARRAY(0x7f8f3b035c20)
When checked with _is_array, this would return true β just like an ordinary array. cute right?
If you would use:
ref( $object );
It would rather return the module package name: My::Module. and that's the difference, bestie π
_is_class_loadable
Takes a module name and an optional version number, and this will check if the module exists and can be loaded by looking at the @INC and using version to compare required version and existing version.
It returns true if the module can be loaded, or false otherwise. π
_is_class_loaded
Provided with a class/package name, this returns true if the module is already loaded, or false otherwise.
It performs this test by checking if the module is already in %INC. easy peasy π
_is_class_loadable
(yeah it's listed again β the original does that too, so I'm keeping both, no cap π)
Provided with a package name, a.k.a. a class, and an optional version, and this will endeavour to check if that class is installed and, if a version is provided, if it is greater or equal to the version provided. π
If the module is not already loaded and a version was provided, it uses Module::Metadata to get that module version.
It returns true if the module can be loaded, or false otherwise.
If an error occurred, it sets an error and returns undef, so be sure to check whether the return value is defined. don't just blindly trust it, babe π
_is_class_loaded
(also listed twice, also keeping it β¨)
Provided with a package name, a.k.a. a class, and this returns true if the class has already been loaded, or false otherwise.
If you are running under mod_perl, this method will use loaded in Apache2::Module to find out β otherwise, it will simply check if the class exists in %INC. slay, adaptable π«Ά
_is_code
Provided with some value β possibly undefined β and this returns true if it is a CODE, such as a subroutine reference or an anonymous subroutine, or false otherwise.
_is_empty
This checks if a value was provided, and if it is defined, or if it has a positive length, or is a scalar object that has the method defined, which returns false. πΈ
Based on those checks, it returns true (1) if it appears the value is undefined or empty, and false (0) otherwise.
_is_glob
Provided with some value β possibly undefined β and this returns true if it is a filehandle, or false otherwise. π
_is_hash
Same as _is_array, but for a hash reference.
You can pass also the additional argument strict, in which case this will apply only to non-objects. iykyk π
For example:
my $hash = {};
say $this->_is_hash( $hash ); # true
my $obj = Foo::Bar->new;
say $this->_is_hash( $obj ); # true
# but...
say $this->_is_hash( $obj => 'strict' ); # false
_is_integer
Returns true if the value provided is an integer, or false otherwise. A valid value includes an integer starting with + or -. π₯
_is_ip
Returns true if the given IP has a syntax compliant with IPv4 or IPv6, including CIDR notation or not, false otherwise. π
For this method to work, you need to have installed Regexp::Common::net. no module, no vibes, bestie π
_is_number
Returns true if the provided value looks like a number, false otherwise. simple as that β¨
_is_object
Provided with some data, this checks if the data is an object. It uses blessed in Scalar::Util to achieve that purpose. π¦
_is_overloaded
Provided with some value β presumably an object β and this will return true if it is overloaded in some way, or false if it is not. π
_is_scalar
Provided with some data, this checks if the data is of type scalar reference, e.g. SCALAR(0x7fc0d3b7cea0), even if it is an object. β
_is_tty
Returns true if the program is attached to a tty (terminal), meaning that it is run interactively, or false otherwise β such as when its output is piped. π so basically "am I being piped right now or are we hanging out for real"
_is_uuid
Provided with a non-zero length value, and this will check if it looks like a valid UUID, i.e. a unique universal ID, and upon successful validation will set the value and return its representation as a Module::Generic::Scalar object. π
An empty string or undef can be provided and will not be checked. very forgiving of it π
_is_version
my $vers = 'v1.2.3';
# or
my $vers = 1.2;
# or
my $vers = 5.006000
say $obj->_is_version( $vers ) ? "ok" : "not ok"; # ok
This takes a string, and checks if it looks like a version number based on a regular expression taken from Changes::Version. π§
Returns true (1) upon success, or false (0) if the string does not look like a version number.
π
_list_symbols
my $obj = My::Class->new;
my @symbols = $obj->_list_symbols;
my @symbols = $obj->_list_symbols( 'Other::Class' );
# possible types are: scalar, array, hash and code
# specify a type to get only the symbols of that type
my @symbols = $obj->_list_symbols( 'My::Class' => 'scalar' );
Okay so this one gives you a whole list of all the symbols in the current package β or, if you pass a package name explicitly, from that package instead. π
You can also optionally pass a symbol type to narrow down what comes back. But β and this is the catch, bestie β if you wanna specify a type, you also have to specify a package, even if that package is just the current one. No skipping steps. π
If something goes wrong, it sets an error object and returns undef in scalar context, or an empty list in list context. π
_load_class
$self->_load_class( 'My::Module' ) || die( $self->error );
$self->_load_class( 'My::Module', qw( :some_tags SOME_CONSTANTS_TO_IMPORT ) ) || die( $self->error );
$self->_load_class(
'My::Module',
qw( :some_tags SOME_CONSTANTS_TO_IMPORT ),
{ version => 'v1.2.3', caller => 'Its::Me' }
) || die( $self->error );
$self->_load_class( 'My::Module', { no_import => 1 } ) || die( $self->error );
Give it a class/package name, optionally a list of things to import, and β as the last parameter β an optional hash reference of options, and it'll try to load the module for you. It uses use in perlfunc, no external module needed. Slay. β¨
On success it hands you back the package name it loaded. π
It wraps everything in an eval to trap errors, and if something blew up it returns undef in perlfunc and sets an error accordingly.
Possible options are:
callerβ the package name of whoever's calling. If you don't give it one, it defaults to whatevercallerin perlfunc hands back.no_importβ set this to a true value and the loaded module won't import anything into your namespace. It's the equivalent of literally doing:
use My::Module ();
versionβ the minimum version this class needs to load. This value goes straight tousein perlfunc.
π¨ THREAD SAFETY WARNING π¨
_load_class is mostly thread-safe, but listen β dynamic class loading using eval and use at runtime can be risky in threaded environments. π
Perl caches loaded modules, but if two threads try to load the same module at the same time, and that module does initialization in BEGIN blocks or has side effects in its import mechanism, you can get race conditions or partial loading. Not the vibe. π
Safe Usage Recommendations π«Ά
- Call
_load_classduring application startup, or before any threads get spawned. - Avoid dynamically loading classes at runtime inside threads unless you're certain the modules being loaded are themselves thread-safe and have zero side effects on import.
- To guarantee full safety, preload modules at initialization time using
use, or call_load_classduring the main thread's setup phase.
_load_classes
This one loads multiple classes at once β you give it an array reference of class names to load, plus an optional hash or hash reference of options, basically the same ones you'd hand to _load_class. π
If one of those classes fails to load, it returns immediately after setting an error. No cap, it does not keep going. π
_looks_like_path
my $path = 'C:\dir\file.txt';
my $path = 'C:/dir';
my $unc_share = '\\Server\Share\...';
my $path = '~john/some/where';
my $path = './foo';
my $path = '../bar';
# Windows style
my $path = '.\foo';
my $path = '..\bar';
my $path = 'config.json';
say $obj->_looks_like_path( $path ) ? "ok" : "not ok"; # ok
Feed it a string and it'll conservatively check whether it looks like a file path. π¦
Returns true (1) when it does, false (0) when it doesn't. Simple as that.
_lvalue
This gives you a generic lvalue method that works both in assign context and lvalue context. π
As of version 0.29.6, this is just an alias for _set_get_callback, which has way more extensive features. So like, it's the older sibling energy β still valid, but the new one ate. π
_obj2h
This makes sure the module object is a hash reference β like, for example, when the module object is based on a file handle. It's what lets Module::Generic work no matter what underlying data type got blessed into the object. Iconic flexibility, honestly. β¨
_on_error
Sets or gets a code reference acting as a callback that gets triggered whenever error or pass_error is called with an error. π
return( $self->error( "Oops" ) ) if( $something_bad_happened );
# or
return( $self->pass_error( $another_error_object ) ) if( $something_bad_happened );
_parse_timestamp
Give it a string representing a date or datetime, and it'll try to parse it and return a DateTime object. π°οΈ It also builds a DateTime::Format::Strptime to preserve the original date/datetime string representation and attaches it to the DateTime object β so when the DateTime object gets stringified, it displays the exact same string that was originally parsed. Chef's kiss. π
Supported formats are:
"2019-10-03 19-44+0000"or"2019-10-03 19:44:01+0000"β found in GNU PO files for example."2019-06-19 23:23:57.000000000+0900"β found in PostgreSQL."2019-06-20T11:08:27"β matching ISO8601 format."2019-06-20 02:03:14"β found in SQLite."2019-06-20 11:04:01"β found in MySQL."Sun, 06 Oct 2019 06:41:11 GMT"β standard HTTP dates."12 March 2001 17:07:30 JST""12-March-2001 17:07:30 JST""12/March/2001 17:07:30 JST""12 March 2001 17:07""12 March 2001 17:07 JST""12 March 2001 17:07:30+0900""12 March 2001 17:07:30 +0900""Monday, 12 March 2001 17:07:30 JST""Monday, 12 Mar 2001 17:07:30 JST""03/Feb/1994:00:00:00 0000""2019-06-20""2019/06/20"2016.04.22"2014, Feb 17""17 Feb, 2014""February 17, 2009""15 July 2021"22.04.2016"22-04-2016""17. 3. 2018.""17.III.2020""17. III. 2018."20030613"2021εΉ΄7ζ14ζ₯"β Japanese regular date using occidental years."什ε3εΉ΄7ζ14ζ₯"β Japanese regular date using Japanese era years.- Unix timestamp, possibly followed by a dot and milliseconds.
- Relative date to current date and time. Example:
-5Y - 5 years
+2M + 2 months
+3D + 3 days
-2h - 2 hours
-4m - 4 minutes
-10s - 10 seconds
'now'β the word now will set the return value to the current date and time.
_set_get
sub name { return( shift->_set_get( 'name', @_ ) ); }
or
sub name { return( shift->_set_get({
field => 'name',
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
Hand it an object property name plus some value, and it'll set or get that value for that property. The workhorse. π΄β¨
Here's the fun part though: if the stored value is an array and you call it in list context, it returns the array as a list, not the array reference. Same deal for a hash reference β you get a hash in list context. In scalar context you just get whatever the value actually is: array reference, hash reference, string, whatever. π
Alternatively, you can pass a hash reference instead of a field name, and add extra parameters like:
checkβ acheckanonymous subroutine that gets called with 2 arguments: the current object, and the value being set β but before it's actually set. If this callback returns false, you get an error ofInvalid value provided., so make sure you return true to signal the check passed. Also, the callback is allowed to die, and that'll be caught and interpreted asfalse. πcallbacksβ a hash reference of callbacks. You can use eithersetoradd, whichever you prefer. The callback gets called with the current object and the value that's already been set. Fair warning: any fatal exception during the callback will not be caught. π
_set_get_array
sub my_array { return( shift->_set_get_array( 'my_array', @_ ) ); }
my $ref = $self->my_array( @some_values );
my $ref = $self->my_array( $array_ref );
or
sub name { return( shift->_set_get_array({
field => 'name',
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
Give it an object property name plus some data and it stores that data as an array reference. π
It returns the current value stored β i.e. an array reference β whether you call it in list or scalar context. No shape-shifting here, it stays a ref.
Example:
sub products { return( shift->_set_get_array( 'products', @_ ) ); }
Alternatively, you can pass a hash reference instead of a field name, and add extra parameters like:
checkβ acheckanonymous subroutine called with 2 arguments: the current object and the value being set, before it gets set. If it returns false, an error ofInvalid value provided.comes back, so return true to show the check passed. It can die too β that gets caught and treated asfalse. πcallbacksβ a hash reference of callbacks. Eithersetoradd, whichever you vibe with. Called with the current object and the value that's already been set. Any fatal exception during the callback is not caught. π
_set_get_array_as_object
sub name { return( shift->_set_get_array_as_object( 'name', @_ ) ); }
or
sub name { return( shift->_set_get_array_as_object({
field => 'name',
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
Give it an object property name plus some data, and it stores that data as an object of Module::Generic::Array. πΈ
If this gets called with no data set at all, you get back an object with nothing inside it, returned to you β click, done π«
Example, bestie:
# In your module
sub products { return( shift->_set_get_array_as_object( 'products', @_ ) ); }
And then using your method:
printf( "There are %d products\n", $object->products->length );
$object->products->push( $new_product );
Alternatively β plot twist β you can pass a hash reference instead of an object property to hand over extra parameters, like so:
- callbacks β a hash reference mapping operation type
"add"(or"set") to callback subroutine name / code reference pairs.
The callback gets called with the current object, plus the value that has already been set.
Any fatal exception during the callback is not caught. Nope. Not caught. π
"check"β a"check"anonymous subroutine that gets called with 2 arguments: the current object, and the value being set β but before it's set.
If this callback returns false, then an error of "Invalid value provided." gets returned, so make sure you return true to signal that the check passed β
The callback can die, and it will be caught, and that'll be interpreted as "false".
- field β the object property name π·οΈ
- wantlist β Boolean. If true, then it returns a list in list context instead of the array object.
For example:
sub children { return( shift->set_get_array_as_object({
field => 'children',
callbacks =>
{
set => '_some_add_callback',
},
}), @_ ); }
The value of the callback can be either a subroutine name or a code reference. Either slaps π
π«Ά _set_get_boolean
sub is_true { return( shift->_set_get_boolean( 'is_true', @_ ) ); }
or
sub name { return( shift->_set_get_boolean({
field => 'name',
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
Hand it an object property name and some data and this stores the data as a boolean value, periodt β¨
- If the data you hand over is a
JSON::PP::BooleanorModule::Generic::Booleanobject, then the data is stored as is β no touchy. - If the data is a scalar reference, its referenced value gets checked and
"true"inModule::Generic::Booleanor"false"inModule::Generic::Booleanis set accordingly. - If the data is a string with value of
"true"or"val"then"true"inModule::Generic::Booleanor"false"inModule::Generic::Booleanis set accordingly. - Otherwise the data provided is checked for whether it's a true value or not, and
"true"inModule::Generic::Booleanor"false"inModule::Generic::Booleanis set accordingly.
If no value is provided, and the object property has already been set, this runs the same checks as above and returns either a JSON::PP::Boolean or a Module::Generic::Boolean object. iykyk π§
Alternatively, you can pass a hash reference instead of an object property to provide callbacks that get called upon addition or removal of a value. That hash reference can contain these properties:
"field"β the object property name π·οΈ"check"β a"check"anonymous subroutine called with 2 arguments: the current object, and the value being set, but before it's set.
If this callback returns false, then an error of "Invalid value provided." gets returned, so make sure to return true to show the check passed.
The callback can die, and it will be caught, and be interpreted as "false" π
"callbacks"β a hash reference of callbacks. You can use either"set"or"add", whichever you prefer.
The callback gets called with the current object, and the value that has already been set.
Any fatal exception during the callback will not be caught.
For example:
sub is_valid { return( shift->set_get_boolean({
field => 'is_valid',
callbacks =>
{
set => '_some_add_callback',
},
}), @_ ); }
The value of the callback can be either a subroutine name or a code reference. Both iconic π
π _set_get_callback
sub name : lvalue { return( shift->_set_get_callback({
get => sub
{
my $self = shift( @_ );
# The context hash is available with $_
if( $_->{list} )
{
return( @{$self->{name}} );
}
else
{
return( $self->{name} );
}
},
set => sub
{
my $self = shift( @_ );
$self->message( 1, "Got here for 'name' in setter callback" );
return( $self->{name} = shift( @_ ) );
},
field => 'name'
}, @_ ) ); }
# ^^^^
# Don't forget the @_ !
Then it can be called indifferently, like:
my $rv = $obj->name( 'John' );
# $rv is John
$rv = $obj->name;
# $rv is John
$obj->name = 'Peter';
$rv = $obj->name;
# $rv is Peter
$obj->colours( qw( orange blue ) );
my @colours = $obj->colours;
# returns a list of colours orange and blue
my $colour = $obj->colours;
# $colour is 'orange'
Give it a hash reference of parameters and this support method calls the accessor "get" callback or the mutator "set" callback depending on whether any arguments were provided. Vibe check passed β
This support method supports "lvalue" methods as described in "Lvalue subroutines" in perlfunc. It's similar to Sentinel, but on steroids, since it handles exceptions and provides context β which is often critical, ngl.
If a fatal exception happens in a callback, it gets trapped using a try-catch block, an error object is set, and "undef" is returned.
But if an error happens while you're operating in an "lvalue" assigned context, like:
$obj->name = 'Peter';
Then to check whether there was an error, you could do:
if( $obj->error )
{
# Do something here
}
If the "fatal" option is set to true, then it simply dies instead. Dramatic but valid π
Supported options are:
"fatal"β Boolean. If true, this makes any exception fatal, so it dies."field"β the name of the object field this helper method is used for. This is optional."get"β the accessor subroutine reference or anonymous subroutine that handles retrieving data.
This is a mandatory option, and this support method dies if it's not provided π¨
It gets passed the current object, and returns whatever is returned in list context β or, in any other context, the first value that the callback would return.
Also, the special variable $_ will be available and contains the call context.
"set"β the mutator subroutine reference or anonymous subroutine that handles storing data.
This is an optional option. Which means you can set only an accessor "get" callback without specifying a mutator "set" callback. Slay, flexibility β¨
It gets passed the current object, and the list of arguments. If the method is used as a regular method β as opposed to an lvalue subroutine β then multiple arguments may be passed:
$obj->colours( qw( blue orange ) );
but if used as an "lvalue" method, then of course only one argument will be available:
$obj->name = 'John';
Also the special variable $_ will be available and contains the call context.
The value returned gets passed back to the caller.
The "context" provided with the special variable $_ inside the callback may have these properties:
"assign"β true when the call context is an"lvalue"subroutine that a value is being assigned to, like:
$obj->name = 'John';
"boolean"β true when the call context is a boolean, like:
if( $obj->active )
{
# Do something
}
"code"β true when the call context is a code reference, like:
$obj->my_callback->();
"count"β contains the number of arguments expected by the caller. This is especially interesting when you're in list context."glob"β true when the call context is a glob."hash"β true when the call context is a hash reference, like:
$obj->meta({ client_id => 1234567 });
my $id = $obj->meta->{client_id};
"list"β true when the call context is a list, like:
my @colours = $obj->colours;
"lvalue"β true when the call context is an"lvalue"subroutine, like:
$obj->name = 'John';
"object"β true when the call context is an object, like:
$obj->something->another_method();
"refscalar"β
my $name = ${$obj->name};
"rvalue"β true when the call context is from the right-hand side.
my $name = $obj->name;
"scalar"β true when the call context is a scalar:
my $name = $obj->name;
say $name; # John
"void"β true when the call context is void, like:
$obj->pointless();
See also Wanted for more on this context-rich information π
π¦ _set_get_class
Given an object property name, a dynamic class fiels definition hash (dictionary), and optional arguments, this special method creates perl packages on the fly by calling the support method "__create_class" πͺ
For example, consider the following:
#!/usr/local/bin/perl
BEGIN
{
use strict;
use Data::Dumper;
};
{
my $o = MyClass->new( debug => 3 );
$o->setup->age( 42 );
print( "Age is: ", $o->setup->age, "\n" );
print( "Setup object is: ", $o->setup, "\n" );
$o->setup->billing->interval( 'month' );
print( "Billing interval is: ", $o->setup->billing->interval, "\n" );
print( "Billing object is: ", $o->setup->billing, "\n" );
$o->setup->rgb( 255, 122, 100 );
print( "rgb: ", join( ', ', @{$o->setup->rgb} ), "\n" );
exit( 0 );
}
package MyClass;
BEGIN
{
use strict;
use lib './lib';
use parent qw( Module::Generic );
};
sub setup
{
return( shift->_set_get_class( 'setup',
{
name => { type => 'scalar' },
# or being lazy:
# name => 'scalar',
age => { type => 'number' },
metadata => { type => 'hash' },
rgb => { type => 'array' },
url => { type => 'uri' },
online => { type => 'boolean' },
created => { type => 'datetime' },
billing => { type => 'class', definition =>
{
interval => { type => 'scalar' },
frequency => { type => 'number' },
nickname => { type => 'scalar' },
}}
}) );
}
1;
__END__
And this is what you get out of it, bestie:
Age is: 42
Setup object is: MyClass::Setup=HASH(0x7fa805abcb20)
Billing interval is: month
Billing object is: MyClass::Setup::Billing=HASH(0x7fa804ec3f40)
rgb: 255, 122, 100
The whole point of using this instead of _set_get_hash_as_object is that here you are the main character β you decide exactly which fields / methods get supported, and with which data type. No mystery keys sliding in uninvited. π
πΈ _set_get_class_array
You hand it an object property name, a dictionary for building a dynamic class with __create_class, and an array reference of hash references, and it'll give you back an array of objects β each one matching one set of data from the array reference. It's giving factory-line but make it chic. π
So say your module has a method like this:
sub products { return( shift->_set_get_class_array( 'products',
{
name => { type => 'scalar' },
customer => { type => 'object', class => 'My::Customer' },
orders => { type => 'array_as_object' },
active => { type => 'boolean' },
created => { type => 'datetime' },
metadata => { type => 'hash' },
stock => { type => 'number' },
url => { type => 'uri' },
}, @_ ) ); }
Then your script calls it like this:
$object->products([
{ name => 'Cool product', customer => { first_name => 'John', last_name => 'Doe', email => 'john.doe@example.com' }, active => 1, stock => 10, created => '2020-04-12T07:10:30' },
{ name => 'Awesome tool', customer => { first_name => 'Mary', last_name => 'Donald', email => 'm.donald@example.com' }, active => 1, stock => 15, created => '2020-05-12T15:20:10' },
]);
And boom β it stores an array reference containing 2 objects with all that above data. Two products, fully objectified. Slay. β¨
π¦ _set_get_class_array_object
Literally the same as _set_get_class_array, except this one returns an array object instead of just a plain perl array. Fancy.
When it's called in list context, it returns its values as a list; otherwise it returns an array object. Context is everything, iykyk. π«
π _set_get_code
sub name { return( shift->_set_get_code( 'name', @_ ) ); }
or
sub name { return( shift->_set_get_code({
field => 'name',
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
You give it an object property name plus some code reference, and it stores / retrieves the current value. Tiny little vault for your coderefs. ποΈ
It returns undef and sets an error if the value you handed over is not a code reference. So don't try it. π
Alternatively, you can pass an hash reference instead of a field name and tack on extra parameters, like these:
checkβ acheckanonymous subroutine that gets called with 2 arguments: the current object, and the value being set β but before it is set. If this callback returns false, you get an error ofInvalid value provided.back, so make sure you return true to signal the check passed. The callback can die, and that'll be caught and interpreted asfalse.callbacksβ an hash reference of callbacks. You can use eithersetoradd, whichever you prefer. The callback is called with the current object and the value that has already been set. Heads up: any fatal exception during the callback will not be caught. πfieldβ the object property name.
π§ _set_get_datetime
sub created_on { return( shift->_set_get_datetime( 'created_on', @_ ) ); }
or
sub created_on { return( shift->_set_get_datetime({
field => 'created_on',
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
# optionally specify a format to use for all objects
format => '%FT%TZ',
# Can also use 'time_zone'
tz => 'UTC',
}, @_ ) ); }
Give it an object property name and some date or datetime string, and it'll try to parse that string and save it as a DateTime object. Time-travel but organized. π°οΈ
If the data is a 10 digits integer, it treats it as a unix timestamp.
Parsing also recognises special words like now. (Iconic of it, honestly.)
The DateTime object it creates gets associated with a DateTime::Format::Strptime object, which lets that DateTime object be stringified as a unix timestamp using local time stamp, whatever it is.
Even if there's no value set and you call this method in a chain, it returns a Module::Generic::Null whose whole purpose is to let you chain without doing anything meaningful. So like β if the created property of your object isn't set yet, but in your script you go:
$object->created->iso8601
Then yeah, the value of iso8601 will be empty, because that's a fake method produced by Module::Generic::Null. Not me warning you to always check the return value of a method. Okay, it is me. Always check it. π«Ά
Alternatively, you can pass an hash reference instead of a field name and add extra parameters, such as:
checkβ acheckanonymous subroutine that gets called with 2 arguments: the current object, and the value being set β but before it is set. If this callback returns false, you'll get an error ofInvalid value provided., so return true to show the check passed. The callback can die, and that gets caught and interpreted asfalse.callbacksβ an hash reference of callbacks. Use eithersetoradd, whichever you prefer. The callback gets called with the current object and the value that has already been set. Any fatal exception during the callback will not be caught. πfieldβ the object property name.formatβ an optional format that will be used to create aDateTime::Format::Strptimeattached to the DateTime object.tzβ a string representing the time zone for the the datetime. π
π₯ _set_get_enum
sub choice { return( shift->_set_get_enum( 'choice', [qw( yes no )], @_ ) ); }
# or
sub choice : lvalue { return( shift->_set_get_enum({
field => 'choice',
allowed => [qw( yes no )],
# case insensitive
case => 0,
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
This support method handles enum values β like, a list of allowed values that can be set. The VIP list, basically. π
It takes either a field name plus an array of allowed values; or an hash reference with the following supported options:
allowedβ an array reference of allowed values.caseβ a boolean value deciding whether the value received should be compared in a case sensitive (true) or case insensitive (false) way against the allowed value. So if it's true, a hypothetical valueyeswould match against theallowedvalues['yes', 'no'], but would fail if that value wereYES. Default is true.checkβ acheckanonymous subroutine called with 2 arguments: the current object, and the value being set β but before it's set. If this callback returns false, you get an error ofInvalid value provided., so make sure to return true and confirm the check passed. The callback can die, and it'll be caught and interpreted asfalse.callbacksβ an hash reference of callbacks. Use eithersetoradd, whichever you prefer. The callback gets called with the current object and the value that has already been set. Any fatal exception during the callback will not be caught. πfieldβ the field name.
π _set_get_file
sub file { return( shift->_set_get_file( 'file', @_ ) ); }
or
sub file { return( shift->_set_get_file({
field => 'file',
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
Give it an object property name and a file, and it stores that file as a Module::Generic::File object. ποΈ
It returns undef and sets an error if the value you provided is not a proper file.
Worth noting: the file doesn't actually have to exist β it can also be a directory, or a symbolic link, or any other file on the system. It's very "no thoughts, head empty, still valid" of it. β¨
Alternatively, you can pass an hash reference instead of a field name and add extra parameters, such as:
checkβ acheckanonymous subroutine called with 2 arguments: the current object, and the value being set β but before it is set. If this callback returns false, then an error ofInvalid value provided.will be returned, so make sure to return true to indicate that the check passed. The callback can die, and it will be caught, and be interpreted asfalse.callbackscallbacksβ an hash reference of callbacks, bestie. You can use eithersetoradd, whichever you prefer. The callback gets called with the current object and the value that's already been set. Heads up though: any fatal exception during the callback will not be caught. π¬fieldβ the object property name.
π _set_get_glob π
sub handle { return( shift->_set_get_glob( 'handle', @_ ) ); }
or
sub handle { return( shift->_se_set_get_globt_get({
field => 'handle',
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
You hand it an object property name plus a glob (that's a file handle, babe π«Ά) and it stores the glob for you.
It returns undef and sets an error if the value you passed isn't a glob. No glob, no glory.
Alternatively you can pass it an hash reference instead of a field name and sneak in extra parameters, like:
checkβ acheckanonymous subroutine that gets called with 2 arguments: the current object, and the value being set β and this happens before it's actually set. If this callback returns false, then an error ofInvalid value provided.comes back at you, so please make sure to return true to say the check passed. The callback can die, and it will be caught, and interpreted asfalse.callbacksβ an hash reference of callbacks. Usesetoradd, whichever you vibe with. The callback is called with the current object and the value that's already been set. Any fatal exception during the callback won't be caught. πfieldβ the object property name.
πΈ _set_get_hash πΈ
sub metadata { return( shift->_set_get_hash( 'metadata', @_ ) ); }
or
sub metadata { return( shift->_set_get_hash({
field => 'metadata',
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
Give it an object property name and an hash reference, and it sets that property to that hash reference. Literally that simple.
You can even pass it an associative array and it'll be saved as a hash reference, like this:
$object->metadata(
transaction_id => 123,
customer_id => 456
);
my $hash = $object->metadata;
Alternatively, you can provide an hash reference instead of a field name and pass additional parameters, such as:
checkβ acheckanonymous subroutine called with 2 arguments, the current object and the value being set, but before it is set. If it returns false, then an error ofInvalid value provided.gets returned, so make sure to return true to indicate the check passed. The callback can die, and it will be caught and interpreted asfalse.callbacksβ an hash reference of callbacks. Eithersetoradd, whichever you prefer. The callback is called with the current object and the value that has already been set. Any fatal exception during the callback will not be caught. πfieldβ the object property name.
π¦ _set_get_hash_as_mix_object π¦
sub metadata { return( shift->_set_get_hash_as_mix_object( 'metadata', @_ ) ); }
or
sub metadata { return( shift->_set_get_hash_as_mix_object({
field => 'metadata',
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
You give it an object property name and an optional hash reference, and it hands you back a Module::Generic::Hash object β¨ β which lets you manipulate the hash just like any regular hash, but on top it gives you all the object oriented methods described in detail in Module::Generic::Hash. Obsessed.
This is different from _set_get_hash_as_object below, whose keys and values are accessed as dynamic methods and method arguments. Don't mix them up, bestie. π
Alternatively, you can provide an hash reference instead of a field name and pass additional parameters, such as:
checkβ acheckanonymous subroutine called with 2 arguments, the current object and the value being set, but before it is set. If it returns false, then an error ofInvalid value provided.will be returned, so make sure to return true to show the check passed. The callback can die, and it will be caught and interpreted asfalse.callbacksβ an hash reference of callbacks.setoradd, whichever you prefer. The callback is called with the current object and the value that has already been set. Any fatal exception during the callback will not be caught. πfieldβ the object property name.
β _set_get_hash_as_object β
Provided with an object property name, an optional class name and an hash reference, and this does the same thing as in _set_get_hash, except it creates a class/package dynamically with a method for each of the hash keys, so you can call the hash keys as methods. Main character energy fr fr. π
Also it does this recursively while handling looping β in which case it reuses the object previously created β and it also takes care of adapting the hash key to a proper field name, so something like 99more-options would become more_options. If the value itself is a hash, it processes it recursively, transforming 99more-options into a proper package name MoreOptions prepended by $class_name provided as argument, or whatever upper package was used in the recursion processing.
For example in your module:
sub metadata { return( shift->_set_get_hash_as_object( 'metadata', @_ ) ); }
Then populating the data:
$object->metadata({
first_name => 'John',
last_name => 'Doe',
email => 'john.doe@example.com',
});
printf( "Customer name is %s\n", $object->metadata->last_name );
π§΅ THREAD SAFETY π§΅
This method uses Module::Generic::Dynamic, and thus is not thread-safe. It should be invoked only during initialization, before any threads are spawned.
For safe usage in multi-threaded environments, avoid using dynamic class creation features at runtime. No cap, this one actually matters. π¨
π _set_get_ip π
sub ip { return( shift->_set_get_ip( 'ip', @_ ) ); }
or
sub ip { return( shift->_set_get_ip({
field => 'ip',
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
This helper method takes a value and checks if it's a valid IP address using _is_ip. If undef or a zero-byte value is provided, it'll just accept it, since the caller can use it to reset the value. Valid.
If a value is successfully set, it returns a Module::Generic::Scalar object representing the string you passed. From there you can hand the result over to Net::IP in your own code, assuming you have that module installed. β¨
Alternatively, you can provide an hash reference instead of a field name and pass additional parameters, such as:
checkβ acheckanonymous subroutine called with 2 arguments, the current object and the value being set, but before it is set. If this callback returns false, then an error ofInvalid value provided.will be returned, so make sure to return true to indicate that the check passed. The callback can die, and it will be caught and interpreted asfalse.callbacksβ an hash reference of callbacks.setoradd, whichever you prefer, bestie. The callback is called with the current object and the value that has already been set. Any fatal exception during the callback will not be caught. πfieldβ the object property name.
π _set_get_lvalue π
This is now an alias for _set_get_callback.
π₯ _set_get_number π₯
Provided with an object property name and a number, and this will create a Module::Generic::Number object and return it. Slay.
If an invalid value is provided (e.g., empty string, non-stringifiable reference, or a value failing the constraint), an error is returned, accessible via the object's error method.
As of version v0.13.0 it also works as an lvalue method. See perlsub.
In your module:
package MyObject;
use parent qw( Module::Generic );
sub level : lvalue { return( shift->_set_get_number( 'level', @_ ) ); }
# or
sub level : lvalue { return( shift->_set_get_number({
field => 'level',
check => sub {
my( $self, $value ) = @_;
# do some check here
return(1); # Do not forget to return true
},
constraint => 'unsigned_int',
# or
# constraint => qr/\d+/,
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
In the script using module MyObject:
my $obj = MyObject->new;
$obj->level = 3; # level is now 3
# or
$obj->level(4) # level is now 4
print( "Level is: ", $obj->level, "\n" ); # Level is 4
print( "Is it an odd number: ", $obj->level->is_odd ? 'yes' : 'no', "\n" );
# Is it an od number: no
$obj->level++; # level is now 5
Alternatively, you can pass an hash reference instead of an object property to provide callbacks that will be called upon addition or removal of value.
This hash reference can contain the following properties:
callbacksβ an hash reference of callbacks. You can use eithersetoradd, whichever you prefer. π
The callback gets called with the current object, plus the value that's already been set. π
Any fatal exception during the callback is not caught β no cap, it just dies out in the open. π
checkβ acheckanonymous subroutine that gets called with 2 arguments: the current object, and the value being set β but before it actually gets set. If this callback returns false, you get an error ofInvalid value provided.back, so make sure you return true to say the check passed, bestie. The callback can die, and that will be caught and interpreted asfalse. πconstraintβ either a regular expression handed to you via theqr//operator, or one of these known tokens:bin,binaryβ a binary, supporting fractional values (e.g.,101.11) and scientific notation (e.g.,101.11E-2). π¦hex,hexadecimalβ a hexadecimal, supporting fractional values (e.g.,"FF.1A") and scientific notation (e.g.,"FF.1AE-2").int,integerβ an integer, periodt.longβ a long integer, i.e. an integer between-2,147,483,648and2,147,483,647.negative_intβ a negative integer.oct,octalβ an octal number, supporting fractional values (e.g.,77.33) and scientific notation (e.g.,77.33E-2).shortβ a short integer, i.e. an integer between-32,768and32,767.unsigned_int,unsigned_integer,positive_intβ an unsigned integer, on which is aliased a positive integer. πunsigned_longβ an unsigned long integer, i.e. an integer between 0 and4,294,967,295(2^32-1).unsigned_real,unsigned_dec,unsigned_decimal,unsigned_float,unsigned_double,positive_real,positive_dec,positive_decimal,positive_float,positive_doubleβ a positive decimal number. β¨unsigned_shortβ an unsigned short integer, i.e. an integer between 0 and65,535(2^16-1).real,dec,decimal,double,floatβ a real number, on which is aliased a decimal number, double-precision number, and floating-point number.signed_byte,byteβ a signed byte, i.e. an integer between -128 and 127.unsigned_byteβ an unsigned byte, i.e. an integer between 0 and 255. π
fieldβ the object property name.undef_okβ if this is set to a true value, this support method will allow undef to be set. Defaults to false, which means an undefined value you pass in just gets ignored silently. π
For example:
sub length { return( shift->_set_get_number({
field => 'length',
callbacks =>
{
set => '_some_add_callback',
},
}), @_ ); }
The value of the callback can be either a subroutine name or a code reference. Slay.
β¨ _set_get_number_as_scalar β¨
sub name { return( shift->_set_get_number_as_scalar( 'name', @_ ) ); }
or
sub name { return( shift->_set_get({
field => 'name',
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
This sets or gets a number as a regular string, but checks the value really is a number by using Regexp::Common. Vibe check passed. β
Alternatively, you can hand it an hash reference instead of a field name, and pass extra parameters like:
checkβ acheckanonymous subroutine that gets called with 2 arguments: the current object, and the value being set, but before it's set. If it returns false, then an error ofInvalid value provided.comes back, so return true to signal the check passed, okay? The callback can die, and that gets caught and interpreted asfalse. πcallbacksβ an hash reference of callbacks. You can use eithersetoradd, whichever you prefer. π The callback gets called with the current object, and the value that's already been set. Any fatal exception during the callback is not caught.fieldβ the object property name.
β¨ _set_get_number_or_object β¨
Provided with an object property name and a number or an object, and this calls the value using _set_get_number or _set_get_object respectively. Two moods, one method. π¦
β¨ _set_get_object β¨
sub myobject { return( shift->_set_get_object({ field => 'myobject', no_init => 1 }, My::Class, @_ ) ); }
sub myobject { return( shift->_set_get_object({
field => 'myobject',
no_init => 1,
callback => sub
{
my( $class, $args ) = @_;
return( $class->new( $args->[0] ) );
},
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
}, My::Class, @_ ) ); }
sub myobject { return( shift->_set_get_object( 'myobject', My::Class, @_ ) ); }
Provided with an object property name, a class/package name and some data, and this will initiate a new object of the given class, passing it the data. π
The property name can also be an hash reference that gives you more granular settings:
callbackβ a callback code reference that gets passed the module class name and the arguments as an array reference. This is how you instantiate the module object in a particular way and/or get finer control over object instantiation. Any fatal error during object instantiation is caught, an error gets set, andundefgets returned in scalar context β or an empty list in list context. πcheckβ acheckanonymous subroutine that gets called with 2 arguments: the current object, and the value being set, but before it's set. If it returns false, then an error ofInvalid value provided.gets returned, so make sure you return true to indicate the check passed. The callback can die, and that gets caught and interpreted asfalse. πfieldβ the actual property name.no_initβ boolean that, when set, says "do not instantiate a class object if one isn't instantiated yet". Main character energy: sometimes you just don't. π
If you pass an undefined value, it will set the property as undefined, removing whatever was set before.
You can also provide an existing object of the given class. _set_get_object will check the object provided really does belong to the specified class, or it'll set an error and return undef. It returns the object currently set, if any. π«Ά
β¨ _set_get_object_array β¨
sub mymethod { return( shift->_set_get_object_array( 'mymethod', 'Some::Module', @_ ) ); }
# or
sub mymethod { return( shift->_set_get_object_array({
field => 'mymethod',
callback => sub
{
my( $class, $args ) = @_;
return( $class->new( $args->[0] ) );
},
check => sub
{
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks =>
{
array_add => sub
{
my $def = shift( @_ );
my( $pos, $ref ) = @$def{qw( start added )};
return unless( blessed( $ref->[0] ) && $ref->[0]->isa( 'MyPackage') );
return(1);
},
array_remove => sub
{
my $def = shift( @_ );
my( $start, $end ) = @$def{qw( start end )};
printf( STDERR "Called from package %s at line %d\n", @{$def->{caller}}[0,2] );
# Do some check to accept or reject
return(1); # always return true to accept
},
},
}, 'Some::Module', @_ ) ); }
Provided with an object property name and a class/package name, and similar to _set_get_object_array2, this will create an array reference of objects. π
Alternatively, you can pass an hash reference instead of the object property name, with these properties:
fieldβ Mandatory. The object property name.callbackβ Optional. A code reference, like an anonymous subroutine, that gets called with the class and an array reference of values provided β possibly empty. π§callbacksβ Optional. An hash of keys-callbacks pairs. The supported callback types are:array_add, andarray_remove. When set, those callbacks get called when data is added to or removed from the array. Be careful though β this may slow down your application depending on how often the array gets called, and what the callback routine actually does. π¬ See thecallbackin Module::Generic::Array for more info, and also the methodsget_callbackin Module::Generic::Array, andhas_callbackin Module::Generic::Array.checkβ acheckanonymous subroutine that gets called with 2 arguments: the current object, and the value being set, but before it's set. If it returns false, then an error ofInvalid value provided.gets returned, so make sure to return true to indicate the check passed. The callback can die, and that gets caught and interpreted asfalse. π
This is a useful callback when the module instantiation either doesn't use the new method or doesn't simply take one or multiple arguments β like when the instantiation method would require an hash of parameters, such as Email::Address::XS. π₯
sub emails { return( shift->_set_get_object_array({
field => 'emails',
callback => sub
{
my( $class, $args ) = @_;
return( $class->parse_bare_address( $args->[0] ) );
},
}, 'Email::Address::XS', @_ ) ); }
_set_get_object_array2
sub mymethod { return( shift->_set_get_object_array2( 'mymethod', 'Some::Module', @_ ) ); }
# or
sub mymethod { return( shift->_set_get_object_array2({
field => 'mymethod',
callback => sub
{
my( $class, $args ) = @_;
return( $class->new( $args->[0] ) );
},
check => sub
{
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
}, 'Some::Module', @_ ) ); }
Okay so you hand it an object property name, a class/package name, and an array ref β and that array ref holds array refs, which each hold hash refs or objects. What you get back is an array of array of objects. Nested. Very demure, very mind-bending, but it slays πβ¨
Alternatively, instead of the object property name you can pass an hash reference with these properties:
fieldβ Mandatory. The object property name.callbackβ Optional. A code reference, like an anonymous subroutine, that gets called with the class and an array reference of values provided β but possibly empty.checkβ Acheckanonymous subroutine called with 2 arguments: the current object, and the value being set, but before it is set.
If this callback returns false, then an error of Invalid value provided. gets returned, so make sure to return true to indicate that the check passed π«Ά
The callback can die, and it will be caught, and be interpreted as false. No cap, die is just a dramatic way of saying no π
This is a useful callback when the module instantiation either does not use the new method or does not simply take one or multiple arguments, such as when the instantiation method would require an hash of parameters, such as Email::Address::XS.
_set_get_object_array_object
Provided with an object property name, a class/package name and some data, and this will create an array of object similar to _set_get_object_array, except the array produced is a Module::Generic::Array π
This method accepts the same arguments as _set_get_object_array.
_set_get_object_lvalue
Same as _set_get_object_without_init but with the possibility of setting the object value as an lvalue method:
$o->my_property = $my_object;
Literally so satisfying, assignment syntax goes brrr π
_set_get_object_variant
sub name { return( shift->_set_get_object_variant( 'name', @_ ) ); }
or
sub name { return( shift->_set_get_object_variant({
field => 'name',
check => sub {
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks => {
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
Provided with an object property name, a class/package name and some data, and depending whether the data provided is an hash reference or an array reference, this will either instantiate an object for the given hash reference or an array of objects with the hash references in the given array π¦
This means the value stored for the object property will vary between an hash or array reference. It's giving versatile. It's giving main character energy.
Alternatively, you can pass an hash reference, instead of the object property name, with the following properties:
fieldβ Mandatory. The object property name.callbackβ Optional. A code reference, like an anonymous subroutine, that will be called with the class and an array reference of values provided, but possibly empty.checkβ Acheckanonymous subroutine that will be called with 2 arguments, the current object, and the value being set, but before it is set.
If this callback returns false, then an error of Invalid value provided. will be returned, so make sure to return true to indicate that the check passed π
The callback can die, and it will be caught, and be interpreted as false.
This is a useful callback when the module instantiation either does not use the new method or does not simply take one or multiple arguments, such as when the instantiation method would require an hash of parameters, such as Email::Address::XS:
sub emails { return( shift->_set_get_object_variant({
field => 'emails',
callback => sub
{
my( $class, $args ) = @_;
return( $class->parse_bare_address( $args->[0] ) );
},
}, 'Email::Address::XS', @_ ) ); }
_set_get_object_without_init
sub mymethod { return( shift->_set_get_object_without_init( 'mymethod', 'Some::Module', @_ ) ); }
# or
sub mymethod { return( shift->_set_get_object_without_init({
field => 'mymethod',
callback => sub
{
my( $class, $args ) = @_;
return( $class->new( $args->[0] ) );
},
check => sub
{
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
}, 'Some::Module', @_ ) ); }
# then
my $this = $obj->mymethod; # possibly undef if it was never instantiated
# return the C<Some::Module> object after having instantiated it
my $this = $obj->mymethod( some => parameters );
Sets or gets an object, but contrary to _set_get_object, this method will not try to instantiate the object β unless of course you pass it some values. No values? No object. It's giving respect my boundaries π
Alternatively, you can pass an hash reference, instead of the object property name, with the following properties:
fieldβ Mandatory. The object property name.callbackβ Optional. A code reference, like an anonymous subroutine, that will be called with the class and an array reference of values provided, but possibly empty.
Whatever this returns will set the value for this object property.
checkβ Acheckanonymous subroutine that will be called with 2 arguments, the current object, and the value being set, but before it is set.
If this callback returns false, then an error of Invalid value provided. will be returned, so make sure to return true to indicate that the check passed πΈ
The callback can die, and it will be caught, and be interpreted as false.
This is a useful callback when the module instantiation either does not use the new method or does not simply take one or multiple arguments, such as when the instantiation method would require an hash of parameters, such as Email::Address::XS.
_set_get_scalar
sub name { return( shift->_set_get_scalar( 'name', @_ ) ); }
or
sub name { return( shift->_set_get_scalar({
field => 'name',
check => sub
{
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks =>
{
set => sub
{
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
Provided with an object property name, and a string β possibly a number, or literally anything really β and this will set the property value accordingly. Very straightforward. Very slay. β
Alternatively, you can pass an hash reference instead of an object property to provide callbacks that will be called upon addition or removal of value.
This hash reference can contain the following properties:
fieldβ The object property name.callbacksβ An hash reference of operation typeadd(orset), orgetto callback subroutine name or code reference pairs.checkβ Acheckanonymous subroutine that will be called with 2 arguments, the current object, and the value being set, but before it is set.
If this callback returns false, then an error of Invalid value provided. will be returned, so make sure to return true to indicate that the check passed π
The callback can die, and it will be caught, and be interpreted as false.
For example:
sub name { return( shift->set_get_scalar({
field => 'name',
callbacks =>
{
set => '_some_add_callback',
get => sub
{
my $self = shift( @_ );
# do something that returns a value.
},
},
}), @_ ); }
The value of the callback can be either a subroutine name or a code reference.
It returns the currently value stored. Yes, that's exactly what it says, and yes, it's iconic π
_set_get_scalar_as_object
sub name { return( shift->_set_get_scalar_as_object( 'name', @_ ) ); }
or
sub name { return( shift->_set_get_scalar_as_object({
field => 'name',
check => sub
{
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks =>
{
set => sub
{
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
Provided with an object property name, and a string or a scalar reference, and this stores it as an object of Module::Generic::Scalar β¨
If there is already an object set for this property, the value provided will be assigned to it using set in Module::Generic::Scalar.
If it is called and not value is set yet, this will instantiate a Module::Generic::Scalar object with no value. Empty queen behavior π
So a call to this method can safely be chained to access the Module::Generic::Scalar methods. For example:
sub name { return( shift->_set_get_scalar_as_object( 'name', @_ ) ); }
Then, calling it:
$object->name( 'John Doe' );
Getting the value:
my $cust_name = $object->name;
print( "Nothing set yet.\n" ) if( !$cust_name->length );
Alternatively, you can pass an hash reference instead of an object property to provide callbacks that will be called upon addition or removal of value.
This hash reference can contain the following properties:
fieldβ The object property name.callbacksβ An hash reference of operation typeadd(orset), orgetto callback subroutine name or code reference pairs.checkβ Acheckanonymous subroutine that will be called with 2 arguments, the current object, and the value being set, but before it is set.
So if your check callback comes back false, you get an error of Invalid value provided. β so bestie, make sure you actually return true to signal that the check passed, ok? π
And if the callback dies? No drama, it gets caught, and it's interpreted as false. π
Like, for example:
sub name { return( shift->set_get_scalar_as_object({
field => 'name',
callbacks =>
{
set => '_some_add_callback',
},
}), @_ ); }
And the callback's value? It can be either a subroutine name or a code reference. Both valid, both slay. β¨
_set_get_scalar_or_object
Give this one an object property name plus a class/package name and it stores the value as an object β it calls _set_get_object if the value is an object of class class, or as a string by calling _set_get_scalar. Dual-threat behaviour, we love to see it. π
If no value has been set yet, this returns a Module::Generic::Null object so you can keep chaining. No "can't call method on undefined value" jumpscare today. π«Ά
_set_get_uri
sub uri { return( shift->_set_get_uri( 'uri', @_ ) ); }
sub uri { return( shift->_set_get_uri( { field => 'uri', class => 'URI::Fast' }, @_ ) ); }
or
sub uri { return( shift->_set_get_uri({
field => 'uri',
check => sub
{
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks =>
{
set => sub
{
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
Hand it an object property name and a uri, and it builds a URI object and sets the property value to match. π¦
Alternatively, the property name can be an hash with the following properties:
fieldβ the object property name πcheckβ acheckanonymous subroutine, called with 2 arguments: the current object, and the value being set β but before it's set. If this callback returns false, then an error ofInvalid value provided.gets returned, so make sure you return true to indicate the check passed. The callback can die, and it will be caught, and interpreted asfalse. πcallbacksβ an hash reference of callbacks. You can use eithersetoradd, whichever you prefer. The callback is called with the current object, and the value that has already been set. Heads up tho: any fatal exception during the callback will not be caught. πclassβ the URI class to use. By default,URI, but you could also useURI::Fast, or other class of your choice. That class will be loaded, if it's not loaded already. β¨
It accepts an URI object (or any other URI class object), an uri or urn string, or an absolute path, i.e. a string starting with /.
It returns the current value, if any, so the return value could be undef β which means it cannot be chained. Maybe it should return a Module::Generic::Null object ? π€ (the author is thinking out loud, we respect it)
_set_get_uuid
sub id { return( shift->_set_get_uuid( 'id', @_ ) ); }
or
sub id { return( shift->_set_get_uuid({
field => 'id',
check => sub
{
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks =>
{
set => sub
{
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
Provided with an object, a property name, and an UUID (Universal Unique Identifier) and this stores it as an object of Module::Generic::Scalar. π
If an empty or undefined value is provided, it will be stored as is. No thoughts, just vibes.
But here's the cute part: if there's no value and this method is called in object context β like in chaining β it returns a special Module::Generic::Null object that prevents the perl error you'd otherwise get from calling whatever method follows on an undefined value. Guardian angel behaviour. π§
Alternatively, you can provide an hash reference instead of a field name, and pass additional parameters, such as:
checkβ acheckanonymous subroutine called with 2 arguments: the current object, and the value being set, but before it is set. If it returns false, then an error ofInvalid value provided.is returned, so bestie β return true to indicate the check passed. If the callback dies, it'll be caught and interpreted asfalse. πcallbacksβ an hash reference of callbacks. You can use eithersetoradd, whichever you prefer. The callback gets called with the current object, and the value that has already been set. And again: any fatal exception during the callback will not be caught. πfieldβ the object property name β
_set_get_version
sub version { return( shift->_set_get_version( 'version', @_ ) ); }
# or
sub version : lvalue { return( shift->_set_get_version( 'version', @_ ) ); }
# or
sub version : lvalue { return( shift->_set_get_version( { field => 'version', class => 'Perl::Version' }, @_ ) ); }
or
sub version { return( shift->_set_get_version({
field => 'version',
check => sub
{
my( $self, $value ) = @_; # do some check
return(1); # Do not forget to return true
},
callbacks =>
{
set => sub {
my( $self, $value ) = @_;
# Do something here with the value set.
},
},
}, @_ ) ); }
Provided with an object, a property name, and a version string, and this stores it as an object of version by default. π
Alternatively, the property name can be an hash with the following properties:
fieldβ the object property name πcheckβ acheckanonymous subroutine, called with 2 arguments: the current object, and the value being set, but before it is set. If this callback returns false, then an error ofInvalid value provided.is returned β so make sure to return true to indicate the check passed. The callback can die, and it will be caught, and interpreted asfalse. πcallbacksβ an hash reference of callbacks. You can use eithersetoradd, whichever you prefer. The callback is called with the current object, and the value that has already been set. Any fatal exception during the callback will not be caught. π¬classβ the version class to use. By default,version, but you could also usePerl::Version, or other class of your choice. That class will be loaded, if it's not loaded already. π₯
The value can also be assigned as an lvalue. Assuming you have a method version that implements _set_get_version:
$obj->version = $version;
would work, but of course also:
$obj->version( $version );
The value can be a legitimate version string, or a version object matching the class to be used, which is by default version. If it's a string, it will be made an object of the class specified using parse if that class supports it, or by simply calling new.
When called in get mode, it will convert any value pre-set, if any, into a version object of the specified class if the value isn't an object of that class already, and return it β or else it will return an empty string or undef, whatever you will have set in your object for this property. It's giving β¨adaptableβ¨.
_set_symbol
$o->_set_symbol(
# class defaults to the current object class
variable => '$some_scalar_ref',
# variable value defaults to scalar reference to undef
# or [], {}, sub{} depending on the variable type
);
# or
$o->_set_symbol(
class => 'Foo::Bar',
variable => '$some_scalar_name',
value => \"some string reference",
);
# or
$o->_set_symbol(
class => 'Foo::Bar',
variable => '@some_array_name',
value => $an_array_reference,
);
# or
$o->_set_symbol(
class => 'Foo::Bar',
variable => '%some_array_name',
value => $an_hash_reference,
);
# or
$o->_set_symbol(
class => 'Foo::Bar',
variable => '&some_sub_name',
value => $a_code_reference,
);
# or
$o->_set_symbol(
class => 'Foo::Bar',
# explicitly specify the variable type
type => 'hash',
variable => '$some_hash_name',
value => $an_hash_reference,
);
# or
$o->_set_symbol(
class => 'Foo::Bar',
type => 'array',
variable => '$some_array_name',
value => $an_array_reference,
);
# or
$o->_set_symbol(
class => 'Foo::Bar',
type => 'scalar',
variable => '$some_array_name',
value => $a_scalar_reference,
);
# or
$o->_set_symbol(
class => 'Foo::Bar',
type => 'code',
variable => '$some_sub_name',
# Like \&some_thing, or maybe sub{ # do something here }
value => $a_code_reference,
);
This method is used to dynamically add a new symbol to a given class, a.k.a. package. A proper symbol type can only be an array reference, an hash reference, a scalar, a code reference, or a glob. This is useful for metaprogramming or creating dynamically extensible APIs. Like, actual wizardry. π§ββοΈβ¨
This takes the following options:
classβ the class, or package name to add the new symbol to. πend_lineβ an integer to specify the end line of the the code reference, represented by the variable, in the class provided. If none is provided, the value forstart_linewill be used. (And yes, "the the" is exactly what the original says β we don't edit the icons. π )filenameβ an optional filename to associate the new symbol with. For example/some/where/file.pl. This is only used when perl debugging is enabled and for variables that are code reference. If no filename is provided, it will default to the value returned bycallerin perlfunc. πstart_lineβ an integer to specify the start line of the code reference, represented by the variable, in the class provided. If no start line is provided, it will default to0. πΈtypetypeβ optional, bestie, but lowkey iconic when you need it π It lets you explicitly set what kind of symbol this is:"scalar","array","hash","code", or"glob". Setting it overrides the sigil-detection logic completely.
If you don't pass it, the type comes straight from the sigil β literally the first character of the variable name. And that sigil decides how the variable gets accessed from the package name. Watch:
$o->_set_symbol(
class => 'Foo::Bar',
variable => '@some_array',
value => [qw( John Peter Paul )],
);
Then @Foo::Bar::some_array is accessible, but $Foo::Bar::some_array is not. Flip it around though:
$o->_set_symbol(
class => 'Foo::Bar',
variable => '$some_array',
value => [qw( John Peter Paul )],
);
and now $Foo::Bar::some_array is accessible, but @Foo::Bar::some_array isn't. Slay, but also: choose your sigil wisely π
Now β if you'd rather hand over a variable name with a dollar sign because you're working with a reference, that's totally fine too. In that case the type gets derived from the value you pass, if that value is an array, a code reference, or a hash.
But heads up, there's a slight difference in the symbol table π Variables starting with % or @ can only be retrieved later using that same sigil. If an array, hash, or code reference variable is stored with $, it gets stored as "REF", and you must dereference it when you retrieve the symbol later. Like so:
$o->_set_symbol(
variable => '$some_array_name',
value => [qw( John Peter Paul )],
);
my $sym = $o->_get_symbol( '$some_array_name' );
my $ref = $$sym;
say "@$ref"; # John Peter Paul
Whereas:
$o->_set_symbol(
variable => '@some_array_name',
value => [qw( John Peter Paul )],
);
my $sym = $o->_get_symbol( '@some_array_name' );
say "@$sym"; # John Peter Paul
Acceptable value types are: "array", "code", "glob", "hash", or "scalar" β but also "lvalue", "regexp", and "vstring" π¦
valueβ a reference to the value you want to assign to that new symbol. Example: a scalar reference, an arrayref, a coderef, whatever fits β¨
If the value isn't suitable for the new symbol, you get an error back. No silent nonsense, periodt.
variableβ the variable name including its"sigil", i.e. that first character of a variable name, such as$,%,@, or&π«
See also "_get_symbol" to retrieve the symbol you just set π«Ά
β οΈ THREAD SAFETY WARNING β οΈ
Okay bestie, listen to me very carefully because this is the serious part π
_set_symbol is not thread-safe. It modifies the package's symbol table (via *{...} operations), and that thing is global β shared across all threads. No cap.
Injecting or redefining symbols dynamically after threads have been created can cause race conditions, unexpected behavior, or crashes. It's giving chaos, genuinely π
Safe Usage Recommendations
- Call
"_set_symbol"only before any threads are created (i.e., during the startup/init phase) πΈ - Avoid calling this method at runtime inside threads or shared libraries if thread safety is a concern.
- If using with threads, consider guarding symbol modification with external synchronization (e.g., a global mutex). However β and this is the important bit β this cannot prevent race conditions if the symbol is already in use.
Possible Safer Alternatives
Per-object or closure-based dispatch may be preferable if your use case allows:
$object->{some_accessor} = sub { ... };
Or use a dynamic delegation pattern via AUTOLOAD:
sub AUTOLOAD {
my $method = our $AUTOLOAD;
return $self->{dynamic_methods}{$method}->(@_);
}
However, if you truly need to define package-level symbols, this method remains appropriate β just observe the threading caveats above, k? π
π _str_val π
my $str = $self->_str_val( $some_object );
This takes a value β possibly an object, especially one that stringifies β and hands you back its string representation β¨
It does the same thing as "overload::StrVal", except it handles undefined values, and it's called on your class object.
If the value you pass is "undef", or if you didn't pass a value at all, this just returns an empty string ''. Designed that way on purpose so perl won't warn at you about undefined values being used π«Ά no drama, no warnings.
π¦ _to_array_object π¦
Provided with arguments or not, and this returns a Module::Generic::Array object made of that data π
my $array = $self->_to_array_object( qw( Hello world ) ); # Becomes an array object of 'Hello' and 'world'
my $array = $self->_to_array_object( [qw( Hello world )] ); # Becomes an array object of 'Hello' and 'world'
Either way you slice it, you get the same array object. Iconic.
πΈ _warnings_is_enabled πΈ
Called with the class object, or by handing it another class object as an argument, and this returns true if warnings are enabled for the given class, false otherwise.
Example:
$self->_warnings_is_enabled();
# Providing another class object
$self->_warnings_is_enabled( $other_object );
π _warnings_is_registered π
Same deal: called with the class object, or with another class object as an argument, and this returns true if warnings are registered for the given class, false otherwise.
And this one's genuinely useful, because calling warnings::enabled() to check whether warnings are enabled for a given class β when that class hasn't registered for warnings using the pragma use warnings::register β will throw an error: "Unknown warnings category" π
Example:
$self->_warnings_is_registered();
# Providing another class object
$self->_warnings_is_registered( $other_object );
π₯ __dbh π₯
If your module has the global variable DB_DSN, this creates a database handler using DBI ποΈβ¨
It'll also use the following global variables in your module to set up the database object: DB_RAISE_ERROR, DB_AUTO_COMMIT, DB_PRINT_ERROR, DB_SHOW_ERROR_STATEMENT, DB_CLIENT_ENCODING, DB_SERVER_PREPARE.
And if DB_SERVER_PREPARE is provided and true, pg_server_prepare gets set to true in the database handler.
It returns the database handler object. Obsessed β one line and you've got a dbh.
β DEBUG β
Returns the value of your global variable DEBUG, if there is one.
π VERBOSE π
Returns the value of your global variable VERBOSE, if there is one.
π ERROR & EXCEPTION HANDLING π
So the whole design philosophy here is a vibe check on control flow: only the main part of the application should be in charge of the flow and triggering exit. Because of that, this module and every other module in this distribution do not die β they set an error and return undef instead. So like, always check the return value, bestie. Always π
Errors that get triggered are transformed into a Module::Generic::Exception object, or whatever exception class is specified by the object property "_exception_class". For example:
sub init
{
my $self = shift( @_ );
$self->SUPER::init( @_ ) || return( $self->pass_error );
$self->{_exception_class} = 'My::Exception';
return( $self );
}
Those error objects can then be retrieved by calling "error" π
If, however, you wanted errors triggered to be fatal, you can set the object property "fatal" to a true value and/or set your package global variable $FATAL_ERROR to true. When "error" is called with an error, it will "die" in perlfunc with the error object rather than merely returning "undef". For example:
package My::Module;
BEGIN
{
use strict;
use warnings;
use parent qw( Module::Generic );
our $VERSION = 'v0.1.0';
our $FATAL_ERROR = 1;
};
sub init
{
my $self = shift( @_ );
$self->{fatal} = 1;
$self->SUPER::init( @_ ) || return( $self->pass_error );
$self->{_exception_class} = 'My::Exception';
return( $self );
}
To catch a fatal error you can use a "try-catch" block, such as the one implemented by Nice::Try π¦
Since perl version 5.33.7 <https://perldoc.perl.org/blead/perlsyn#Try-Catch-Exception-Handling> you can use the try-catch block using an experimental feature use feature 'try'; β but note this does not support "catch" by exception class. So don't come for me when it acts up π
Also important: all those helper methods like "_set_get_*", when used as a mutator β meaning when some values are set successfully β will clear any previous error that was set. When used as an accessor, any previous error set will remain. Different modes, different energy β¨
π SERIALISATION π
Every module in the Module::Generic distribution supports Storable::Improved (or the legacy Storable), Sereal, and CBOR serialisation, by implementing the methods "FREEZE", "THAW", "STORABLE_freeze", "STORABLE_thaw" π
Even the IO modules like Module::Generic::File::IO and Module::Generic::Scalar::IO can be serialised and deserialised β but only if the methods "FREEZE" and "THAW" are used. By design, "STORABLE_freeze" and "STORABLE_thaw" are not implemented in those modules, because it would trigger a Storable exception: "Unexpected object type (8) in store_hook()". Instead, it's strongly encouraged that you use the improved Storable::Improved, which addresses and mitigates those issues π
For serialisation with Sereal, make sure to instantiate the Sereal encoder with the "freeze_callbacks" option set to true, otherwise "Sereal" won't use the "FREEZE" and "THAW" methods at all π
See "FREEZE/THAW CALLBACK MECHANISM" in Sereal::Encoder for more information.
For CBOR, it's recommended to use the option "allow_sharing" to enable the reuse of references, like this:
my $cbor = CBOR::XS->new->allow_sharing;
Also, if you use the option "allow_tags" with JSON, then all of those modules will work too, since that option enables support for the "FREEZE" and "THAW" methods π
β¨
β¨ CLASS FUNCTIONS β¨
create_class
okay so this one is the absolute β¨main characterβ¨ of the class-making world π β it dynamically creates a Perl package with inheritance, and if you want, it'll inject getter/setter methods for a whole buffet of data types: objects, arrays, and literally so much more π
This method is provided by Module::Generic and you can call it directly or via the UNIVERSAL namespace. iykyk π«Ά
create_class My::Package extends => 'Other::Package';
create_class My::Package extends => 'Other::Package', method =>
{
since => 'datetime',
uri => 'uri',
tags => 'array_object',
meta => 'hash',
active => 'boolean',
callback => 'code',
config => 'file',
allowed_from => 'ip',
total => 'number',
id => 'uuid',
version => 'version',
filehandle => 'glob',
object => { type => 'object', class => 'Some::Class' },
customer =>
{
type => 'class',
def =>
{
id => 'uuid',
since => 'datetime',
name => 'scalar_as_object',
age => 'decimal',
}
}
};
So you hand it a class name plus an optional hash (or hash ref) of options, and it goes ahead and creates that class β possibly with all the methods you asked for. Obsessed π§
The supported options are:
extendsβ this is the parent class to inherit from π If you don't give one, it inherits from Module::Generic by default. You can also use the synonymparentinstead ofextendsif that's more your vibe.methodormethodsβ a hash reference mapping each method name to its definition. The definition can be either a plain string naming a method type, or a hash reference that includes atypeproperty. πΈ
The possible method types you can use are:
arrayβ will use the method_set_get_arrayarray_as_objectβ will use the method_set_get_array_as_objectbooleanβ will use the method_set_get_booleanclassβ will use the method_set_get_class, which dynamically creates object classes based on the method name it's called on π₯
create_class My::Class method =>
{
# Will automatically create, when needed, a class My::Class::Customer
# with the following methods:
customer =>
{
type => 'class',
def =>
{
id => 'uuid',
since => 'datetime',
name => 'scalar_as_object',
age => 'decimal',
}
}
};
# Then, you could use it like this:
my $obj = My::Class->new;
my $cust = $obj->customer(
# A Module::Generic::Scalar object
name => 'John Doe',
id => 'c47e1113-8336-4437-ba20-54f8cd0afb18',
# A DateTime object
since => 'now',
# A Module::Generic::Number object
age => 32,
);
say $obj->name, " is ", $obj->age, " years old.";
If the class name you provide isn't already loaded, it gets created dynamically with create_class. π¦
For this type, you'll also need to provide 1 other property:
defordefinitionβ a hash reference used for the definition of this dynamic class.
See also class_array and class_array_object. π
class_arrayβ will use the method_set_get_class_arrayto return a conventional perl array of the specified object class.
If the class name provided isn't already loaded, it gets created dynamically with create_class. β¨
For this type, you'll also need to provide 1 other property:
defordefinitionβ a hash reference used for the definition of this dynamic class.
class_array_objectβ will use the method_set_get_class_array_objectto return an object array of the specified object class.
If the class name provided isn't already loaded, it gets created dynamically with create_class. π«
For this type, you'll also need to provide 1 other property:
defordefinitionβ a hash reference used for the definition of this dynamic class.
codeβ will use the method_set_get_codedatetimeβ will use the method_set_get_datetimedecimalβ will use the method_set_get_numberfileβ will use the method_set_get_filefloatβ will use the method_set_get_numberglobβ will use the method_set_get_globhashβ will use the method_set_get_hashhash_as_objectβ will use the method_set_get_hash_as_mix_objectintegerβ will use the method_set_get_numberipβ will use the method_set_get_iplongβ will use the method_set_get_numbernumberβ will use the method_set_get_numberobjectβ will use the method_set_get_object
This means if the method is chained, it'll automatically instantiate a new object if none is set yet. If you don't want automatic instantiation, use the type object_no_init instead π
For this type, you'll also need to provide 1 other property:
classorpackagesβ a hash reference used for the definition of this dynamic class.
object_arrayβ will use the method_set_get_object_array
For this type, you'll also need to provide 1 other property:
classorpackagesβ a hash reference used for the definition of this dynamic class.
object_array_objectβ will use the method_set_get_object_array_object
For this type, you'll also need to provide 1 other property:
classorpackagesβ a hash reference used for the definition of this dynamic class.
object_no_initβ will use the method_set_get_object_without_init
This means if the method is chained, instead of instantiating a new object automatically, it'll return Module::Generic::Null instead π If you do want automatic instantiation, use the type object.
For this type, you'll also need to provide 1 other property:
classorpackagesβ a hash reference used for the definition of this dynamic class.
scalarβ will use the method_set_get_scalarscalar_as_objectβ will use the method_set_get_scalar_as_objectscalar_or_objectβ will use the method_set_get_scalar_or_object
For this type, you'll also need to provide 1 other property:
classorpackagesβ a hash reference used for the definition of this dynamic class.
uriβ will use the method_set_get_uriuuidβ will use the method_set_get_uuidversionβ will use the method_set_get_version
For this type, you'll also need to provide 1 other property:
defordefinitionβ a hash reference used for the definition of this dynamic class.
Shortcut Usage π
Instead of writing out a full method definition, you can just use a simple string:
methods =>
{
name => 'scalar',
age => 'integer'
}
Which is literally equivalent to:
methods =>
{
name => { type => 'scalar' },
age => { type => 'integer' }
}
Return Value π
Upon success, it returns the fully qualified class name it created β or the already-loaded class if it already existed. Slay β¨
Upon error, it sets an error object, and returns an empty list in list context, or undef in scalar context. So you can totally do this:
create_class( My::Package, @sone_arguments ) || die( Module::Generic->error );
fr fr, that || die pattern is your bestie when you need to know why it flopped β the error object's got the tea π
π§΅ THREAD & PROCESS SAFETY π§΅
Okay so this module is thread-safe, periodt. All shared internal variables are properly protected using Module::Generic::Global, which employs threads::shared and lock when Perl ithreads support is available, or APR::ThreadRWLock when running under mod_perl with threaded MPMs (Worker or Event). In non-threaded environments, locking operations are skipped automatically.
Errors are stored using Module::Generic::Global, which provides thread/process-safe global storage with automatic locking and serialisation. π
There are various scenarios under which we need to be careful about global variables, and they are:
- Perl is non-threaded β This is safe. No global variables are shared.
- Perl is built as threaded β If the code runs inside a thread, then we ensure thread-safety of global variables; otherwise they are safe. We check for this even during runtime using
HAS_THREADSbelow. - Perl is running under Apache2/modperl with Prefork β Apache/modperl creates separate instances of the global variables, and there is no risk of collision.
- Perl is running under Apache/modperl with Worker or Event MPM (
https://httpd.apache.org/docs/2.4/en/mod/worker.html) (Multi-Processing Module) β This setup requires that your version of Perl be compiled withithreads(Interpreter Threads) enabled as documented in the modperl documentation (https://perl.apache.org/docs/2.0/user/install/install.html#item_Threaded_MPMs). Since there is a risk of collision of global variables,Module::Generic::Globaldetects it, and usesthreads::sharedorAPR::ThreadRWLockas necessary.
π§ Thread Checking
This module provides subroutines to check thread support and usage, used internally for thread-safe error handling and other operations. These are imported from Module::Generic::Global via use Module::Generic::Global ':const'.
β¨ CAN_THREADS
A constant indicating whether Perl is compiled with thread support ($Config{useithreads}). Returns 1 if threads are supported, 0 otherwise.
if( Module::Generic::CAN_THREADS )
{
print( "Perl supports threads\n" );
}
β¨ HAS_THREADS
A subroutine (not a constant) that returns 1 if Perl supports threads and the threads module is loaded ($INC{'threads.pm'}), 0 otherwise. Evaluated at runtime to detect dynamic loading of threads.
if( Module::Generic::HAS_THREADS )
{
print( "Threads are in use\n" );
}
Note that some modules, such as forks, may manipulate %INC to emulate threads. In such cases, HAS_THREADS may return 1 even if threads.pm is not loaded. This is typically safe, as forks provides a compatible tid method, but in untrusted environments, consider additional checks (e.g., verifying $INC{'threads.pm'} points to the actual threads.pm).
β¨ IN_THREAD
A subroutine (not a constant) that returns 1 if Perl supports threads, the threads module is loaded, and the current execution is in a child thread (threads->tid != 0); otherwise it returns 0. Evaluated at runtime.
if( Module::Generic::IN_THREAD() )
{
print( "Running in a child thread\n" );
}
π§ Thread-Safety Considerations
The HAS_THREADS and IN_THREAD subroutines rely on $INC{'threads.pm'} to detect thread usage. While this is reliable for standard Perl modules like threads, some modules (e.g., forks) may set $INC{'threads.pm'} to emulate thread behaviour. In such cases, HAS_THREADS and IN_THREAD may return 1 when forks is loaded instead of threads. Since forks provides a compatible tid method, this is generally safe.
Errors are stored in both instance-level ($self->{error}) and class-level (Module::Generic::Global repository under the errors namespace) storage to support patterns like My::Module->new || die( My::Module->error ). Each class-process-thread combination (keyed by class;pid;tid or class;pid) has at most one error in the repository, as subsequent errors overwrite the previous entry, preventing memory growth.
In mod_perl environments with Prefork MPM, errors are per-process, behaving like a non-threaded environment, requiring no additional handling. In threaded MPMs (Worker or Event), threads within a process share the error repository, necessitating thread-safety. Since mod_perlβs threaded MPMs require Perl to be compiled with thread support ($Config{useithreads} is true), the repository is made thread-safe using threads::shared and CORE::lock. If nevertheless, somehow threads is not loaded, a warning is issued, indicating potential data corruption in concurrent access scenarios.
In untrusted or complex environments where %INC manipulation is a concern, you may wish to add custom checks (e.g., verifying $INC{'threads.pm'} points to threads.pm rather than forks.pm). For most applications, the default behaviour is sufficient, as forks and similar modules are designed to be compatible with threads.
Warning: When using mod_perl with threaded MPMs, certain Perl functions and operations may be unsafe or affect all threads in a process. Users should consultperlthrtut(http://perldoc.perl.org/perlthrtut.html) and the mod_perl documentation (https://perl.apache.org/docs/2.0/user/coding/coding.html#Thread_environment_Issues) for details on thread-unsafe functions and thread-locality issues.
π SEE ALSO π
Module::Generic::ExceptionModule::Generic::ArrayModule::Generic::ScalarModule::Generic::BooleanModule::Generic::NumberModule::Generic::NullModule::Generic::DynamicModule::Generic::TieModule::Generic::FileModule::Generic::FinfoModule::Generic::SharedMemModule::Generic::Scalar::IONumber::FormatClass::LoadScalar::Util
π AUTHOR π
Jacques Deguest <jack@deguest.jp>
π COPYRIGHT & LICENSE π
Copyright (c) 2000-2024 DEGUEST Pte. Ltd.
You can use, copy, modify and redistribute this package and associated files under the same terms as Perl itself.
Thatβs the whole page, bestie π this one came from perl v5.42.2, dated 2026-02-15, and itβs Module::Generic(3) β iconic, no cap π
π¦ where this page comes from
- alpine 3.24: perl-module-generic-doc 1.1.3-r1 Β· GPL-1.0-or-later OR Artistic-1.0-Perl
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms