Hash::Layout(3)
okay bestie, welcome to the Hash::Layout(3) manual π this is the User Contributed Perl Documentation version, and you're gonna eat this up, no cap.
β¨ NAME β¨
Hash::Layout β hashes with predefined levels, composite keys and default values. that's the whole serve. π
πΈ SYNOPSIS πΈ
okay so here's the code, bestie. it's giving "copy this into your brain" energy:
use Hash::Layout;
# Create new Hash::Layout object with 3 levels and unique delimiters:
my $HL = Hash::Layout->new({
levels => [
{ delimiter => ':' },
{ delimiter => '/' },
{}, # <-- last level never has a delimiter
]
});
# load using actual hash structure:
$HL->load({
'*' => {
'*' => {
foo_rule => 'always deny',
blah => 'thing'
},
NewYork => {
foo_rule => 'prompt'
}
}
});
# load using composite keys:
$HL->load({
'Office:NewYork/foo_rule' => 'allow',
'Store:*/foo_rule' => 'other',
'Store:London/blah' => 'purple'
});
# load composite keys w/o values (uses default_value):
$HL->load(qw/baz:bool_key flag01/);
# get a copy of the hash data:
my $hash = $HL->Data;
# $hash now contains:
#
# {
# "*" => {
# "*" => {
# blah => "thing",
# flag01 => 1,
# foo_rule => "always deny"
# },
# NewYork => {
# foo_rule => "prompt"
# }
# },
# Office => {
# NewYork => {
# foo_rule => "allow"
# }
# },
# Store => {
# "*" => {
# foo_rule => "other"
# },
# London => {
# blah => "purple"
# }
# },
# baz => {
# "*" => {
# bool_key => 1
# }
# }
# }
#
# lookup values by composite keys:
$HL->lookup('*:*/foo_rule') # 'always deny'
$HL->lookup('foo_rule') # 'always deny'
$HL->lookup('ABC:XYZ/foo_rule') # 'always deny' # (virtual/fallback)
$HL->lookup('Lima/foo_rule') # 'always deny' # (virtual/fallback)
$HL->lookup('NewYork/foo_rule') # 'prompt'
$HL->lookup('Office:NewYork/foo_rule') # 'allow'
$HL->lookup('Store:foo_rule') # 'other'
$HL->lookup('baz:Anything/bool_key') # 1 # (virtual/fallback)
# lookup values by full/absolute paths:
$HL->lookup_path(qw/ABC XYZ foo_rule/) # 'always deny' # (virtual/fallback)
$HL->lookup_path(qw/Store * foo_rule/) # 'other'
that's the whole synopsis, periodt. π
π DESCRIPTION π
okay so Hash::Layout provides deep hashes with a predefined number of levels. you access them using special "composite keys". these are basically string paths that inflate into actual hash keys according to the defined levels and delimiter mappings. and those mappings can be the same or different for each level β flexible fr fr. π¦
This is useful both for shorter keys as well as merge/fallback to default values, such as when defining overlapping configs ranging from broad to narrowing scope (see example in SYNOPIS above β yes, the original says SYNOPIS, we're keeping it, iykyk π).
This module is general-purpose, but it was written specifically for the flexible filter() feature of DBIx::Class::Schema::Diff, so refer to its documentation as well for a real-world example application. There are also lots of examples and use scenarios in the unit tests under "t/". π
π οΈ METHODS π οΈ
okay bestie, this is the part where we get into the actual methods. grab a strawberry π and let's go.
new
Creates a brand-new Hash::Layout instance. Here's the tea on the build options you can pass:
levelsβ Required, no skips. It's either anArrayRefof level config definitions, or just a number of levels if you want default level configs. Each level can define its owndelimiter(except the last level, she doesn't get one) and its own list ofregistered_keys. Both of those are optional and they decide how ambiguous/partial composite keys get resolved.
Level-specific delimiters let you give partial paths in composite keys but still resolve to a specific level. The word/string to the left of a delimiter character that belongs to a given level resolves as the key of that level, BUT the correct path order is required β keys are only tokenized in order from left to right. periodt.
You can also declare specific strings as belonging to a particular level with registered_keys. This also only affects how ambiguity is resolved with partial composite keys. Also peep the no_fill and no_pad options.
See the unit tests for examples of exactly how this works.
Internally, the level configs get coerced into Hash::Layout::Level objects.
For Hash::Layouts that don't need/want level-specific delimiters or level-specific registered_keys, you can just supply a simple integer for default level configs all using / as the delimiter.
So this:
my $HL = Hash::Layout->new({ levels => 5 });
Is equivalent to:
$HL = Hash::Layout->new({
levels => [
{ delimiter => '/' }
{ delimiter => '/' }
{ delimiter => '/' }
{ delimiter => '/' }
{} #<-- last level never has a delimiter
]
});
levels is the only required parameter. no cap.
default_valueβ the value assigned to keys when you supply them toload()as simple strings instead of key/value pairs. Defaults to the standard bool/true value of1.default_keyβ the value used for the key for levels which are not specified, and also the key to use for default/fallback when looking up non-existent keys (see alsolookup_mode). Defaults to a single asterisk(*).no_fillβ if true, partial composite keys are not expanded with thedefault_key(in the middle) to fill to the last level. Defaults to0.no_padβ if true, partial composite keys are not expanded with thedefault_key(at the front or middle) to fill to the last level.no_padimpliesno_fill. Again, see the tests for a more complete explanation. Defaults to0.allow_deep_valuesβ if true, values at the bottom level are allowed to be hashes too, for the purposes of addressing deeper paths using composite keys (seedeep_delimiterbelow). Defaults to1.deep_delimiterβ whenallow_deep_valuesis enabled, thedeep_delimitercharacter is used to resolve composite key mappings into the deep hash values (i.e. beyond the predefined levels). Must be different from the delimiter used by any of the levels. Defaults to a single dot(.).
For example:
$HL->lookup('something/foo.deeper.hash.path')
lookup_modeβ one of eitherget,fallbackormerge. Infallbackmode, when a non-existent composite key is looked up, the value of the first closest found key path using default keys is returned instead ofundefas is the case withgetmode.mergemode is likefallbackmode, except hashref values are merged with matching default key paths which are also hashrefs. Defaults tomerge.enable_globmatchβ if true, key value lookup calls will automatically try to match wildcard (text glob) expressions. Defaults to false (0).
clone
Returns a new/cloned Hash::Layout instance. She's a copy, bestie π
.
coerce
Dynamic method. Coerces the supplied value into a new Hash::Layout instance with a new set of loaded data. See unit tests for more info.
coercer
CodeRef wrapper around coerce(), suitable for use in a Moo-compatible attribute declaration. cute.
load
Loads new data into the hash.
Data can be supplied as hashrefs with normal/local keys or composite keys, or both. Composite keys can also be supplied as sub-keys and are resolved relative to the location in which they appear, as one would expect.
Composite keys can also be supplied as simple strings without corresponding values, in which case their value is set to whatever default_value is set to (which defaults to 1).
See the unit tests for more details and lots of examples of using load().
set
Simpler alternative to load(). Expects exactly two arguments as standard key/values. no more, no less.
resolve_key_path
Converts a composite key string into its full path and returns it as a list. Called internally wherever composite keys are resolved.
path_to_composite_key
Inverse of resolve_key_path; takes a path as a list and returns a single composite key string (i.e. joins using the delimiters for each level). Obviously, it only returns fully-qualified, non-ambiguous (not partial) composite keys.
exists
Returns true if the supplied composite key exists and false if it doesn't. Does not consider default/fallback key paths.
exists_path
Like exists(), but requires the key to be supplied as a resolved/fully-qualified path as a list of arguments. Used internally by exists().
get
Retrieves the real value of the supplied composite key, or undef if it does not exist. Use exists() to distinguish undef values. Does not consider default/fallback key paths (that is what lookup() is for).
get_path
Like get(), but requires the key to be supplied as a resolved/fully-qualified path as a list of arguments. Used internally by get().
lookup
Returns the value of the supplied composite key, falling back to default key paths if it does not exist, depending on the value of lookup_mode.
If the lookup_mode is set to get, lookup() behaves exactly the same as get().
If the lookup_mode is set to fallback and the supplied key does not exist, lookup() will search the hierarchy of matching default key paths, returning the first value that exists.
If the lookup_mode is set to merge, lookup() behaves the same as it does in fallback mode for all non-hashref values. For hashref values, the hierarchy of default key paths is searched and all matches (that are themselves hashrefs), including the exact/lowest value itself, are merged and returned.
lookup_path
Like lookup(), but requires the key to be supplied as a resolved/fully-qualified path as a list of arguments. Used internally by lookup().
lookup_leaf_path
Like lookup_path(), but only returns the value if it is a leaf (i.e. not a hashref with deeper sub-values). Empty hashrefs ({}) are also considered leaf values.
lookup_path_globmatch
Like lookup_path(), but matches/returns by comparing a full composite key string with wildcard (text glob) expressions.
delete
Deletes the supplied composite key and returns the deleted value, or undef if it does not exist. Does not consider default/fallback key paths, or delete multiple items at once (e.g. like the Linux rm command does with shell globs).
delete_path
Like delete(), but requires the key to be supplied as a resolved/fully-qualified path as a list of arguments. Used internally by delete().
Data
Returns a read-only (i.e. cloned) copy of the full loaded hash structure.
num_levels
Returns the number of levels defined for this Hash::Layout instance.
level_keys
Returns a hashref of all the keys that have been loaded/exist for the supplied level index (the first level is at index 0).
def_key_bitmask_strings
Debug method. Returns a list of all the default key paths as a list of bitmasks (in binary/string form). Any key path which has at least one default key at any level is considered a default path and is indexed as a bitmask, with 1 values representing the default key position(s). For instance, the key path {*}{*}{foo_rule} from the 3-level example from the SYNOPSIS is indexed as the bitmask 110 (6 in decimal).
These bitmasks are used internally to efficiently search for and properly order default key values for quick fallback/merge lookups, even when there are a very large number of levels (and thus a very, VERY large number of possible default paths). That is why they are tracked and indexed ahead of time.
This is a debug method which should not be needed to be used for any production code. I decided to leave it in just to help document some of the internal workings of this module.
reset
Clears and removes all loaded data and resets internal key indexes and counters.
π EXAMPLES π
For more examples, see the following:
- The SYNOPSIS
- The unit tests in
t/ DBIx::Class::Schema::Diff#filter
π AUTHOR π
Henry Van Styn <vanstyn@cpan.org>
Β©οΈ COPYRIGHT AND LICENSE Β©οΈ
This software is copyright (c) 2014 by InteliTree Solutions llc.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
That's the whole vibe. This page came from perl v5.42.0, dated 2025-07-25, and it's Hash::Layout(3) β k bye bestie, go slay some composite keys πβ¨π¦
π¦ where this page comes from
- fedora 43: perl-Hash-Layout 2.00-15.fc43 Β· GPL-1.0-or-later OR Artistic-1.0-Perl
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms