St. Nick's Reindeers Need More H2O!
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 forbless
, 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
- likeh2o
, but dives deeply intoARRAY
refs, e.g., that containHASH
refs; and appliesh2o
to themo2d
- likeo2h
, but acts on deeply objectified references that were created byd2o
ddd
- a shortcut around dumping references toSTDERR
viaData::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!