NAME
Hash::Wrap - create on-the-fly objects from hashes
VERSION
version 0.12
SYNOPSIS
use Hash::Wrap;
my $result = wrap_hash( { a => 1 } );
print $result->a; # prints
print $result->b; # throws
# import two constructors, <cloned> and <copied> with different behaviors.
use Hash::Wrap
{ -as => 'cloned', clone => 1},
{ -as => 'copied', copy => 1 };
my $cloned = cloned( { a => 1 } );
print $cloned->a;
my $copied = copied( { a => 1 } );
print $copied->a;
DESCRIPTION
Hash::Wrap creates objects from hashes, providing accessors for hash elements. The objects are hashes, and may be modified using the standard Perl hash operations and the object's accessors will behave accordingly.
Why use this class? Sometimes a hash is created on the fly and it's too much of a hassle to build a class to encapsulate it.
sub foo () { ... ; return { a => 1 }; }
With Hash::Wrap
:
use Hash::Wrap;
sub foo () { ... ; return wrap_hash( { a => 1 ); }
my $obj = foo ();
print $obj->a;
Elements can be added or removed to the object and accessors will track them. The object may be made immutable, or may have a restricted set of attributes.
There are many similar modules on CPAN (see "SEE ALSO" for comparisons).
What sets Hash::Wrap apart is that it's possible to customize object construction and accessor behavior:
It's possible to use the passed hash directly, or make shallow or deep copies of it.
Accessors can be customized so that accessing a non-existent element can throw an exception or return the undefined value.
On recent enough versions of Perl, accessors can be lvalues, e.g.
$obj->existing_key = $value;
USAGE
Simple Usage
use
'ing Hash::Wrap without options imports a subroutine called wrap_hash
which takes a hash, blesses it into a wrapper class and returns the hash:
use Hash::Wrap;
my $h = wrap_hash { a => 1 };
print $h->a, "\n"; # prints 1
The wrapper class has no constructor method, so the only way to create an object is via the wrap_hash
subroutine. (See "WRAPPER CLASSES" for more about wrapper classes) If wrap_hash
is called without arguments, it will create a hash for you.
Advanced Usage
wrap_hash
is an awful name for the constructor subroutine
So rename it:
use Hash::Wrap { -as => "a_much_better_name_for_wrap_hash" };
$obj = a_much_better_name_for_wrap_hash( { a => 1 } );
The Wrapper Class name matters
If the class name matters, but it'll never be instantiated except via the imported constructor subroutine:
use Hash::Wrap { -class => 'My::Class' };
my $h = wrap_hash { a => 1 };
print $h->a, "\n"; # prints 1
$h->isa( 'My::Class' ); # returns true
Again, the wrapper class has no constructor method, so the only way to create an object is via the wrap_hash
subroutine.
The Wrapper Class needs its own class constructor method
To generate a wrapper class which can be instantiated via its own constructor method:
use Hash::Wrap { -class => 'My::Class', -new => 1 };
The default wrap_hash
constructor subroutine is still exported, so
$h = My::Class->new( { a => 1 } );
and
$h = wrap_hash( { a => 1 } );
do the same thing.
To give the constructor method a different name:
use Hash::Wrap { -class => 'My::Class', -new => '_my_new' };
To prevent the constructor subroutine from being imported:
use Hash::Wrap { -as => undef, -class => 'My::Class', -new => 1 };
A stand alone Wrapper Class
To create a stand alone wrapper class,
package My::Class;
use Hash::Wrap { -base => 1 };
1;
And later...
use My::Class;
$obj = My::Class->new( \%hash );
It's possible to modify the constructor and accessors:
package My::Class;
use Hash::Wrap { -base => 1, -new => 'new_from_hash', -undef => 1 };
1;
OPTIONS
Hash::Wrap works at compile time. To modify its behavior pass it options when it is use
'd:
use Hash::Wrap { %options1 }, { %options2 }, ... ;
Multiple options hashes may be passed; each hash specifies options for a separate constructor or class.
For example,
use Hash::Wrap
{ -as => 'cloned', clone => 1},
{ -as => 'copied', copy => 1 };
creates two constructors, cloned
and copied
with different behaviors.
Constructor
-as
=> subroutine name-
Import the constructor subroutine with the given name. It defaults to
wrap_hash
. -copy
=> boolean-
If true, the object will store the data in a shallow copy of the hash. By default, the object uses the hash directly.
-clone
=> boolean | coderef-
Store the data in a deep copy of the hash. if true, "dclone" in Storable is used. If a coderef, it will be called as
$clone = coderef->( $hash )
By default, the object uses the hash directly.
--immutable
=> boolean-
The object's attributes and values are locked and may not be altered. Note that this locks the underlying hash.
--lockkeys
=> boolean | arrayref-
If the value is true, the object's attributes are restricted to the existing keys in the hash. If it is an array reference, it specifies which attributes are allowed, in addition to existing attributes. The attribute's values are not locked. Note that this locks the underlying hash.
Accessors
-undef
=> boolean-
Normally an attempt to use an accessor for an non-existent key will result in an exception. This option causes the accessor to return
undef
instead. It does not create an element in the hash for the key. -lvalue
=> flag-
If non-zero, the accessors will be lvalue routines, e.g. they can change the underlying hash value by assigning to them:
$obj->attr = 3;
The hash entry must already exist or this will throw an exception.
lvalue subroutines are only available on Perl version 5.16 and later.
If
-lvalue = 1
this option will silently be ignored on earlier versions of Perl.If
-lvalue = -1
this option will cause an exception on earlier versions of Perl.
Class
-base
=> boolean-
If true, the enclosing package is converted into a proxy wrapper class. This should not be used in conjunction with
-class
. See "A stand alone Wrapper Class". -class
=> class name-
A class with the given name will be created and new objects will be blessed into the specified class by the constructor subroutine. The new class will not have a constructor method.
If not specified, the class name will be constructed based upon the options. Do not rely upon this name to determine if an object is wrapped by Hash::Wrap.
-new
=> boolean | Perl Identifier-
Add a class constructor method.
If
-new
is a true boolean value, the method will be callednew
. Otherwise-new
specifies the name of the method.
Extra Class Methods
-defined
=> boolean | Perl Identifier-
Add a method which returns true if the passed hash key is defined or does not exist. If
-defined
is a true boolean value, the method will be calleddefined
. Otherwise it specifies the name of the method. For example,use Hash::Wrap { -defined => 1 }; $obj = wrap_hash( { a => 1, b => undef } ); $obj->defined( 'a' ); # TRUE $obj->defined( 'b' ); # FALSE $obj->defined( 'c' ); # FALSE
or
use Hash::Wrap { -defined => 'is_defined' }; $obj = wrap_hash( { a => 1 } ); $obj->is_defined( 'a' );
-exists
=> boolean | Perl Identifier-
Add a method which returns true if the passed hash key exists. If
-exists
is a boolean, the method will be calledexists
. Otherwise it specifies the name of the method. For example,use Hash::Wrap { -exists => 1 }; $obj = wrap_hash( { a => 1 } ); $obj->exists( 'a' );
or
use Hash::Wrap { -exists => 'is_present' }; $obj = wrap_hash( { a => 1 } ); $obj->is_present( 'a' );
WRAPPER CLASSES
A wrapper class has the following characteristics.
It has the methods
DESTROY
,AUTOLOAD
andcan
.It will have other methods if the
-undef
and-exists
options are specified. It may have other methods if it is a stand alone class.It will have a constructor if either of
-base
or-new
is specified.
Wrapper Class Limitations
Wrapper classes have
DESTROY
,can
method, andAUTOLOAD
methods, which will mask hash keys with the same names.Classes which are generated without the
-base
or-new
options do not have a class constructor method, e.gClass->new()
will not return a new object. The only way to instantiate them is via the constructor subroutine generated via Hash::Wrap. This allows the underlying hash to have anew
attribute which would otherwise be masked by the constructor.
LIMITATIONS
Lvalue accessors
Lvalue accessors are available only on Perl 5.16 and later.
Accessors for deleted hash elements
Accessors for deleted elements are not removed. The class's can
method will return undef
for them, but they are still available in the class's stash.
SEE ALSO
Here's a comparison of this module and others on CPAN.
- Hash::Wrap (this module)
-
core dependencies only
only applies object paradigm to top level hash
accessors may be lvalue subroutines
accessing a non-existing element via an accessor throws by default, but can optionally return
undef
can use custom package
can copy/clone existing hash. clone may be customized
- Object::Result
-
As you might expect from a DCONWAY module, this does just about everything you'd like. It has a very heavy set of dependencies.
- Hash::AsObject
-
core dependencies only
applies object paradigm recursively
accessing a non-existing element via an accessor creates it
- Data::AsObject
-
moderate dependency chain (no XS?)
applies object paradigm recursively
accessing a non-existing element throws
- Class::Hash
-
core dependencies only
only applies object paradigm to top level hash
can add generic accessor, mutator, and element management methods
accessing a non-existing element via an accessor creates it (not documented, but code implies it)
can()
doesn't work
- Hash::Inflator
-
core dependencies only
accessing a non-existing element via an accessor returns undef
applies object paradigm recursively
- Hash::AutoHash
-
moderate dependency chain. Requires XS, tied hashes
applies object paradigm recursively
accessing a non-existing element via an accessor creates it
- Hash::Objectify
-
light dependency chain. Requires XS.
only applies object paradigm to top level hash
accessing a non-existing element throws, but if an existing element is accessed, then deleted, accessor returns undef rather than throwing
can use custom package
- Data::OpenStruct::Deep
-
uses source filters
applies object paradigm recursively
- Object::AutoAccessor
-
light dependency chain
applies object paradigm recursively
accessing a non-existing element via an accessor creates it
- Data::Object::Autowrap
-
core dependencies only
no documentation
- Object::Accessor
-
core dependencies only
only applies object paradigm to top level hash
accessors may be lvalue subroutines
accessing a non-existing element via an accessor returns
undef
by default, but can optionally throw. Changing behavior is done globally, so all objects are affected.accessors must be explicitly added.
accessors may have aliases
values may be validated
invoking an accessor may trigger a callback
SUPPORT
Websites
The following websites have more information about this module, and may be of help to you. As always, in addition to those websites please use your favorite search engine to discover more resources.
MetaCPAN
A modern, open-source CPAN search engine, useful to view POD in HTML format.
RT: CPAN's Bug Tracker
The RT ( Request Tracker ) website is the default bug/issue tracking system for CPAN.
You can email the author of this module at DJERIUS at cpan.org
asking for help with any problems you have.
Bugs / Feature Requests
Please report any bugs or feature requests by email to bug-hash-wrap at rt.cpan.org
, or through the web interface at https://rt.cpan.org/Public/Bug/Report.html?Queue=Hash-Wrap. You will be automatically notified of any progress on the request by the system.
Source Code
The code is open to the world, and available for you to hack on. Please feel free to browse it and play with it, or whatever. If you want to contribute patches, please send me a diff or prod me to pull from your repository :)
https://gitlab.com/djerius/hash-wrap
https://gitlab.com/djerius/hash-wrap.git
AUTHOR
Diab Jerius <djerius@cpan.org>
COPYRIGHT AND LICENSE
This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory.
This is free software, licensed under:
The GNU General Public License, Version 3, June 2007