2023 twenty-four merry days of Perl Feed

St. Nick's Reindeers Need More H2O!

Util::H2O::More - 2023-12-22

Last year in 2022 [1], Santa Claus discovered a very useful Perl module called Util::H2O that makes it very seamless to add object oriented programming conveniences to Perl programs; it is particularly useful when it comes to adding Perl OOP to existing or legacy code bases.

Over the past year, he's done quite a bit of digging into a related module named, Util::H2O::More. The following is a summary of a few neat things available in this module, most of which are built upon Util::H2O's h2o method which gives accessors to to HASH references - and the inverse, that converts an blessed reference with accessors into a pure HASH reference, o2h. There's a mnemonic there, hash 2 object; and the reverse, oject 2 hash. All of the public methods try to follow this pattern, which will hopefully be apparent below.

Santa's Got Options... for Options

There are lots of modules on CPAN that present Getopt::Long in different ways. Util::H2O::More makes its entry into the mix with two different methods; one that builds upon the other.

The first is a nifty method called opt2h2o. This method makes it more convenient to define the fields of a reference destined to be objectified by h2o by using the parameter array description format used by Getopt::Long::GetOptionsFromArray.

opt2h2o is used to provide a list of default accessors to h2o to a given HASH reference. This reference is then provided to GetOptionsFromArray, as shown in the example below that can explain better than mere words:

use strict;
use warnings;
use Util::H2O::More qw/h2o opt2h2o/;
use Getopt::Long qw//;

my @opts = (qw/name=s country=s meaning=s/);
my $o = h2o { meaning => q{???} }, opt2h2o(@opts); # example showing how to set default
Getopt::Long::GetOptionsFromArray( \@ARGV, $o, @opts );

if ($o->country) {
  printf qq{Did you know, Santa is known as '%s' in the country/region of %s? It means, "%s"!\n},
    $o->name, $o->country, $o->meaning;
}

Which results in,

prompt> perl Santa-facts.pl --name Jultomten --meaning "Christmas Brownie" --country Sweden
Did you know, Santa is known as 'Jultomten' in the country/region of Sweden? It means, "Christmas Brownie"!

But as famous elvish pitchman, Willy Nays says, but wait, there's more ...!

Util::H2O::More has a method that combines everything, including the need to require Getopt::Long;, called Getopt2h2o; it can dramatically shorten the amount of typing needed to process options and provide accessors to the options:

use strict;
use warnings;
use Util::H2O::More qw/Getopt2h2o/;

my $o = Getopt2h2o \@ARGV, { meaning => q{???} }, qw/name=s country=s meaning=s/;

if ($o->country) {
  printf qq{Did you know, Santa is known as '%s' in the country/region of %s? It means, "%s"!\n},
    $o->name, $o->country, $o->meaning;
}

Which results in,

prompt> perl ./Santa-facts.pl --name Christkindl --meaning "Christ child" --country Germany
Did you know, Santa is known as 'Christkindl' in the country/region of Germany? It means, "Christ child"!

Personally Santa prefers this version the best. He still loves brownies very much, as his waistline can attest - especially with red soda from the local grocer!

Ye Old Configuration Files

Config::Tiny

Util::H2O::More also has a couple of convenient options for handling conf files written in both .ini and YAML formats.

The first method is designed to provide accessors to configuration references provided by Config::Tiny; in fact there is no need to explicitly use or require Config::Tiny.

We begin with an INI format configuratino file, santa.ini:

  ; santa.ini
  ;   this is an ini file that is
  ;   used to power Santa's Facts
  ;   generating Perl script

  [SantaByCountry]
  Afghanistan=Aba Chaghaloo, ???
  Greece=Αγιος Βασίλης, Santa Clause
  Sweden=Jultomten, Christmas Brownie
  Austria=Christkind, Christ Child
  Switzerland=Christkindl, Christ Child
  Germany=Christkindle, Christ Child
  Mexico=Niño Dios, God Child
  CentralAmerica=Niño Jesús, Baby Jesus

  ; sample from https://www.historiesdarkmysteries.com/2021/12/the-surprising-origins-of-jolly-old-st.html

Combining the joys of Getopt2h2o with ini2h2o, we get a pretty compact script with minimal set up. The example below is not missing it's statement to use Config::Tiny - ini2h2o handles that internally.

use strict;
use warnings;
use Util::H2O::More qw/Getopt2h2o ini2h2o/;

my $o = Getopt2h2o \@ARGV, { conf => q{./santa.ini} }, qw/conf=s/;

my $conf = ini2h2o $o->conf;

print qq{Did You Know:\n};
foreach my $country (keys %{$conf->SantaByCountry}) {
    my $entry = $conf->SantaByCountry->$country;
    my ($name, $meaning) = split /, +/, $entry;
    printf qq{ + Santa is known as '%s' in the country/region of %s? It means, "%s"!\n},
      $name, $country, $meaning;
}

And running this script with the configuration file yields,

prompt> perl ./Santa-facts.pl
Did You Know:
  + Santa is known as 'Aba Chaghaloo' in the country/region of Afghanistan? It means, "???"!
  + Santa is known as 'Niño Jesús' in the country/region of CentralAmerica? It means, "Baby Jesus"!
  + Santa is known as 'Niño Dios' in the country/region of Mexico? It means, "God Child"!
  + Santa is known as 'Christkindle' in the country/region of Germany? It means, "Christ Child"!
  + Santa is known as 'Christkindl' in the country/region of Switzerland? It means, "Christ Child"!
  + Santa is known as 'Jultomten' in the country/region of Sweden? It means, "Christmas Brownie"!
  + Santa is known as 'Christkind' in the country/region of Austria? It means, "Christ Child"!
  + Santa is known as 'Î~QγιοÏ~B Î~RαÏ~CίληÏ~B' in the country/region of Greece? It means, "Santa Clause"!

He is now able to build up a configuration file that has considerably more facts for his Did you know ...? script.

The script even uses the aforementioned method, Getopt2h2o, to enable a --conf flag in case Santa ever wanted to use a different configuration file.

prompt> perl ./Santa-facts.pl --config ./santa2.ini
...

Given a $config reference, h2o2ini gives Santa the ability to write the reference back as an .ini file using Config::Tiny's write method underneath the hood.

If Santa had modifed the .ini file, then he could have written it back to disk using the inverse method, h2o2ini.

YAML Files

    ---
    Santa:
      By:
        Country:
          Afghanistan:
            name: Aba Chaghaloo
            meaning: ???
          Greece:
            name: Αγιος Βασίλης
            meaning: Santa Clause
          Sweden:
            name: Jultomten
            meaning: Christmas Brownie
          Austria:
            name: Christkind
            meaning: Christ Child
          Switzerland:
            name: Christkindl
            meaning: Christ Child
          Germany:
            name: Christkindle
            meaning: Christ Child
          Mexico:
            name: Niño Dios
            meaning: God Child
          CentralAmerica:
            name: Niño Jesús
            meaning: Baby Jesus

Here, again, there is no need to use YAML. The yaml2h2o method employed below handles that behind the scenes, as well.

use strict;
use warnings;
use Util::H2O::More qw/Getopt2h2o yaml2h2o/;

# author note: added because it was needed with the YAML example
# but not the .ini example, not really sure why ...
use open qw( :std :encoding(UTF-8) );

my $o = Getopt2h2o \@ARGV, { conf => q{./santa.yaml} }, qw/conf=s/;

my ($conf) = yaml2h2o $o->conf;

print qq{Did You Know:\n};
foreach my $country (keys %{$conf->Santa->By->Country}) {
    my $name = $conf->Santa->By->Country->$country->name;
    my $meaning = $conf->Santa->By->Country->$country->meaning;
    printf qq{ + Santa is known as '%s' in the country/region of %s? It means, "%s"!\n},
      $name, $country, $meaning;
}

And running this script with the YAML configuration file now yields,

  prompt> perl ./Santa-facts.pl
  Did You Know:
    + Santa is known as 'Aba Chaghaloo' in the country/region of Afghanistan? It means, "???"!
    + Santa is known as 'Niño Jesús' in the country/region of CentralAmerica? It means, "Baby Jesus"!
    + Santa is known as 'Niño Dios' in the country/region of Mexico? It means, "God Child"!
    + Santa is known as 'Christkindle' in the country/region of Germany? It means, "Christ Child"!
    + Santa is known as 'Christkindl' in the country/region of Switzerland? It means, "Christ Child"!
    + Santa is known as 'Jultomten' in the country/region of Sweden? It means, "Christmas Brownie"!
    + Santa is known as 'Christkind' in the country/region of Austria? It means, "Christ Child"!
    + Santa is known as 'Αγιος Βασίλης' in the country/region of Greece? It means, "Santa Clause"!

There are many other cool aspects to Util::H2O::More that Santa wishes to show you, but he hopes that this Christmas break you discover all the time and lines of code that this module has to offer for a vast number of applications.

There is no h2o2yaml (or ways t handle JSON date either way), but if someone reading has the need, the author is quite active on Github and may be more than happy to help out a friend in need.

There is Still More!

Util::H2O::More is any Perl programmer's secret weapon for dealing with very common things that result ultimately in slinging around a bunch of HASH references - and occasionally, very complex structures that include ARRAY refs.

It builds on Util::H2O's massively powerful combination of h2o and o2h to provide ad hoc accessors to data in flight - and to remove them again when needed.

Some other pretty useful methods provided for by Util::H2O::More are:

baptise - a drop-in replacement for bless, but allows accessors to be defined:
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;
d2o - like h2o, but dives deeply into ARRAY refs, e.g., that contain HASH refs; and applies h2o to them
o2d - like o2h, but acts on deeply objectified references that were created by d2o
ddd - a shortcut around dumping references to STDERR via Data::Dumper

Conclusion

Getting this far down into the article is quite an achievement. Congratulations for persevering with your faith in Perl. As you can see, Santa is very pleased to indulge in the true meaning of Christmas - which obviously, has everything to with spreading as much practical Perl knowledge as possible.

Joyeux Noël!

1. https://perladvent.org/2022/2022-12-06.html
2. https://metacpan.org/pod/Util::H2O::More
3. https://metacpan.org/pod/Util::H2O
4. https://www.historiesdarkmysteries.com/2021/12/the-surprising-origins-of-jolly-old-st.html
Gravatar Image This article contributed by: oodler@cpan.org