Module:...Dynamic(3)

πŸ“¦ version 1.1.3, in alpine 3.24

πŸ’– NAME πŸ’–

Okay so bestie, welcome to the Module:...Dynamic(3) page from User Contributed Perl Documentation. The main character here is Module::Generic::Dynamic, aka the Dynamic Object Class. It’s giving β€œbuild objects on the fly” energy, and we’re gonna walk through it together 🫢


✨ SYNOPSIS ✨

package My::Module;
use parent qw( Module::Generic::Dynamic );

# Then, instantiating an object
my $object = My::Module->new({
    name => 'Test Product',
    quantity => 20,
    metadata => { sku => 'ABC123', price => 99.99 },
    tags => [qw( product entrepreneurship capital )],
    created => '2023-11-18T10:30:00',
    is_active => 1,
    url => 'https://example.com',
    file_path => './config.ini',
});

# Accessing methods
print $object->name; # Test Product
print $object->metadata->sku; # ABC123
print $object->tags->join(','); # product,entrepreneurship,capital
print $object->created->year; # 2023

# Dynamic method creation via AUTOLOAD
$object->version('1.2.3');
print $object->version->stringify; # 1.2.3

🌈 VERSION 🌈

v1.3.0 β€” yes bestie, that’s the version πŸ’…


πŸ¦‹ DESCRIPTION πŸ¦‹

Module::Generic::Dynamic provides a framework for dynamically creating classes and methods based on an input hash reference. It automatically generates accessor methods for each key-value pair, inferring the appropriate data type and handling method creation at runtime using AUTOLOAD.

This module is particularly useful for creating flexible, data-driven objects where the structure is not known in advance. It supports a wide range of data types, including scalars, hashes, arrays, booleans, URIs, UUIDs, IP addresses, versions, files, code references, globs, and custom objects.

For more granular control over the method to be used for each data key-value, use _set_get_class in Module::Generic.


πŸ‘‘ METHODS πŸ‘‘

✨ new ✨

Provided with a hash reference of data, this creates a new object and dynamically generates a class based on the calling package name. For each key-value pair in the hash, it creates an accessor method and assigns an appropriate handler based on the data type:

  • array β€” If the array contains hash references, creates a method using _set_get_object_array_object in Module::Generic with a new dynamic class for each hash element. Otherwise, uses _set_get_array_as_object in Module::Generic to return a Module::Generic::Array object.
  • boolean β€” For keys matching is_, has_, enable, active, valid, disabled, or inactive (case-insensitive) with values 0, 1, undef, or empty string, uses _set_get_boolean in Module::Generic to return a Module::Generic::Boolean object.
  • code β€” For code references, uses _set_get_code in Module::Generic to store and execute the code.
  • file β€” For keys matching file or path with a valid file path (e.g., ./config.ini, ~/docs), uses _set_get_file in Module::Generic to return a Module::Generic::File object.
  • glob β€” For glob references (e.g., filehandles), uses _set_get_glob in Module::Generic.
  • hash β€” Creates a method using _set_get_object in Module::Generic and a new dynamic class (e.g., My::Module::KeyName) for nested hash data.
  • integer β€” For keys matching count, size, or quantity with an integer value, uses _set_get_number in Module::Generic to return a Module::Generic::Number object.
  • IP address β€” For keys matching ip, ip_addr, or ip_address with a valid IPv4 or IPv6 address, uses _set_get_ip in Module::Generic to return a Module::Generic::Scalar object.
  • number β€” For numeric values (integer or floating-point), uses _set_get_number in Module::Generic to return a Module::Generic::Number object.
  • object β€” For blessed objects (e.g., JSON::PP::Boolean, Module::Generic::File, custom classes), uses the appropriate handler (e.g., _set_get_boolean in Module::Generic for booleans) or _set_get_object in Module::Generic for others, preserving the object’s class.
  • string β€” Creates a method using _set_get_scalar_as_object in Module::Generic, returning a Module::Generic::Scalar object.
  • URI β€” For keys matching uri or url or values starting with http:// or https://, uses _set_get_uri in Module::Generic to return a URI object. If Regexp::Common::URI is available, it validates URIs more precisely.
  • UUID β€” For keys matching id, _id, or ending with id with a valid UUID format, uses _set_get_uuid in Module::Generic to return a Module::Generic::Scalar object.
  • version β€” For keys matching version with a valid version string (e.g., 1.2.3), uses _set_get_version in Module::Generic to return a version object.

If a key results in an invalid method name (e.g., starting with digits), it is sanitized by removing non-alphanumeric characters and leading digits.

Example:

my $obj = Module::Generic::Dynamic->new({
    name => 'Test',
    is_active => 1,
    url => 'https://example.com',
    file => './config.ini',
});
print $obj->name; # Test
print $obj->is_active; # 1 (Module::Generic::Boolean)
print $obj->url->host; # example.com
print $obj->file->basename; # config.ini

πŸŒ€ AUTOLOAD πŸŒ€

Dynamically creates accessor methods for undefined method calls based on the method name and the first argument’s type (if provided). It uses the same type-guessing logic as new, inferring handlers from:

  • Method name patterns (e.g., is_active for boolean, url for URI).
  • Argument types (e.g., JSON::PP::Boolean, hash, array, scalar).
  • Value formats (e.g., UUID, IP address, file path).

If no arguments are provided (getter call), the method is created based on the method name. For example:

my $obj = Module::Generic::Dynamic->new;
$obj->url('https://example.com');
print $obj->url->host; # example.com
print $obj->is_active; # Creates boolean accessor, returns undef

Invalid inputs (e.g., non-UUID values for id fields) fall back to _set_get_scalar_as_object in Module::Generic.


πŸŽ€ SERIALISATION πŸŽ€

Serialisation is supported for CBOR, Sereal, Storable::Improved, and Storable. The module implements FREEZE, THAW, STORABLE_freeze, STORABLE_thaw, and TO_JSON to handle object serialization and deserialization, preserving data types and structure.

Example:

my $obj = Module::Generic::Dynamic->new({ name => 'Test', tags => [qw( product test )] });
my $data = $obj->serialise( $obj, serialiser => 'Storable::Improved' );
my $new_obj = Module::Generic->new->deserialise( data => $data, serialiser => 'Storable::Improved' );
print $new_obj->name; # Test
print $new_obj->tags->join(','); # product,test

πŸ’€ THREAD SAFETY WARNING πŸ’€

This module is not thread-safe. No cap.

Module::Generic::Dynamic dynamically creates packages and injects methods into symbol tables at runtime using eval. In multi-threaded environments (Perl ithreads), this can cause:

  • Race conditions between threads attempting to create the same symbol.
  • Corrupted symbol tables if a method is installed while another thread is reading or writing from the same class.
  • Use this module only during application initialization, before any threads are spawned.
  • Avoid invoking new or triggering AUTOLOAD from within a thread.
  • In persistent environments (e.g., mod_perl), precompile structures at startup.

😭 ERROR HANDLING 😭

Errors in new and AUTOLOAD, such as invalid parameters or failed method creation, return undef with an error object accessible via $obj->error. Check for errors to handle failures gracefully:

my $obj = Module::Generic::Dynamic->new({ name => 'Test' });
die $obj->error if !defined $obj;

Invalid method names (e.g., starting with digits) are sanitised by removing non-alphanumeric characters and leading digits, or ignored if no valid name remains.


πŸ™ˆ LIMITATIONS πŸ™ˆ

  • Invalid method names (e.g., 123invalid) are sanitised or ignored, which may lead to unexpected behavior if not checked.
  • Type guessing is conservative to avoid false positives. For example, a string like /api/v1 is treated as a scalar unless the key name suggests a file (e.g., file_path).
  • Ambiguous inputs (e.g., a number that could be a version or integer) may default to a less specific handler (e.g., _set_get_number in Module::Generic).

Users can override type guessing by explicitly using _set_get_class in Module::Generic for more control.


🌸 AUTHOR 🌸

Jacques Deguest β€” jack@deguest.jp 🫢


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.


Mwah πŸ’‹ β€” this page comes from perl v5.42.2, dated 2026-02-15, and it’s the Module:...Dynamic(3) page. Bye bestie! 🫢

πŸ“¦ where this page comes from