The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Util::H2O::More - provides baptise, a drop-in replacement for bless; like if bless created accessors for you. This module also provides additional methods built using h2o or o2h from Util::H2O that allow for the incremental addition of OOP into existing or small scale Perl code without having to fully commit to a Perl OOP framework or compromise one's personal Perl style.

Util::H2O::More now provides a wrapper method now, d2o that will find and objectify all HASH refs contained in ARRAYs at any level, no matter how deep. This ability is very useful for dealing with modern services that return ARRAYs of HASH, traditional DBI queries, and other modules that can provide LISTs of HASH refs, such as Web::Scraper.

SYNOPSIS

It is easy to create an OOP module using baptise instead of bless, which means it includes accessors (thanks to Util::H2O::h2o). In most cases, baptise can be used as a drop-in replacement for bless.

Below is an example of a traditional Perl OOP class constructor using baptise to define a set of default accessors, in addition to any that are created by virtue of the %opts passed.

    use strict;
    use warnings;
    
    package Foo::Bar;

    # exports 'h2o' also
    use Util::H2O::More qw/baptise/;
      
    sub new {
      my $pkg    = shift;
      my %opts   = @_;
      
      # replaces bless, defines default constructures and creates
      # constructors based on what's passed into %opts
      
      my $self = baptise \%opts, $pkg, qw/bar haz herp derpes/;
       
      return $self;
    }
     
    1;

Then on a caller script,

    use strict;
    use warnings;
     
    use Foo::Bar;
     
    my $foo = Foo::Bar->new(some => q{thing}, else => 4);
     
    print $foo->some . qq{\n};
     
    # set bar via default accessor
    $foo->bar(1);
    print $foo->bar . qq{\n};
    
    # default accessors also available from the class defined
    # above,
    #   $foo->haz, $foo->herp, $foo->derpes
    
    # and from the supplied tuple,
    #   $foo->else

For more examples, please look at the classes created for the unit tests contained in t/lib.

NB: There are other useful methods, so please read all of the POD. This section simply covers baptise, which was the first method based on Util::H2O::h2o presented in this module.

DESCRIPTION

The primary method, baptise, essentially provides the same interface as the core keyword bless with an additional slurpy third parameter where one may specify a list of default accessors.

Ultimately h2o provides a very compelling approach that allows one to incrementally add OOP into their Perl. At the very least it makes dealing with HASH references much easier, and without the committment to a full Perl OOP framework. Perl is meant to be multi-paradigm, which means that it should be easy to mix the best of different methods into one glorious creation. Util::H2O, and by extension, Util::H2O::More; seeks to accomplish making it possible for OOP concepts.

Util::H2O::h2o is a deceptively powerful tool that, above all, makes it easy and fun to add accessors to ad hoc HASHreferences that many Perl developers like to use and that get returned, unblessed by many popular modules. For example, HTTP::Tiny, Web::Scraper, and the more common select% methods DBI flavors implement. In particular, any JSON returned by a HTTP::Tiny web request is not just ublessed, but still serialized. Yet another great example is the configuration object returned by the very popular module, Config::Tiny.

Still more useful utilities may be built upon h2o, e.g.; d2o which is able to handle data structures that contain HASH references buried or nested arbitrarily within ARRAY references.

For example, d2o cleans things up very nicely for dealing with web APIs:

  my $response = h2o HTTP::Tiny->get($JSON_API_URL);
  die if not $response->success; 
  my $JSON_data_with_accessors = d2o JSON::decode_json $response->content;

Finally, and what started this module; the usage pattern of h2o begs it to be able to support being used as a drop in replacement for bless. But is does a fine job as serving as the basis for a better bless.

METHODS

baptise REF, PKG, LIST

Takes the same first 2 parameters as bless; with the addition of a list that defines a set of default accessors that do not rely on the top level keys of the provided hash reference.

The -recurse option:

Like baptise, but creates accessors recursively for a nested hash reference. Uses h2o's -recurse flag.

Note: The accessors created in the nested hashes are handled directly by h2o by utilizing the -recurse flag. This means that they will necessarily be blessed using the unchangable behavior of h2o, which maintains the name space of Util::H2O::_$hash even if h2o is passed with the -isa and -class flags, which are both utilized to achieve the effective outcome of baptise and bastise -recurse.

opt2h2o LIST

Handy function for working with Getopt::Long, which takes a list of options meant for Getopt::Long; and extracts the flag names so that they may be used to create default accessors without having more than one list. E.g.,

    use Getopt::Long qw//;
    my @opts = (qw/option1=s options2=s@ option3 option4=i o5|option5=s/);
    my $o = h2o {}, opt2h2o(@opts);
    Getopt::Long::GetOptionsFromArray( \@ARGV, $o, @opts ); # Note, @ARGV is passed by reference
    
    # now options are all available as accessors, e.g.:
    if ($o->option3) {
      do_the_thing();
    }

Note: default values for options may still be placed inside of the anonymous hash being objectified via h2o. This will work perfectly well with baptise and friends.

    use Getopt::Long qw//;
    my @opts = (qw/option1=s options2=s@ option3 option4=i o5|option5=s/);
    my $o = h2o { option1 => q{foo} }, opt2h2o(@opts);
    Getopt::Long::GetOptionsFromArray( \@ARGV, $o, @opts ); # Note, @ARGV is passed by reference 

    # ...
    # now $o can be used to query all possible options, even if they were
    # never passed at the commandline 

ini2h2o FILENAME

Takes the name of a file, uses Config::Tiny to open it, then gives it accessors using internally, o2h2o, described below.

Given some configuration file using INI:

  [section1]
  var1=foo
  var2=bar
  
  [section2]
  var3=herp
  var4=derp

We can parse it with Config::Tiny and objectify it with h2o:

  use Util::H2O::More qw/ini2h2o/;
  my $config = ini2h2o qq{/path/to/my/config.ini}
  # ... $config now has accessors based Config::Tiny's read of config.ini

h2o2ini REF, FILENAME

Takes and object created via ini2h2o and writes it back out to FILENAME in the proper INI format, using Config::Tiny.

Given the example in ini2h2o, we can go a step further and writ eout a new configuration file after reading it and modifying a value.

  use Util::H2O::More qw/ini2h2o h2o2ini/;

  my $config = ini2h2o q{/path/to/my/config.ini}

  # update $config, write it out as a different file
  $config->section1->var1("some new value");
  h2o2ini $config, q{/path/to/my/other-config.ini};

o2h2o REF

Primarily inspired by Util::H2O's example for adding accessors to an reference that has already been blessed by another package. The motivating example is one that shows how to add accessors to a Config::Tiny object.

o2h REF

Uses Util::H2O::o2h, so behaves identical to it. A new hash reference is returned, unlike h2o or baptise. See Util::H2O's POD for a lot more information.

This method complements h2o or baptise very well in the sitution, e.g., when one is dealing with an ad hoc object that then needs to be sent as a serialzied JSON string. It's convenient to be able to get the un<bless>'d data structure most JSON encoding methods are expecting.

Implementation note:

Access to Util::H2O::o2h, but adjusts $Util::H2O::_PACKAGE_REGEX to accept package names that are generated by baptise. This effectively is an unbless.

Historical Note:

Util::H2O::More::o2h preceded Util::H2O::o2h, and the author of the latter added it after seeing it's usefulness in cases where the ability to get a pure HASH reference after having objectified was useful. A good example is the standard dislike JSON modules' encode_json method implementations have for blessed references; this also affects environments like Dancer2 or Mojo that employ automatic serialization steps beyond route handlers. Returning a blessed reference would cause the underlying serialization routines to warn or die without using o2h to return a pure HASH reference.

d2o REF

This method is basically a wrapper around h2o that will traverse an arbitrarily complex Perl data structure, applying h2o to any HASH references along the way.

A common usecase where d2o is useful is a web API call that returns some list of HASH references contained inside of an ARRAY reference.

For example,

  my $array_of_hashes = JSON::decode_json $json;
  d2o $array_of_hashes;
  my $co = $array_of_hashes->[3]->company->name;

Here, $array_of_hashes is an ARRAY reference that contains a set of elements that are HASH references; a pretty common situation when dealing with records from an API or database call. [3], refers the 4th element, which is a HASH reference. This HASH reference has an accessor via d2o, and this returns another HASH that has an accessor called name.

The structure of $array_of_hashes is based on JSON, e.g., that is of the form:

  [
    {
      "id": 1,
      "name": "Leanne Graham",
      "username": "Bret",
      "email": "Sincere@april.biz",
      "address": {
        "street": "Kulas Light",
        "suite": "Apt. 556",
        "city": "Gwenborough",
        "zipcode": "92998-3874",
        "geo": {
          "lat": "-37.3159",
          "lng": "81.1496"
        }
      },
      "phone": "1-770-736-8031 x56442",
      "website": "hildegard.org",
      "company": {
        "name": "Romaguera-Crona",
        "catchPhrase": "Multi-layered client-server neural-net",
        "bs": "harness real-time e-markets"
      }
    },
    {
      "id": 2,
      "name": "Ervin Howell",
      "username": "Antonette",
      "email": "Shanna@melissa.tv",
      "address": {
        "street": "Victor Plains",
        "suite": "Suite 879",
        "city": "Wisokyburgh",
        "zipcode": "90566-7771",
        "geo": {
          "lat": "-43.9509",
          "lng": "-34.4618"
        }
      },
      "phone": "010-692-6593 x09125",
      "website": "anastasia.net",
      "company": {
        "name": "Deckow-Crist",
        "catchPhrase": "Proactive didactic contingency",
        "bs": "synergize scalable supply-chains"
      }
    },
    ...
  ]

  (* froms, https://jsonplaceholder.typicode.com/users)

h2o's -array Modifier

As of version 0.20 of Util::H2O, h2o has now a -arrays modifier that does something very similar to d2o, which was released shortly after -arrays was released in Util::H2O. Had the author of this module known about it, he would not have created d2o. Nonetheless, d2o does some things -arrays doesn't do (and similarly, when o2h -arrays versus o2d).

The biggest difference seems to be that h2o doesn't bless the ARRAY containers or provide virtual methods. Be advised, however, -arrays is probably sufficient for the use case d2o was originally create for; i.e., to more easily objectify complicated data structures obtained from things like JSON returned from web APIs. the virtual methods added to ARRAYs is not something one would expect form h2o, which strives to provide a lite or tiny touch. The vmethods do make iterating over the ARRAYs easier, though.

o2d REF

Does for data structures objectified with d2o what o2h does for objects created with h2o. It only removes the blessing from Util::H2O:: and Util::H2O::More::__a2o references.

a2o REF

Used internally.

Used internally to give virual methods to ARRAY ref containers potentially holding HASH references.

a2o is not intended to be useful outside of the context of d2o, but it's exposed in case it is, anyway.

ARRAY container vmethods

It is still somewhat inconvenient, though idiomatic, to refer to ARRAY elements directly as in the example above. However, it is still inconsistent with idea of Util::H2O. So, d2o leans into its heavy nature by adding some "virtual" methods to ARRAY containers.

all

Returns a LIST of all items in the ARRAY container.

  my @items = $root->some-barray->all;

get INDEX

Given an ARRAY container from d2o, returns the element at the given index. See push example below for a practical example.

push LIST

Pushes LIST onto ARRAY attached to the vmethod called; also applies the d2o method to anything pushed.

  my @added = $root->some->barray->push({ foo => 1 }, {foo => 2});
  my $one   = $root->some->barray->get(0)->foo; # returns 1 via "get"
  my $two   = $root->some->barray->get(1)->foo; # returns 2 via "get"

Items that are push'd are returned for convenient assignment.

pop

Pops an element from ARRAY container available after applying d2o to a structure that has ARRAY refs at any level.

  my $item = $root->some-barray->pop;

unshift LIST

Similar to push, just operates on the near end of the ARRAY.

Items that are shift'd are returned for convenient assignment.

shift

Similar to pop, just operates on the near end of the ARRAY.

scalar

Returns the number of items in the ARRAY container, which is more convenient that doing,

  my $count = scalar @{$root->some->barray->all}; 

EXTERNAL METHODS

h2o

Because Util::H2O::More exports h2o as the basis for its operations, h2o is also available without needing to qualify its full name space.

DEPENDENCIES

Util::H2O

Requires Util::H2O because this module is effectively a wrapper around h2o.

It also uses the state keyword, which is only available in perls >= 5.10.

While some methods are designed to work with external modules, e.g., opt2h2o is meant to work with Getopt::Long; at this time there are no dependencies for such methods required by Util::H2O::More itself.

BUGS

No. I mean maybe. Buyer beware.

LICENSE AND COPYRIGHT

Perl/perl

ACKNOWLEDGEMENTS

Thank you to HAUKEX for creating Util::H2O and hearing me out on its usefulness for some unintended use cases.

AUTHOR

Oodler 577 <oodler@cpan.org>