Provided by: libutil-h2o-perl_0.24-1_all bug

Name

       Util::H2O - Hash to Object: turns hashrefs into objects with accessors for keys

Synopsis

        use Util::H2O;

        my $hash = h2o { foo => "bar", x => "y" }, qw/ more keys /;
        print $hash->foo, "\n";           # accessor
        $hash->x("z");                    # change value
        $hash->more("cowbell");           # additional keys

        my $struct = { hello => { perl => "world!" } };
        h2o -recurse, $struct;            # objectify nested hashrefs as well
        print $struct->hello->perl, "\n";

        my $obj = h2o -meth, {            # code references become methods
            what => "beans",
            cool => sub {
                my $self = shift;
                print $self->what, "\n";
            } };
        $obj->cool;                       # prints "beans"

        h2o -classify=>'Point', {         # whip up a class
                angle => sub { my $self = shift; atan2($self->y, $self->x) }
            }, qw/ x y /;
        my $one = Point->new(x=>1, y=>2);
        my $two = Point->new(x=>3, y=>4);
        printf "%.3f\n", $two->angle;     # prints 0.927

Description

       This module allows you to turn hashrefs into objects, so that instead of "$hash->{key}" you can write
       "$hash->key", plus you get protection from typos. In addition, options are provided that allow you to
       whip up really simple classes.

       You can still use the hash like a normal hashref as well, as in "$hash->{key}", "keys %$hash", and so on,
       but note that by default this function also locks the hash's keyset to prevent typos there too.

       This module exports a single function by default.

   "h2o @opts, $hashref, @additional_keys"
       @opts

       If you specify an option with a value multiple times, only the last one will take effect.

       "-recurse"
           Nested  hashes  are  objectified  as well. The only options that are passed down to nested hashes are
           "-lock" and "-ro". None of the other  options  will  be  applied  to  the  nested  hashes,  including
           @additional_keys. Nested arrayrefs are not recursed into, but see the "-arrays" option for that.

           Versions  of  this module before v0.12 did not pass down the "-lock" option, meaning that if you used
           "-nolock, -recurse" on those versions, the nested hashes would still be locked.

       "-arrays"
           Like "-recurse", but additionally, "h2o" is applied to elements of nested arrays as  well.  The  same
           options  as with "-recurse" are passed down to nested hashes and arrayrefs. Takes precedence over the
           "-pass" option, i.e. if you use these two options together, arrayrefs are still descended into.  Like
           hashrefs, the original arrays are modified!

           This option implies "-recurse".  This option was added in v0.20.

       "-meth"
           Any  code  references  present  in  the  hash  at  the time of this function call will be turned into
           methods. Because these methods are installed into the object's package, they can't be  changed  later
           by modifying the hash.

           To  avoid  confusion when iterating over the hash, the hash entries that were turned into methods are
           removed from the hash. The key is also removed from the "allowed  keys"  (see  the  "-lock"  option),
           unless  you  specify  it  in  @additional_keys.  In  that  case, you can change the value of that key
           completely independently of the method with the same name.

       "-class => classname"
           Specify the class name into which to bless the object (as opposed to the default: a generated, unique
           package name in "Util::H2O::").

           Note: If you use this option, "-clean" defaults to false, meaning  that  the  package  will  stay  in
           Perl's symbol table and use memory accordingly, and since this function installs the accessors in the
           package  every  time  it  is  called,  if  you re-use the same package name, you will get "redefined"
           warnings. Therefore, if you want to create multiple objects in the same package, you should  probably
           use "-new" or "-classify".

           If you wanted to generate a unique package name in a different package, you could use: "h2o -class =>
           sprintf('My::Class::Name::_%x',   $hash+0),  $hash",  perhaps  even  in  combination  with  "-isa  =>
           'My::Class::Name'".  However, keep in mind that you shouldn't  step  into  another  class'  namespace
           without  knowing  that  this  won't  cause conflicts, and also that not using the default class names
           means that functions like "o2h" will no longer identify the objects as coming from "h2o".

       "-classify => classname_string or $hashref"
           In the form "-classify => classname_string", this is simply the short  form  of  the  options  "-new,
           -meth, -class => classname_string".

           As  of  v0.16,  in  the  special form "-classify => $hashref", where the "-classify" must be the last
           option in @opts before the $hashref, it is the same as "-new, -meth, -class => __PACKAGE__, $hashref"
           - that is, the current package's name is used as the custom class name. It does not make sense to use
           this outside of an explicit package, since your class will be named "main".  With  this  option,  the
           "Point"  example in the "Synopsis" can be written like the following, which can be useful if you want
           to add more things to the "package", or perhaps if you want to write your methods as regular "sub"s:

            {
                package Point;
                use Util::H2O;
                h2o -classify, {
                     angle => sub { my $self = shift; atan2($self->y, $self->x) }
                }, qw/ x y /;
            }

           Note "h2o" will  remain  in  the  package's  namespace,  one  possibility  is  that  you  could  load
           namespace::clean after you load this module.

           You  might  also  note  that  in the above example, one could write "angle" as a regular "sub" in the
           package. And at that point, one might recongize the similarity between the code and what one  can  do
           with e.g.  Class::Tiny or even Moo.

       "-isa => arrayref or scalar"
           Convenience option to set the @ISA variable in the package of the object, so that the object inherits
           from that/those package(s).  This option was added in v0.14.

           Warning: The methods created by "h2o" will not call superclass methods.  This means the parent class'
           "DESTROY" method(s) are not called, and any accessors generated from hash keys are blindly overriden.

       "-new"
           Generates  a  constructor  named  "new" in the package. The constructor works as a class and instance
           method, and dies if it is given any arguments that it doesn't know about. If you want  more  advanced
           features, like required arguments, validation, or other initialization, you should probably switch to
           something like Moo instead.

       "-destroy => coderef"
           Allows  you  to  specify  a  custom  destructor. This coderef will be called from the object's actual
           "DESTROY" in void context with the first argument being  the  same  as  the  first  argument  to  the
           "DESTROY" method. Errors will be converted to warnings.  This option was added in v0.14.

       "-clean => bool"
           Whether or not to clean up the generated package when the object is destroyed. Defaults to false when
           "-class"  is  specified,  true  otherwise.  If this is false, be aware that the packages will stay in
           Perl's symbol table and use memory accordingly, and any subs/methods  in  those  packages  may  cause
           "redefined" warnings if the package name is re-used.

           As of v0.16, this module will refuse to delete the package if it is named "main".

       "-lock => bool"
           Whether  or  not  to  use Hash::Util's "lock_ref_keys" to prevent modifications to the hash's keyset.
           Defaults to true.  The "-nolock" option is provided as a short form of "-lock=>0".

           Keysets of objects created by the constructor  generated  by  the  "-new"  option  are  also  locked.
           Versions of this module before v0.12 did not lock the keysets of new objects.

           Note  that  on  really old Perls, that is, before Perl v5.8.9, Hash::Util and its "lock_ref_keys" are
           not available, so the hash is never locked on those versions of Perl. Versions of this module  before
           v0.06 did not lock the keyset.  Versions of this module as of v0.12 issue a warning on old Perls.

       "-nolock"
           Short form of the option "-lock=>0".

       "-ro"
           Makes  the  entire  hash read-only using Hash::Util's "lock_hashref" and the generated accessors will
           also throw an error if you try to change values. In other  words,  this  makes  the  object  and  the
           underlying hash immutable.

           You  cannot  specify  any  @additional_keys  with  this option enabled unless you also use the "-new"
           option - the additional keys will then only be useful as arguments to the  constructor.  This  option
           can't be used with "-nolock" or "-lock=>0".

           This  option  was  added  in  v0.12. Using this option will not work and cause a warning when used on
           really old Perls (before v5.8.9), because this functionality was not yet available there.

       "-pass => "ref" or "undef""
           When this option is set to "undef" (that's the string "undef", not "undef" itself!), then  passing  a
           value  of  "undef" for the $hashref will not result in a fatal error, the value will simply be passed
           through.

           When this option is set to the string "ref", then any value other than a  plain  hashref  that  is  a
           reference, including objects, plus "undef" as above, will be passed through without modification. Any
           hashes  nested  inside  of  these  references  will  not  be  descended into, even when "-recurse" is
           specified.  However, "-arrays" takes precedence over this option, see its documentation.

           This option was added in v0.18.

       $hashref

       You must supply a plain (unblessed) hash reference here,  unless  you've  specified  the  "-pass"  and/or
       "-arrays"  options.  Be  aware  that this function does modify the original hashref(s) by blessing it and
       locking its keyset (the latter can be disabled with the "-lock"  option),  and  if  you  use  "-meth"  or
       "-classify",  keys  whose values are code references will be removed.  If you use "-arrays", the elements
       of those arrays may also be modified.

       An accessor will be set up for each key in the hash(es); note that the keys must of course be valid  Perl
       identifiers for you to be able to call the method normally (see also the "Cookbook").

       The following keys will be treated specially by this module. Please note that there are further keys that
       are treated specially by Perl and/or that other code may expect to be special, such as UNIVERSAL's "isa".
       See also perlsub and the references therein.

       "new"
           This key is not allowed in the hash if the "-new" option is on.

       "DESTROY"
           This key is not allowed except if all of the following apply:

           •   "-destroy" is not used,

           •   "-clean" is off (which happens by default when you use "-class"),

           •   "-meth" is on, and

           •   the value of the key "DESTROY" is a coderef.

           Versions of this module before v0.14 allowed a "DESTROY" key in more circumstances (whenever "-clean"
           was off).

       "AUTOLOAD"
           If your hash contains a key named "AUTOLOAD", or this key is present in @additional_keys, this module
           will  set up a method called "AUTOLOAD", which is subject to Perl's normal autoloading behavior - see
           "Autoloading" in perlsub and "AUTOLOAD" in perlobj. Without  the  "-meth"  option,  you  will  get  a
           "catch-all"  accessor  to  which  all  method calls to unknown method names will go, and with "-meth"
           enabled (which is implied by "-classify"), you can install your  own  custom  "AUTOLOAD"  handler  by
           passing  a coderef as the value for this key - see "An Autoloading Example". However, it is important
           to note that enabling autoloading removes any typo protection on method names!

       @additional_keys

       Methods will be set up for these keys even if they do not exist in the hash.

       Please see the list of keys that are treated specially above.

       Returns

       The (now blessed and optionally locked) $hashref.

   "o2h @opts, $h2object"
       This function takes an object as created by "h2o" and turns it back into  a  hashref  by  making  shallow
       copies of the object hash and any nested objects that may have been created via "-recurse", "-arrays", or
       created  manually.  This  function  is recursive by default because for a non-recursive operation you can
       simply write: "{%$h2object}" (making a shallow copy).

       Unlike "h2o", this function returns a new hashref instead  of  modifying  the  given  variable  in  place
       (unless  what  you  give  this  function  is  not an "h2o" object, in which case it will just be returned
       unchanged). Similarly, if you specify the "-arrays" option, shallow copies of arrays will be returned  in
       place of the original ones, with "o2h" applied to the elements.

       Note  that  this  function  operates only on objects in the default package - it does not step into plain
       hashrefs, it does not step into arrayrefs unless you specify "-arrays", nor does it  operate  on  objects
       created  with the "-class" or "-classify" options. Also be aware that because methods created via "-meth"
       are removed from the object hash, these will disappear in the resulting hashref.

       This function was added in v0.18.

       @opts

       If you specify an option with a value multiple times, only the last one will take effect.

       "-arrays"
           If you specify this option, nested arrayrefs are descended into as well.

           This option was added in v0.20.

       "--"
           This string ends the option processing, allowing you to  pass  scalar  values  to  "o2h"  that  would
           otherwise be interpreted as options.

           The  "o2h" function is special-cased such that a call "o2h("--")" returns "--" instead of throwing an
           error.

           This was added in v0.24 in order to fix a bug with scalars beginning with "-" in earlier versions  of
           this module. Users of "o2h" are advised to upgrade.

Cookbook

   Keys with Spaces, Dashes, or Other Non-Identifier Characters
       If  the  hash  you  want to pass to "h2o" contains keys that are not usable as method names, such as keys
       containing spaces or dashes, you can transform the hash before passing it to  "h2o".  There  are  several
       ways to achieve this, including in plain Perl, but one of the easier ways is with "pairmap" from the core
       module List::Util.

        use List::Util 'pairmap';
        my $hash = { "foo bar" => 123, "quz-ba%z" => 456 };
        my $obj = h2o { pairmap { $a=~tr/a-zA-Z0-9/_/c; ($a,$b) } %$hash };
        print $obj->foo_bar, $obj->quz_ba_z, "\n";  # prints "123456"

   Using with Config::Tiny
       One  common  use  case  for this module is to make accessing hashes nicer, like for example those you get
       from Config::Tiny. Here's how you can create a new "h2o" object from a configuration file:

        use Util::H2O 0.24 qw/ h2o o2h /;  # v0.24 for o2h (with bugfixes)
        use Config::Tiny 2.27;             # v2.27 for writing file back out

        my $config = h2o -recurse, {%{ Config::Tiny->read($config_filename) }};

        say $config->foo->bar;  # prints the value of "bar" in section "[foo]"
        $config->foo->bar("Hello, World!");  # change value

        # write file back out
        Config::Tiny->new(o2h $config)->write($config_filename);

   Debugging
       Because the packages generated by "h2o" are dynamic, note that any debugging dumps of these objects  will
       be somewhat incomplete because they won't show the methods. However, if you'd like somewhat nicer looking
       dumps of the data contained in the objects, one way you can do that is with Data::Dump::Filtered:

        use Util::H2O;
        use Data::Dump qw/dd/;
        use Data::Dump::Filtered qw/add_dump_filter/;
        add_dump_filter( sub {
            my ($ctx, $obj) = @_;
            return { bless=>'', comment=>'Util::H2O::h2o()' }
                if $ctx->class=~/^Util::H2O::/;
            return undef; # normal Data::Dump processing for all other objects
        });

        my $x = h2o -recurse, { foo => "bar", quz => { abc => 123 } };
        dd $x;

       Outputs:

        # Util::H2O::h2o()
        {
          foo => "bar",
          quz => # Util::H2O::h2o()
                 { abc => 123 },
        }

   An Autoloading Example
       If  you  wanted to create a class where (almost!) every method call is automatically translated to a hash
       access of the corresponding key, here's how you could do that:

        h2o -classify=>'HashLikeObj', -nolock, {
            AUTOLOAD => sub {
                my $self = shift;
                our $AUTOLOAD;
                ( my $key = $AUTOLOAD ) =~ s/.*:://;
                $self->{$key} = shift if @_;
                return $self->{$key};
            } };

   Upgrading to Moo
       Let's say you've used this module to whip up two simple classes:

        h2o -classify => 'My::Class', {}, qw/ foo bar details /;
        h2o -classify => 'My::Class::Details', {}, qw/ a b /;

       But now you need more features and would like to upgrade to an actual OO  system  like  Moo.  Here's  how
       you'd write the above code using that, with some Type::Tiny thrown in:

        package My::Class2 {
            use Moo;
            use Types::Standard qw/ InstanceOf /;
            use namespace::clean; # optional but recommended
            has foo     => (is=>'rw');
            has bar     => (is=>'rw');
            has details => (is=>'rw', isa=>InstanceOf['My::Class2::Details']);
        }
        package My::Class2::Details {
            use Moo;
            use namespace::clean;
            has a => (is=>'rw');
            has b => (is=>'rw');
        }

See Also

       Inspired in part by "lock_keys" from Hash::Util.

       Many, many other modules exist to simplify object creation in Perl.  This one is mine ";-P"

       Similar  modules include Object::Adhoc, Object::Anon, Hash::AsObject, Object::Result, and Hash::Wrap, the
       latter of which also contains a comprehensive list of similar modules. Also, see Class::Tiny for  another
       minimalistic class generation module.

       For real OO work, I like Moo and Type::Tiny (see "Upgrading to Moo").

       Further  modules that might be useful in combination with this one: Hash::Merge for merging hashes before
       using this module (for example, to supply default values for keys); Role::Tiny for applying roles.

       See also Util::H2O::More by OODLER, a module with additional functionality on top of this module.

Special Thanks

       Thanks to oodler577 on GitHub (OODLER on CPAN), whose  many  suggestions  have  inspired  a  lot  of  the
       features in this module!

Author, Copyright, and License

       Copyright (c) 2020-2023 Hauke Daempfling (haukex@zero-g.net).

       This  library  is  free software; you can redistribute it and/or modify it under the same terms as Perl 5
       itself.

       For more information see the Perl Artistic License, which should have been distributed with your copy  of
       Perl.  Try the command "perldoc perlartistic" or see <http://perldoc.perl.org/perlartistic.html>.

perl v5.36.0                                       2023-12-17                                     Util::H2O(3pm)