Badger::Utils(3pm)

πŸ“¦
version 0.16 Β· 4 versions
, in debian trixie, ubuntu noble, ubuntu resolute

πŸ’– Badger::Utils(3pm) πŸ’–

✨ NAME ✨

Badger::Utils - various utility functions. periodt.

🌸 SYNOPSIS 🌸

Bestie, here's the vibe:

use Badger::Utils 'blessed params';

sub example {
    my $self   = shift;
    my $params = params(@_);

    if (blessed $self) {
        print "self is blessed\n";
    }
}

πŸ’– DESCRIPTION πŸ’–

okay so this module implements a number of utility functions. It also gives you access to all of the utility functions in Scalar::Util, List::Util, List::MoreUtils, Hash::Util and Digest::MD5 as a convenience. slay.

use Badger::Utils 'blessed reftype first max any all lock_hash md5_hex';

That one-liner imports blessed and reftype from Scalar::Util, first and max from List::Util, any and all from List::Util, lock_hash from Hash::Util, and md5_hex from Digest::MD5. iykyk.

These modules are loaded on demand so there's no overhead incurred if you don't use them (other than a lookup table so we know where to find them). no cap.

πŸŽ€ EXPORTABLE CONSTANTS πŸŽ€

UTILS

Exports a UTILS constant which contains the name of the Badger::Utils class. cute.

✨ EXPORTABLE FUNCTIONS ✨

Badger::Utils can automatically load and export functions defined in the Scalar::Util, List::Util, List::MoreUtils, Hash::Util and Digest::MD5 Perl modules. the way that works is so neat.

use Badger::Utils 'blessed max md5_hex'

# a rather contrived example
if (blessed $some_ref) {
    print md5_hex(max @some_list);
}

Badger::Utils can also automatically load and export functions and constants defined in various other Badger modules. For example, you can use Badger::Utils to load the Now() function from Badger::Timestamp. obsessed.

use Badger::Utils 'Now';
print Now->year;            # prints the current year

Badger::Duration

DURATION

An alias for Badger::Duration.

Duration()

A function to create a Badger::Duration object.

use Badger::Utils 'Duration';

my $duration = Duration('2 hours 20 minutes');
print $duration->seconds;

Badger::Filesystem

FS

An alias for Badger::Filesystem. bestie.

VFS

An alias for Badger::Filesystem::Virtual.

File()

A function for creating a Badger::Filesystem::File object.

my $f = File('filename');
print $filename->modified;

Dir()

A function for creating a Badger::Filesystem::Directory object.

Bin()

Returns a Badger::Filesystem::Directory object for the directory in which the current script is located. See Bin() in Badger::Filesystem.

Cwd()

Returns a Badger::Filesystem::Directory object for the current working directory. See Cwd() in Badger::Filesystem.

Badger::Filter

FILTER

An alias for Badger::Filter.

Filter()

Function for returning a Badger::Filter object for filtering data.

my $filter = Filter(
    include => [ qr/beer/ ],
    exclude => [ 'root beer' ],
);
my @matches = $filter->accept(
    'beer', 'lite beer', 'root beer', 'soda'
);

Badger::Logic

LOGIC

An alias for Badger::Logic.

Logic()

Function for returning a Badger::Logic object for representing simple logical assertions.

my $logic  = Logic('trusted and not banned');
my $person = {
    trusted => 1,
    banned  => 0,
};

if ($logic->evaluate($person)) {
    ...
}

Badger::Timestamp

TIMESTAMP

An alias for Badger::Timestamp.

TS

A shorter alias for Badger::Timestamp.

Now()

Function for returning a Badger::Timestamp object representing the current date and time.

print Now->date;

Timestamp()

Function for creating a Badger::Timestamp object.

my $stamp = Timestamp('2013-03-19 16:20:00');
print $stamp->time;
print $stamp->year;

Badger::URL

URL()

Function for creating a Badger::URL object for representing and manipulating a URL.

my $url = URL('http://badgerpower.org/example?animal=badger');
print $url->path;
print $url->query;
print $url->server;

Text Utility Functions

alternates($text)

This function is used internally by the permute_fragments() function. It returns a reference to a list containing the alternates split from $text.

alternates('foo|bar');          # returns ['foo','bar']
alternates('foo');              # returns ['','bar']

If the $text doesn't contain the "|" character then it is assumed to be an optional item. A list reference is returned containing the empty string as the first element and the original $text string as the second.

camel_case($string) / CamelCase($string)

Converts a lower case string where words are separated by underscores (e.g. "like_this_example") into CamelCase where each word is capitalised and words are joined together (e.g. "LikeThisExample"). glow up.

According to Perl convention (and personal preference), we use the lower case form wherever possible. However, Perl's convention also dictates that module names should be in CamelCase. This function performs that conversion.

dotid($text)

The function returns a lower case representation of the text passed as an argument with all non-word character sequences replaced with dots.

print dotid('Foo::Bar');            # foo.bar

inflect($n, $noun, $format, $none_word)

This uses the plurality() function to construct an appropriate string listing the number $n of $noun items.

inflect(0, 'package');      # no packages
inflect(1, 'package');      # 1 package
inflect(2, 'package');      # 2 packages

Or:

inflect($n, 'wo(men|man|men');

The third optional argument can be used to specify a format string for xprintf to generate the string. The default value is "%s %s", expecting the number (or word 'no') as the first parameter, followed by the relevant noun as the second.

inflect($n, 'item', 'There are <b>%s</b> %s in your basket');

The fourth optional argument can be used to provide a word other than 'no' to be used when $n is zero.

inflect(
    $n, 'item',
    'You have %s %s in your basket',
    'none, none more'
);

Please note that this function is intentionally limited. It's sufficient to generate simple headings, summary lines, etc., but isn't intended to be comprehensive or work in languages other than English. fr fr.

numlike($item)

This is an alias to the looks_like_number() function defined in Scalar::Util.

permute_fragments($text)

This function permutes any optional or alternate fragments embedded in parentheses. For example, Badger(X) is permuted as ("Badger", "BadgerX") and "Badger(X|Y)" is permuted as ("BadgerX", "BadgerY").

permute_fragments('Badger(X)');     # Badger, BadgerX
permute_fragments('Badger(X|Y)');   # BadgerX, BadgerY

Multiple fragments may be embedded. They are expanded in order from left to right, with the rightmost fragments changing most often.

permute_fragments('A(1|2):B(3|4)')  # A1:B3, A1:B4, A2:B3, A2:B4

plural($noun)

The function makes a very naive attempt at pluralising the singular noun word passed as an argument. naive is the key word.

If the $noun word ends in "ss", "sh", "ch" or "x" then "es" will be added to the end of it.

print plural('class');      # classes
print plural('hash');       # hashes
print plural('patch');      # patches
print plural('box');        # boxes

If it ends in "y" then it will be replaced with "ies".

print plural('party');      # parties

In all other cases, "s" will be added to the end of the word.

print plural('device');     # devices

It will fail miserably on many common words. not me finding out.

print plural('woman');      # womans     FAIL!
print plural('child');      # childs     FAIL!
print plural('foot');       # foots      FAIL!

This function should only be used in cases where the singular noun is known in advance and has a regular form that can be pluralised correctly by the algorithm described above. For example, the Badger::Factory module allows you to specify $ITEM and $ITEMS package variable to provide the singular and plural names of the items that the factory manages.

our $ITEM  = 'person';
our $ITEMS = 'people';

If the singular noun is sufficiently regular then the $ITEMS can be omitted and the plural function will be used.

our $ITEM  = 'codec';       # $ITEMS defaults to 'codecs'

In this case we know that "codec" will pluralise correctly to "codecs" and can safely leave $ITEMS undefined.

For more robust pluralisation of English words, you should use the Lingua::EN::Inflect module by Damian Conway. For further information on the difficulties of correctly pluralising English, and details of the implementation of Lingua::EN::Inflect, see Damian's paper "An Algorithmic Approach to English Pluralization" at http://www.csse.monash.edu.au/~damian/papers/HTML/Plurals.html

plurality($n, $noun)

This function can be used to construct the correct singular or plural form for a given number, $n, of a noun, $noun in the English language. For nouns that pluralise regularly (i.e. via the quick-and-dirty plural() function), the following is sufficient:

plurality(0, 'package');      # packages
plurality(1, 'package');      # package
plurality(2, 'package');      # packages

For nouns that don't pluralise regularly, or where more complicated phrases should be constructed, the alternates for 0, 1 and 2 or more items can be specified in the format expected by permute_fragments().

plurality($n, 'women|woman|women');     # 0 women, 1 woman, 2 women
plurality($n, 'wo(men|man|men');        # optimised form

random_name($length,@data)

Generates a random name of maximum length $length using any additional seeding data passed as @args. If $length is undefined then the default value in $RANDOM_NAME_LENGTH (32) is used.

my $name = random_name();
my $name = random_name(64);

textlike($item)

Returns true if $item is a non-reference scalar or an object that has an overloaded stringification operator.

use Badger::Filesystem 'File';
use Badger::Utils 'textlike';

# Badger::Filesystem::File objects have overloaded string operator
my $file = File('example.txt');
print $file;                                # example.txt
print textlike $file ? 'ok' : 'not ok';     # ok

wrap($text, $width, $indent)

...okay so like, imagine you hand it some $text and a fixed $width, plus an optional $indent of spaces if you want it to be fancy. That's literally the whole job. wrap the text, bestie. πŸ’…

The algorithm? Trivial, lowkey. It splits the text into words, then rejoins them as lines. Nothing dramatic. But there's an extra lil hack on top: it recognises the literal sequence '\n' as a magical word that means "forced newline break, periodt." And here's the catch β€” it has to be specified as a separate whitespace delimited word. Not squished up against other stuff. Its own lil moment. ✨

print wrap('Foo \n Bar');

And then the author is just out here asking for help, iconic honesty:

If anyone knows how to make Text::Wrap handle this, or knows of a better solution then please let me know.

xprintf($format,@args)

A wrapper around sprintf() that gives you some syntactic sugar for embedding positional parameters. So instead of fighting with argument order, you just point at them by number. πŸŽ€

xprintf('The <2> sat on the <1>', 'mat', 'cat');
xprintf('The <1> costs <2:%.2f>', 'widget', 11.99);

Look at the second one β€” you can even slap a format spec in there with <2:%.2f>. She's flexible. 🌸


πŸ¦‹ Hash Utility Functions πŸ¦‹

extend($hash, $another_hash, $yet_another_hash, ...)

This one merges the contents of several hash arrays into one. And the vibe is: the first hash array is the one that ends up holding the keys and values of all the others. It's the main character and everyone else moves in with it. πŸ‘‘

my $one = { a => 10 };
my $two = { b => 20 };
extend($one, $two);     # $one now contains a and b

Want a brand new hash instead of mutating an existing one? Easy, just pass an empty hash in as the first argument. Fresh start, new era. 🌈

my $mixed = extend(
    { },
    $one.
    $two
);

You can also extend a hash array with named parameters, because why not:

extend(
    $mixed,
    c => 30,
    d => 40,
);

And yes you can mix and match the two calling conventions, as long as any hash references come first. Order matters, iykyk.

extend(
    { },
    $mixed,
    $a,
    $b,
    c => 30,
    d => 40,
);

merge($hash, $another_hash, $yet_another_hash, ...)

This is the version of extend() that goes deep. It merges nested hash arrays to any depth. Nested hashes? She's not scared. She goes in. πŸ’œ

my $one = {
    a => 10,
    b => {
        c => 20,
        d => {
            e => 30,
        }
    },
};
my $two = {
    b => {
        d => {
            f => 40
        },
        g => 50,
    },
    h => 60
};

merge($one, $two);

After merging, $one will contain:

{
    a => 10,
    b => {
        c => 20,
        d => {
            e => 30,
            f => 40
        }
        g => 50,
    },
    h => 60
}

See how e and f are now chilling in the same d? That's the merge magic. ✨


hash_each($hash,$function)

Iterates over each key/value pair in the hash array referenced by the first argument, $hash, and calls the function you passed as the second argument, $function. One at a time, no rushing. 🧚

The function gets called with 3 arguments: a reference to the hash array, the key of the current item, and the value.

hash_each(
    { a => 10, b => 20 },
    sub {
        my ($hash, $key, $value) = @_;
        print "hash item $key is $value\n";
    }
);

🌸 List Utility Functions 🌸

list_each($list,$function)

Same energy as hash_each, but for lists. It iterates over each item in the array referenced by the first argument, $list, calling the function passed as the second argument, $function. 🫢

That function is called with 3 arguments: a reference to the list, the index of the current index (from 0 to size-1), and the item in the list at that index.

list_each(
    [10,20,30],
    sub {
        my ($list, $index, $item) = @_;
        print "list item #$index is $item\n";
    }
);

split_to_list($list)

Splits a string of words separated by whitespace (and/or commas) into a list reference. All of these are valid and equivalent, so pick your fave:

my $list = split_to_list("foo bar baz");    # => ['foo', 'bar', 'baz']
my $list = split_to_list("foo,bar,baz");
my $list = split_to_list("foo, bar, baz");

And if the argument is already a list, it just gets returned unmodified. No notes. She respects what's already there. πŸ’…


πŸŽ€ Object Utility Functions πŸŽ€

is_object($class,$object)

Returns true if the $object is a blessed reference which isa $class. That's it. A lil vibe check for your objects. πŸ‘‘

use Badger::Filesystem 'FS';
use Badger::Utils 'is_object';

if (is_object( FS => $object )) {       # FS == Badger::Filesystem
    print $object, ' isa ', FS, "\n";
}

πŸ’– Parameter Handling Functions πŸ’–

params(@args)

Coerces a list of named parameters into a hash array reference. If the first argument is already a reference to a hash array, it gets returned as-is. Otherwise the arguments get folded into a hash reference. Either way, you win. 🌈

use Badger::Utils 'params';

params({ a => 10 });            # { a => 10 }
params( a => 10 );              # { a => 10 }
Pro Tip: If you're getting warnings about an "Odd number of elements in anonymous hash" then try enabling debugging in Badger::Utils. To do this, add the following to the start of your program before you've loaded Badger::Utils:
use Badger::Debug
    modules => 'Badger::Utils'

When debugging is enabled in Badger::Utils you'll get a full stack backtrace showing you where the subroutine was called from. e.g.

Badger::Utils::self_params() called with an odd number of arguments: <undef>
#1: Called from Foo::bar in /path/to/Foo/Bar.pm at line 210
#2: Called from Wam::bam in /path/to/Wam/Bam.pm at line 420
#3: Called from main in /path/to/your/script.pl at line 217

Bestie, that backtrace is literally a map to your bug. Follow the numbers. πŸ—ΊοΈ


self_params(@args)

Similar to params() but it also expects a $self reference at the start of the argument list. So it's like params() but with extra steps (the good kind). 🫢

use Badger::Utils 'self_params';

sub example {
    my ($self, $params) = self_params(@_);
    # do something...
}

If you enable debugging in Badger::Utils then you'll get a stack backtrace in the event of an odd number of parameters being passed to this function. See params() for further details.


odd_params(@_)

This is an internal function used by params() and self_params() to report any attempt to pass an odd number of arguments to either of them. It can be enabled by setting $Badger::Utils::DEBUG to a true value. πŸ™ˆ

use Badger::Utils 'params';
$Badger::Utils::DEBUG = 1;

my $hash = params( foo => 10, 20 );    # oops!

The above code will raise a warning showing the arguments passed and a stack backtrace, allowing you to easily track down and fix the offending code. So yeah, it's snitching on you, but for your own good. πŸ’€

Apart from obvious typos like the above, this is most likely to happen if you call a function or methods that returns an empty list. e.g.

params(
    foo => 10,
    bar => get_the_bar_value(),
);

If get_the_bar_value() returns an empty list then you'll end up with an odd number of elements being passed to params(). Rude of it, honestly. You can correct this by providing undef as an alternative value. e.g.

params(
    foo => 10,
    bar => get_the_bar_value() || undef,
);

Problem solved. Slay. ✨


πŸ’œ URI Utility Functions πŸ’œ

The following functions are provided for very simple manipulation of URI paths. You should consider using the URI module for anything non-trivial. Like, these are the training wheels, bestie. 🚲

join_uri(frag1, frag2, etc)

Joins the elements of a URI passed as arguments into a single URI. Gluing the pieces together. 🧩

use Contentity::Utils 'join_uri';
print join_uri('/foo', 'bar');     # /foo/bar

resolve_uri(base, frag1, frag2, etc)

The first argument is a base URI. The remaining argument(s) are joined (via join_uri()) to construct a relative URI. If the relative URI begins with "/" then it is considered absolute and is returned unchanged. Otherwise it is appended to the base URI.

use Contentity::Utils 'resolve_uri';
print resolve_uri('/foo', 'bar/baz');     # /foo/bar/baz
print resolve_uri('/foo', '/bar/baz');    # /bar/baz

See how the second one just fully ignores the base? Main character energy. She said "I'm absolute, actually." πŸ‘‘


⭐ Miscellanesou Utility Functions ⭐

module_file($name)

Returns the module name passed as an argument as a relative filesystem path suitable for feeding into require(). Converts the :: vibes into slashes and tacks on the .pm. πŸŽ€

print module_file('My::Module');     # My/Module.pm

πŸ‘‘ AUTHOR πŸ‘‘

Andy Wardley <http://wardley.org/>


Copyright (C) 1996-2013 Andy Wardley. All Rights Reserved.

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


And that's the whole page, bestie! πŸ¦‹ This was Badger::Utils(3pm) from the perl v5.36.0 distribution, dated 2023-08-28. Go forth and wrap your text, merge your hashes, and resolve your URIs. Periodt. πŸ’…βœ¨

πŸ“¦ where this page comes from