String Replacement is the most frequent routine as a programmer. When I updated my routine Perl code from Fedora Core 7 (~2006) to Recky 6.5 (2025), one of the MAGIC REPLACEMENT STRING didn't work (from $MAGIC_FROM_1 to $MAGIC_TO_1, in HTML, as in below). I assumed that the issue is likely Perl syntax problem in my $MAGIC_FROM_1. The replacement of $MAGIC_FROM_2 to $MAGIC_TO_2 works. Would anyone help me ? Multiple files in the $MAGIC_PATH are treated at once.
#! /usr/bin/perl
use strict;
use warnings;
my $MAGIC_PATH = "/home/mkido/bin/SQ_casted_1/db_gr";
# Generic Switch of _MAGIC_EXT1, usually
my $MAGIC_EXT1 = 0;
print "Type html, pl, txt, OR php.\n";
$MAGIC_EXT1 = <STDIN>; # read the next line
chomp ($MAGIC_EXT1);
my $MAGIC_FROM_1 = '<a href=\"http://www.google.com/\?q=';
my $MAGIC_TO_1 = "000_";
my $MAGIC_FROM_2 = "\">YouTube</a>";
my $MAGIC_TO_2 = "_999";
my $filename = "";
my @allfiles = "";
print "\nhello world\n\n";
opendir DIRHANDLER, $MAGIC_PATH;
@allfiles = readdir DIRHANDLER;
foreach $filename (sort @allfiles) {
print "$filename\n";
}
print "\n\n";
print "Now is the business\n";
foreach $filename (sort @allfiles) {
if( $filename =~ /$MAGIC_EXT1/ ){
print "$filename\n";
open (IN, "SQ_casted_1/db_gr/$filename");
open (my $fh, '>', "/home/mkido/bin/SQ_casted_2/db_gr/$filename");
while ( my $line1 = <IN> ){
if( $line1 =~ /http/ ){
(my $newline1 = $line1 ) =~ s/$MAGIC_FROM_1/$MAGIC_TO_1/g;
($newline1 = $line1 ) =~ s/$MAGIC_FROM_2/$MAGIC_TO_2/g;
print $fh $newline1;
}else{ print $fh $line1; }
} ### closing while-loop
close(IN);
close($fh);
} ### closing if_filename_has_html
}
print "\n\n";
Weekly Challenge 378
Each week Mohammad S. Anwar sends out The Weekly Challenge, a chance for all of us to come up with solutions to two weekly tasks. My solutions are written in Python first, and then converted to Perl. Unless otherwise stated, Copilot (and other AI tools) have NOT been used to generate the solution. It's a great way for us all to practice some coding.
Task 1: Second Largest Digit
Task
You are given an alphanumeric string.
Write a script to find the second largest distinct digit in the given string. Return -1 if none found.
My solution
For this task I use a set called digits to store the digits found in the input_string. By using a set, duplicate digits are only counted once. If this set has at least two values (unique digits), I return the second higest number. If it is shorter, I return -1.
def second_largest_digit(input_string) -> int:
digits = set(int(c) for c in input_string if c.isdigit())
return sorted(digits)[-2] if len(digits) > 1 else -1
As Perl does not have sets, I use a hash to achieve the same functionality.
sub main ($input_string) {
my %digits = ();
for my $char ( split //, $input_string ) {
if ( $char =~ /[0-9]/ ) {
$digits{$char} = 1;
}
}
if ( scalar( keys(%digits) ) < 2 ) {
say -1;
}
else {
my @sorted_digits = sort { $a <=> $b } keys %digits;
say $sorted_digits[-2];
}
}
Examples
$ ./ch-1.py aaaaa77777
-1
$ ./ch-1.py abcde
-1
$ ./ch-1.py 9zero8eight7seven9
8
$ ./ch-1.py xyz9876543210
8
$ ./ch-1.py 4abc4def2ghi8jkl2
4
Task 2: Sum of Words
Task
You are given three strings consisting of lower case English letters a to j only. The letter value of a = 0, b = 1, c = 3, etc.
Write a script to find if sum of first two strings return the third string.
My solution
For this task I have a function called words_to_number that converts a word to an integer. It also checks that the word only contains the letters a to j. The ord function gives the integer representing the Unicode code point of that character. The letter a has the integer 97, while j is 106.
def word_to_number(word: str) -> int:
if not re.search('^[a-j]+$', word):
raise ValueError(f"String '{word}' contains invalid characters")
number_string = "".join(str(ord(c) - ord('a')) for c in word)
return int(number_string)
The sum_of_words function simply checks if the math is valid.
def sum_of_words(str1, str2, str3) -> bool:
return word_to_number(str1) + word_to_number(str2) == word_to_number(str3)
The Perl code follows the same logic.
sub word_to_number($str) {
if ( $str !~ /^[a-j]+$/ ) {
die "String '$str' contains invalid characters\n";
}
my $number_string = join( "", map { ord($_) - ord('a') } split //, $str );
return int($number_string);
}
sub main ( $str1, $str2, $str3 ) {
say word_to_number($str1) + word_to_number($str2) == word_to_number($str3)
? 'true'
: 'false';
}
Examples
$ ./ch-2.py acb cba cdb
true
$ ./ch-2.py aab aac ad
true
$ ./ch-2.py bc je jg
false
$ ./ch-2.py a aaaa aa
true
$ ./ch-2.py c d h
false
$ ./ch-2.py gfi hbf bdhd
true
Originally published at Perl Weekly 777
Hi there!
In the recent weeks I looked at a lot of MetaCPAN profiles (aka. author pages) such as that of MANWAR. If I could also find their LinkedIn profile I invited them to connect via LinkedIn. (If I have not sent you an invitation yet, then I guess I missed your profile. I'd be glad to get a connect request via LinkedIn.)
I noticed that a large percentage of the people still have their @cpan.org email address listed. Despite the fact that cpan.org email forwarding has been shut down 6 weeks ago. That means people will get annoyed if hey try to contact you using that address.
You could replace that address or hide it and offer other ways for people to contact you. Either of them is better than having a bad address.
In addition, I noticed that some of the links people have there are not working. (e.g. incorrect link to their LinkedIn profile, or to their home page etc.)
In order to fix these you probably first need to check and update your PAUSE account. After logging in look for the Edit Account Info menu option. There you can list your email address and you can even decide if you'd like to have a visible address or not.
Then you could take a look at your MetaCPAN profile. For this visit MetaCPAN. Login in the top-right corner. If you don't remember whether you used GitHub or Google, don't worry. Inside you can connect them in the Identities menu point. Then go to the Profile menu point and update the fields there.
Finally, if you have updated your profile after reading this, I'd be glad if you sent me an email so I'll know this messaged had some positive impact.
Oh, and if you don't have a CPAN account and you have not uploaded anything yet, then what are you waiting for?
Enjoy your week!
--
Your editor: Gabor Szabo.
Articles
Time::Str - Time Zones and Leap Seconds
Time::Str parses and formats date/time strings across 20+ standard formats, with an optional C/XS backend and nanosecond precision. The previous post, Introducing Time::Str, covered parsing and formatting. This one covers two additions, time zones and leap seconds, and ends with a note on the new C parsers. (Reddit)
Discussion
Confused about a few parts of the new Core OOP
Memory sharing between scripts
First record in implicit -n loop behaves differently than the rest. Hoping to find an explanation.
Grants
PEVANS Core Perl 5: Grant Report for May 2026
Perl
This week in PSC (228) | 2026-06-08
The Weekly Challenge
The Weekly Challenge by Mohammad Sajid Anwar will help you step out of your comfort-zone. You can even win prize money of $50 by participating in the weekly challenge. We pick one champion at the end of the month from among all of the contributors during the month, thanks to the sponsor Marc Perry.
The Weekly Challenge - 378
Welcome to a new week with a couple of fun tasks "Second Largest Digit" and "Sum of Words". If you are new to the weekly challenge then why not join us and have fun every week. For more information, please read the FAQ.
RECAP - The Weekly Challenge - 377
Enjoy a quick recap of last week's contributions by Team PWC dealing with the "Reverse Existence" and "Prefix Suffix" tasks in Perl and Raku. You will find plenty of solutions to keep you busy.
Prefixed Existence
This article offers a great introduction to the Raku language through its implementation of the The Weekly Challenge #377, specifically the use of native Raku functions, such as .flip and .combinations. The code is well-written, typical of Raku, and fully supported with an extensive breakdown of each line of code, detailed run-time logs, and references to helpful documentation.
Perl Weekly Challenge: Week 377
The article compares Perl and Raku solutions for Week 376 of the Challenge and illustrates how the two languages are different in terms of their evolution. Specifically, it illustrates how Raku provides a more modern set of high-level features (e.g., built-in support for substrings and pairs) compared to the equivalent structural features in Perl, which require additional code to implement.
From Start to End and Back
Jorg takes a multi-paradigm approach to solving The Weekly Challenge #377 in both Perl and J (the array-processing language). This article showcases Jorg's technical knowledge because he generalises the first task from its minimum definition and well-organises the second task's input by length to optimise the process. Additionally, Jorg's complete walkthrough of the functional, tacit programming techniques found in language J is well presented, allowing for the easy comprehension of complex manipulations of Arrays and RegEx optimisations.
Perl Weekly Challenge 377
W. Luis Mochan employs the expressive capabilities of the Perl language in order to create solutions that exhibit exceptional brevity with no extra syntax or words. This will definitely capture the interest of developers looking for clean and functional solutions.
They run and hide their heads / They might as well be deadā¦
This post provides a great, multi-lingual breakdown of the challenge with clean idiomatic solutions in Perl, Raku, Python and Elixir. By demonstrating how each of the programming paradigms handle the same logic, Packy provides educational value beyond measure while maintaining interest with a clever Beatles-style music theme.
Strings within strings
Peter offers a refreshingly simple and useful way to accomplish this task using only Perl. He places an emphasis on keeping the code readable, ensuring UTF-8 characters can safely contain more than one byte, and maintaining a high level of performance in real-world applications. There is no ambiguity or sacrifice made in developing an easy-to-interpret solution to a hard problem and accommodating corner cases.
The Weekly Challenge - 377: Reverse Existence
In this article, Reinier discusses how to efficiently leverage Perl's string manipulation and logical checks to confirm if input strings share overlapping character sequences with the least amount of code. This article is an excellent resource for learning to write perl code that efficiently performs substring matching in a clear, idiomatic manner with minimal additional complexity.
The Weekly Challenge - 377: Prefix Suffix
Reinier made use of Perl's built-in string matching capabilities to efficiently find all pairs where one of the strings serves as both the beginning and end of one of the other strings. This showcases a great example of writing efficient, clear code to solve a complex logic problem of substring containment in as little code as possible.
A Suffix to Existence
Roger's article provides an excellent comparison between different programming languages by demonstrating how language paradigms solve the same algorithms for sliding windows and substring matches, thereby providing significant educational value to the reader by making comparisons between high-level built-in functions with manual implementations for the same problem.
Existence
In this post, Simon analysed the issue into smaller pieces/steps made it easier for implementation to remain very readable and maintainable. A strong resource for developers wishing to observe how simple, idiomatic methods can more elegantly achieve string manipulating constraints.
Weekly collections
NICEPERL's lists
Great CPAN modules released last week.
Events
Perl development using AI (online)
June 17, 2026
Berlin.pm - Naumanns Biergarten
June 24, 2026
Toronto.pm - June Social Evening
June 25, 2026
The Perl and Raku Conference 2026
June 26-29, 2026, Greenville, SC, USA
Paris.pm monthly meeting
July 8, 2026
Purdue Perl Mongers (HackLafayette) - TBA
July 08, 2026
Boston Perl Mongers virtual monthly
July 14, 2026
You joined the Perl Weekly to get weekly e-mails about the Perl programming language and related topics.
Want to see more? See the archives of all the issues.
Not yet subscribed to the newsletter? Join us free of charge!
(C) Copyright Gabor Szabo
The articles are copyright the respective authors.
In Perl, is there an nsort function (to sort numerically) available in a mainstream CPAN module?
There is nsort_by in both List::MoreUtils and List::UtilsBy, but no nsort. Sort::Naturally has nsort, but I don't want a ānaturalā sort, but a plain numeric one.
Sure, I can do sort { $a <=> $b } but I'd like to do it in a cleaner way.
The regex itself is functional (it matches the paragraph it finds the ^font\.size = 12\.0$ line in), but I can't figure out how to get a suitable Perl executable command to reproduce the result.
I am currently running
perl -lne 'BEGIN{undef $/;} print while /(^\S.*\n)+^font\.size = 12\.0(\n^\S.*)+/gm' teststring.txt
but it's just printing out the entire file. How can I get Perl to behave like Regex101?
Regex
/(^\S.*$\n)+^font\.size = 12\.0$(\n^\S.*$)+/gm
teststring.txt
# bbox.tolerance = 1e-5
[[heading]]
# Complex Plane
level = 1
greedy = true
font.name = "CoFoRobert-Medium"
font.size = 12.0
# font.size_tolerance = 1e-5
# font.color = 0x333333
# font.superscript = false
# font.italic = false
# font.serif = true
# font.monospace = false
# font.bold = false
# bbox.left = 128.5
# bbox.top = 358.7200012207031
# bbox.right = 208.11978149414062
# bbox.bottom = 373.52801513671875
# bbox.tolerance = 1e-5
[[heading]]
Expected output:
[[heading]]
# Complex Plane
level = 1
greedy = true
font.name = "CoFoRobert-Medium"
font.size = 12.0
# font.size_tolerance = 1e-5
# font.color = 0x333333
# font.superscript = false
# font.italic = false
# font.serif = true
# font.monospace = false
# font.bold = false
# bbox.left = 128.5
# bbox.top = 358.7200012207031
# bbox.right = 208.11978149414062
# bbox.bottom = 373.52801513671875
# bbox.tolerance = 1e-5
Weekly Challenge 377
Each week Mohammad S. Anwar sends out The Weekly Challenge, a chance for all of us to come up with solutions to two weekly tasks. My solutions are written in Python first, and then converted to Perl. Unless otherwise stated, Copilot (and other AI tools) have NOT been used to generate the solution. It's a great way for us all to practice some coding.
Task 1: Reverse Existence
Task
You are given a string.
Write a script to find whether any substring of length 2 is also present in the reverse of the given string.
My solution
This is relatively straight forward. I create a variable reversed_string which is the string reversed. I then have a loop called start_pos that goes from 0 to 2 less than the length of the string. At each position, I check if the two letters at that position are in the original string.
def reverse_existence(input_string: str) -> bool:
reversed_string = input_string[::-1]
for start_pos in range(len(input_string)-1):
if reversed_string[start_pos:start_pos+2] in input_string:
return True
return False
The Perl solution follows the same logic.
sub main ($input_string) {
my $reversed_string = reverse($input_string);
foreach my $start_pos ( 0 .. length($input_string) - 2 ) {
if (
index( $input_string, substr( $reversed_string, $start_pos, 2 ) )
!= -1 )
{
say "true";
return;
}
}
say "false";
}
Examples
$ ./ch-1.py abcba
true
$ ./ch-1.py racecar
true
$ ./ch-1.py abcd
false
$ ./ch-1.py banana
true
$ ./ch-1.py hello
true
Task 2: Prefix Suffix
Task
You are given an array of strings.
Write a script to find if the two strings (str1, str2) in the given array such that str1 is prefix and suffix of str2. Return the total count of such pairs.
My solution
In Python, this is a one line solution. I use the combinations function from the itertools module to produce all combinations of pairs of str1 and str2. For each pair, I use the startwith and endswith function on the strings to see if the criteria is met.
def prefix_suffix(array: list[str]) -> int:
return sum(
1
for str1, str2 in combinations(array, 2)
if (str1.startswith(str2) and str1.endswith(str2)) or
(str2.startswith(str1) and str2.endswith(str1))
)
The Perl solution is a little more complex. I have a function called is_prefix_suffix that checks that s1 starts with an ends with s2.
sub is_prefix_suffix ( $s1, $s2 ) {
my $l = length($s2);
return ( substr( $s1, 0, $l ) eq $s2 and substr( $s1, 0 - $l ) eq $s2 )
? 1
: 0;
}
The main function uses the combinations function from the Algorithm::Combinatorics module to generate all possible pairs. It then calls the above function on each order of the pair to see if it is true.
use Algorithm::Combinatorics 'combinations';
sub main (@array) {
my $count = 0;
my $iter = combinations( \@array, 2 );
while ( my $c = $iter->next ) {
my ( $str1, $str2 ) = @$c;
if ( is_prefix_suffix( $str1, $str2 )
or is_prefix_suffix( $str2, $str1 ) )
{
++$count;
}
}
say $count;
}
Examples
$ ./ch-2.py a aba ababa aa
4
$ ./ch-2.py pa papa ma mama
2
$ ./ch-2.py abao ab
0
$ ./ch-2.py abab abab
1
$ ./ch-2.py ab abab ababab
3
-
App::Ack - A grep-like program for searching source code
- Version: v3.10.0 on 2026-06-07, with 817 votes
- Previous CPAN version: v3.9.0 was released 1 year, 11 days before
- Author: PETDANCE
-
Config::IniFiles - A module for reading .ini-style configuration files.
- Version: 3.001000 on 2026-06-11, with 15 votes
- Previous CPAN version: 3.000003 was released 6 years, 2 months, 17 days before
- Author: SHLOMIF
-
Cpanel::JSON::XS - cPanel fork of JSON::XS, fast and correct serializing
- Version: 4.42 on 2026-06-07, with 47 votes
- Previous CPAN version: 4.41 was released 10 days before
- Author: RURBAN
-
CPANSA::DB - the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit
- Version: 20260607.001 on 2026-06-07, with 25 votes
- Previous CPAN version: 20260531.001 was released 5 days before
- Author: BRIANDFOY
-
Cucumber::TagExpressions - A library for parsing and evaluating cucumber tag expressions (filters)
- Version: 10.0.0 on 2026-06-11, with 18 votes
- Previous CPAN version: 9.1.0 was released 3 months, 21 days before
- Author: CUKEBOT
-
DateTime::Format::Natural - Parse informal natural language date/time strings
- Version: 1.27 on 2026-06-07, with 19 votes
- Previous CPAN version: 1.26_02 was released 5 days before
- Author: SCHUBIGER
-
GD - Perl interface to the libgd graphics library
- Version: 2.86 on 2026-06-09, with 32 votes
- Previous CPAN version: 2.85 was released 7 days before
- Author: RURBAN
-
HTTP::Tiny - A small, simple, correct HTTP/1.1 client
- Version: 0.096 on 2026-06-08, with 116 votes
- Previous CPAN version: 0.095 was released 4 days before
- Author: HAARG
-
JSON::Validator - Validate data against a JSON schema
- Version: 5.19 on 2026-06-10, with 59 votes
- Previous CPAN version: 5.18 was released 1 day before
- Author: JHTHORSEN
-
Mail::DMARC - Perl implementation of DMARC
- Version: 1.20260612 on 2026-06-12, with 38 votes
- Previous CPAN version: 1.20260306 was released 3 months, 6 days before
- Author: MSIMERSON
-
Net::DNS - Perl Interface to the Domain Name System
- Version: 1.55 on 2026-06-11, with 29 votes
- Previous CPAN version: 1.54_02 was released 3 days before
- Author: NLNETLABS
-
Number::Phone - base class for Number::Phone::* modules
- Version: 4.0011 on 2026-06-10, with 24 votes
- Previous CPAN version: 4.0010 was released 3 months, 4 days before
- Author: DCANTRELL
-
Protocol::HTTP2 - HTTP/2 protocol implementation (RFC 7540)
- Version: 1.13 on 2026-06-07, with 27 votes
- Previous CPAN version: 1.12 was released 3 months, 20 days before
- Author: CRUX
-
SPVM - The SPVM Language
- Version: 0.990189 on 2026-06-12, with 36 votes
- Previous CPAN version: 0.990188 was released 1 day before
- Author: KIMOTO
-
Test::Simple - Basic utilities for writing tests.
- Version: 1.302220 on 2026-06-09, with 200 votes
- Previous CPAN version: 1.302219 was released 6 months before
- Author: EXODIST
-
WWW::Mechanize - Handy web browsing in a Perl object
- Version: 2.21 on 2026-06-13, with 104 votes
- Previous CPAN version: 2.20 was released 7 months, 21 days before
- Author: OALDERS
-
WWW::Mechanize::Chrome - automate the Chrome browser
- Version: 0.79 on 2026-06-08, with 22 votes
- Previous CPAN version: 0.78 was released the same day
- Author: CORION
This blog post is a follow up on my previous post, Introducing Time::Str, covered parsing and formatting. This one covers two additions, time zones and leap seconds, and ends with a note on the new C parsers.
If you have any questions or feedback, feel free to leave them here or on the blog, and I'll be happy to respond.
[link] [comments]

Paul writes:
May is always a quiet month for me because I my big stage event to look after, but I got a few things done:
- 4 = Experimentation in Object::Pad's version of how to fix the "late added fields" bug
- https://github.com/Perl/perl5/issues/24393
- 1 = Fix a couple of integer overflow bugs relating to security reports
- 3 = Begin work on the
av_spliceAPI- https://github.com/Perl/perl5/pull/24451
Total: 8 hours
June's focus is getting 5.44 out the door, and lining up the various features in design or development to start the next cycle.
I'm actually somewhat enthused over the new Core OOP additions over the last few releases. I didn't think the create an object (usually a hash) and bless it was all that hard, but felt a little ... clunky compared to other languages. The newer syntax and encapsulation behavior is a welcome addition to the language. To make sure I really understood it, I tried a few examples. Most was pretty easy, until I got inheritance and the train went off the rails in that it stopped being DWIM suddenly. IMO, the docs seem a little lacking but I was able to start piecing the knowledge together with lots of 'net searching and a small example -- mostly.
use v5.42; use feature 'class'; no warnings 'experimental::class'; class Animal { # Define the parent class field $name :param :reader :writer; method speak() { say "$name makes a sound."; } my method secret() { say __CLASS__ . " secret is 42"; } # only callable inside the class method cheat() { $self->&secret; } } class Dog :isa(Animal) { # Define the child class field $breed :param; method speak { # Override or extend functionality print "override: "; $self->SUPER::speak(); #say "$name (the $breed) says: Woof!"; ## <<-- EXPECTED # Global symbol "$name" requires explicit package name #say "$SUPER::name (the $breed) says: Woof!"; ## <<-- UNDERSTAND THIS MIGHT BE NEEDED # Use of uninitialized value $SUPER::name #say $self->SUPER::name, " (the $breed) says: Woof!"; #method call, so same as below say $self->name(), " (the $breed) says: Bark!"; # works! WTH?! } method rename($newname) { say "renaming from " . $self->name() . " to $newname"; #$name = $newname; ## <<-- EXPECTED # Global symbol "$name" requires explicit package name #$SUPER::name = $newname; ## <<-- UNDERSTAND THIS MIGHT BE NEEDED #no error but also doesn't work # go add ":writer" to parent class $self->set_name($newname); # works! WTH?! } } # Main my $buddy = Dog->new( name => "Buddy", breed => "Golden Retriever" ); $buddy->speak(); $buddy->rename("buddyboy"); $buddy->speak(); my $anna = Animal->new( name => "Anna" ); $anna->speak(); #$anna->&secret(); # ??? Undefined subroutine &main::secret called; cannot do this! $anna->cheat(); I think that exercises most everything. After getting stuck, I went back to reread perlclass several times and finally spotted "lexical" hiding in a couple of paragraphs...which was really important. [Hint: I think that needs to be highlighted more, as well as adding some more example in the doc.]
The heart of the my problem is that there seems to be no "protected" scoping for variable (fields). Why not? [See the "EXPECTED" in the example.]
This is more obvious with a small chart:
What we have in 5.42.2: class Base { method m1 { } # scope: public [& protected] my method m2 { } # scope: private # do not want # scope: public # not available # scope: protected field f1; # scope: private }; Why isn't it: class Base { method m1 { } # scope: public [& protected] my method m2 { } # scope: private # do not want # scope: public field f1; # scope: protected my field f1; # scope: private }; That would allow a derived class to use the base class's vars easily and with less typing, no calling a function/method to get or set. Or did I miss something in my reading and there is a way to have "protected vars"? Or am I being impatient and it's coming in 5.44 or 5.46 and I just need to hold off on this until then?
[link] [comments]
Time::Str parses and formats date/time strings across 20+ standard formats, with an optional C/XS backend and nanosecond precision. The previous post, Introducing Time::Str, covered parsing and formatting. This one covers two additions, time zones and leap seconds, and ends with a note on the new C parsers.
Time Zones
Time::Str has understood the offset in a timestamp (+01:00,
Z), but it could not turn a zone name, or a local time with no offset,
into a precise instant. Two new modules add that:
- Time::Str::TimeZone resolves names to objects and caches them.
- Time::TZif and Time::TZif::POSIX compute the UTC offset of a zone at a given instant.
Example
use Time::Str::TimeZone qw(timezone);
use Time::Str qw(str2time time2str);
$str = 'Dec. 24, 2012 12:30 p.m.';
$tz = timezone('America/New_York');
$time = str2time($str, format => 'DateTime', timezone => $tz);
say time2str($time, timezone => $tz);
# 2012-12-24T12:30:00-05:00
The input string has no offset. The timezone object resolves the local
wall-clock time to UTC (New York in December is EST, -05:00), and
time2str renders the result back in the same zone. The result is
DST-aware, so a July date prints -04:00.
Resolving abbreviations
The first post showed that Time::Str captures abbreviations such as
IST in tz_abbrev without deciding what they mean, since one
abbreviation can name several zones. A timezone_map supplies the
meaning for your data:
$map = {
EST => timezone('America/New_York'),
EDT => timezone('America/New_York'),
};
$str = 'Dec. 24, 2012 12:30 p.m. EST';
$time = str2time($str, format => 'DateTime', timezone_map => $map);
The abbreviation selects an object, and the object determines the offset for that instant.
Time::TZif and Time::TZif::POSIX
Time::TZif reads compiled TZif files (RFC 8536), the binary zoneinfo
the operating system ships. Time::TZif::POSIX evaluates a POSIX TZ
rule string (IEEE Std 1003.1), for zones expressed as rules such as
EST5EDT,M3.2.0,M11.1.0:
use Time::TZif;
$tz = Time::TZif->new(
path => "$dir/America/New_York",
name => 'America/New_York',
);
$off = $tz->offset_for_utc($epoch); # seconds east
use Time::TZif::POSIX;
$tz = Time::TZif::POSIX->new(
tz_string => 'EST5EDT,M3.2.0,M11.1.0',
);
Both provide the same two methods, offset_for_utc and
offset_for_local, which str2time and time2str use. Either object
can be passed as timezone =>.
Implementation notes
TZhandling follows POSIX. It honors theTZenvironment variable: a database name, a POSIX rule, or the system default via/etc/localtime.- It uses the system IANA database. OS vendors keep the tzdb current, so on a current POSIX system there is nothing to bundle or upgrade separately.
- Maintenance. DateTime::TimeZone ships its own copy of the tzdb and is re-released when the rules change; Time::TZif reads the system copy instead.
- Memory. For
America/New_York, Time::TZif uses 17,765 bytes against 87,174 for DateTime::TimeZone. - Offset lookup speed is shown below.
- Runtime reload. Clearing the cache (
Time::Str::TimeZone->reset) lets a long-running process pick up an updated database or a changedTZwithout restarting.
Choosing how local times resolve
A spring-forward gap and a fall-back overlap have no single answer: one wall-clock time does not exist, another occurs twice. Time::TZif resolves these by policy.
Defaults are set on the constructor and apply to every lookup the object makes:
$tz = Time::TZif->new(
path => $path,
name => 'America/New_York',
gap_policy => 'later', # spring-forward
overlap_policy => 'earlier', # fall-back
);
Either default can be overridden for a single call, by passing the
policy to offset_for_local:
# resolve this overlap as daylight time, this once
$off = $tz->offset_for_local($time, overlap_policy => 'dst');
The five values are reject (the constructor default, which croaks on
an impossible or ambiguous local time), earlier and later (pick by
clock order), and std and dst (pick the standard-time or
daylight-time side). A local time then resolves the way your libc or
DateTime.pm would, per object or per call.
Benchmarks
Memory held for America/New_York:
bytes
Time::TZif 17765
DateTime::TimeZone 87174
UTC offset lookup:
Rate DateTime::Lite DateTime Time::TZif
DateTime::Lite 64306/s -- -91% -99%
DateTime 709978/s 1004% -- -86%
Time::TZif 5093119/s 7820% 617% --
Time::TZif is roughly 7x DateTime on this lookup.
Limitation: Windows
The current catalog targets POSIX systems, where the zoneinfo files and
TZ semantics are standard. A Windows fallback is planned on top of
Time::OlsonTZ::Data,
originally by the late Zefram and now maintained by Dan Book, which
packages the Olson database for platforms that do not ship it.
Leap Seconds
When I asked for feedback on IRC, Karen Etheridge ran her JSON Schema
tests against str2time. It accepted any timestamp whose seconds field
was 60.
A leap second can appear only as 23:59:60 UTC, and only on June 30 or
December 31. 12:30:60 is not a leap second.
str2time now checks this without a table:
str2time('2016-12-31T23:59:60Z');
# ok: folded onto 23:59:59
str2time('2016-12-31T12:30:60Z');
# croaks: "Unable to convert: a leap second must occur at 23:59:60 UTC"
str2time('2016-06-15T23:59:60Z');
# croaks: "Unable to convert: no leap second on this UTC date"
POSIX time cannot represent a 61st second, so str2time folds
23:59:60 onto the preceding 23:59:59, then confirms the UTC instant
is the last second of June 30 or December 31 (since 1972). The check
runs after the offset is applied, so it also holds for non-UTC offsets:
str2time('2017-01-01T05:29:60+05:30'); # ok
# the same instant as 2016-12-31 23:59:60 UTC
The check is table-free: it accepts every June 30 and December 31 slot, whether or not a leap second was inserted that day.
Time::LeapSecond
That gap led to
Time::LeapSecond, which
knows the leap seconds that were actually scheduled. It reads the system
data file (TZDB leap-seconds or IERS leap-seconds.list), falls back
to a built-in table, and exposes the instants, the TAI-UTC offset, and
the file's expiration $EXPIRES (the POSIX epoch the data is complete
until).
The plan is to use it in str2time: while time() is before
$EXPIRES the table is authoritative, so an unlisted :60 is rejected;
once past it the file may be stale, so str2time falls back to the
table-free check and accepts any valid slot. Better to accept a wrong
leap second than reject a real one.
Native C parsers
An update on the first post's "what's next": several parsers are now
generated C, built with Ragel,
which removes the regex step from the parse path. Parsing
2012-12-24T12:30:45.123456+01:00:
Rate DT8601 DT3339 D::Parse T::Moment T::Str
DT::F::ISO8601 23442/s -- -50% -78% -100% -100%
DT::F::RFC3339 46804/s 100% -- -56% -99% -100%
D::Parse 105263/s 349% 125% -- -99% -99%
T::Moment 7030938/s 29893% 14922% 6579% -- -36%
T::Str 11020543/s 46912% 23446% 10369% 57% --
On this input Time::Str is about 57% faster than my other module Time::Moment.
Time::Str, Time::TZif, Time::TZif::POSIX, Time::Str::TimeZone and Time::LeapSecond are on CPAN and GitHub.
cpanm Time::Str
Hello there.
I have been using perl for a good 15 years now and stumbled upon something today I could not find a good solution and would like some input.
This is for a hobby, not work related.
I like running an offline game server and a few AI players (bots) in it and develop them over time, this project has bern going for about 5-6 years, the game being ragnarok and the bot being openkore, a standalone perl script that logs into the game and plays it.
I have recently delved into making the bots share decision making, for that I used socket based communication between them using IO:Socket, then I made a central standalone script that receives updates from the bots via sockets and decides the next state of the group, then it sends back this decision to each individual bot.
This runs in a loop, with each bot sending updates every second and the decisions being made every 10 seconds.
That worked very well until I increased the number of agents above 25-30, then the number of socket reads started being too much for my script, since each bot reads the update from every other bot the number of reads per second scales to N squared.
I could just run many separate groups of 20 bots each but I would like to approach it in a way to avoid that.
I would like input in a good to go about this.
I tried looking into IPC::Shareable to maybe create a shared %state variable in memory to which the controller could write and each bot read, and likewise a %info one where bots could update their info and the controller read them all but it only works in unix and use this system in windows.
The bot project is this: https://github.com/OpenKore/openkore
But I run a very customized fork not the one from the repository.
I have a few videos in youtube about the development of said bot in: https://youtube.com/@henrybk5644?si=O68e4xIzHLURepXj
So what do you guys think would be a good idea to increase the scalability of this system or reduce socket communication overhead?
I thought maybe C++ memory sharing over perlXS but I never looked into that.
Thanks.
[link] [comments]
We were all present.
- 5.43.11 has so far turned up a few problems, thankfully small. As a result there are new versions of Archive::Tar and HTTP::Tiny to sync, which we intend to merge.
- We decided how to proceed with our schedule: we will wait another week for any other findings in 5.43.11, and if nothing else shows up then we intend to start working on 5.44.0-RC0 at that point.
- We discussed briefly the trajectory of the core teamās LLM policy conversation. For now we continue keeping an eye on the thread.
- We noted PRs to add two new documents to the PPCs repository but didnāt have time to discuss them in this meeting. We intend to do so in the next one.
edit: SOLVED. Had to change $xCt++ to ++$xCt
System Info:
% perl -version This is perl 5, version 34, subversion 1 (v5.34.1) built for darwin-thread-multi-2level (with 2 registered patches, see perl -V for more detail) % system_profiler SPSoftwareDataType | grep 'System Version' System Version: macOS 15.5 (24F74) I have a list of files that I'm feeding into perl from the command line and using regex to extract info (a date), create a folder, and then move the file into that folder. Anything that doesn't get matched in the regex gets dropped into a separate folder "__OTHER" to be looked at.
The issue is that the FIRST instance of a file that should be placed into the __OTHER directory ALSO creates a directory (empty) matching the filename. The rest behave as expected.
Script:
#! /usr/bin/perl -wnl use File::Copy; BEGIN { $file=""; $extraDir="__OTHER"; $xCt=0; $moved=0; mkdir $extraDir; } $file="$_"; $moved=0; (s/^Test_(\d{4}_\d{2}_\d{2}).+/$1 - / or move("$file","$extraDir/$file") and $xCt++ and $moved=1); $moved == 0 and mkdir; move("$file","$_/$file"); END{ print "$. files processed; $xCt in $extraDir"; } Sample File Names I'm using:
1f7edcd5-b0cb-4fb0-892f-38a324454eb9.jpg 2b3aa5b9-d80c-4f43-a370-82f4e14dac64.jpg 3f69d564-7c93-4105-ad2f-66678523693c.jpg Test_2025_01_01_02_59_59_0a876bef-c475-48bb-968c-39373dcd36b0.jpg Test_2025_01_01_02_59_59_2a336499-b914-4105-9eca-a9c92c4fe4b6.jpg Test_2025_01_01_02_59_59_5cfd88c1-67b8-4aa4-a785-d528de3f254b.jpg Test_2025_01_02_02_59_59_2ec971d9-915a-4966-8306-e611fa0c455a.jpg Test_2025_01_02_02_59_59_4a9951ab-c7ac-4974-a1e2-735ca33e7e61.jpg Test_2025_01_02_02_59_59_4bb85bcb-f760-4680-a2d1-8940cba9d5f3.jpg Test_2025_01_02_02_59_59_5c1dc5fa-a5fc-49f2-a434-20931649178a.jpg Test_2025_01_21_02_59_59_cc8b1938-b9f7-4ccc-bd59-8d452f78eb4c.jpg Test_2025_01_21_02_59_59_d5a5b732-d38e-468c-9f22-08ef90a7b5a5.jpg Test_2025_01_21_02_59_59_d87482dd-ac2f-4621-9029-e6612d7331bd.jpg Test_2025_01_21_02_59_59_d972e346-b707-46a5-9a98-ef0195d8d246.jpg Test_2025_01_21_02_59_59_dd5dac68-57ea-4389-a256-fd88c67ede9e.jpg Test_2025_01_21_02_59_59_e506e2f7-aa98-4b4e-9eae-9cac9126c790.jpg Test_2025_01_21_02_59_59_f5b80b75-43c1-4638-aad6-79a1ca686560.jpg And the result:
% find . -exec file -- {} + .: directory ./1f7edcd5-b0cb-4fb0-892f-38a324454eb9.jpg: directory ./2025_01_02 - : directory ./2025_01_02 - /Test_2025_01_02_02_59_59_2ec971d9-915a-4966-8306-e611fa0c455a.jpg: empty ./2025_01_02 - /Test_2025_01_02_02_59_59_4bb85bcb-f760-4680-a2d1-8940cba9d5f3.jpg: empty ./2025_01_02 - /Test_2025_01_02_02_59_59_5c1dc5fa-a5fc-49f2-a434-20931649178a.jpg: empty ./2025_01_02 - /Test_2025_01_02_02_59_59_4a9951ab-c7ac-4974-a1e2-735ca33e7e61.jpg: empty ./2025_01_01 - : directory ./2025_01_01 - /Test_2025_01_01_02_59_59_2a336499-b914-4105-9eca-a9c92c4fe4b6.jpg: empty ./2025_01_01 - /Test_2025_01_01_02_59_59_5cfd88c1-67b8-4aa4-a785-d528de3f254b.jpg: empty ./2025_01_01 - /Test_2025_01_01_02_59_59_0a876bef-c475-48bb-968c-39373dcd36b0.jpg: empty ./2025_01_21 - : directory ./2025_01_21 - /Test_2025_01_21_02_59_59_dd5dac68-57ea-4389-a256-fd88c67ede9e.jpg: empty ./2025_01_21 - /Test_2025_01_21_02_59_59_d87482dd-ac2f-4621-9029-e6612d7331bd.jpg: empty ./2025_01_21 - /Test_2025_01_21_02_59_59_f5b80b75-43c1-4638-aad6-79a1ca686560.jpg: empty ./2025_01_21 - /Test_2025_01_21_02_59_59_e506e2f7-aa98-4b4e-9eae-9cac9126c790.jpg: empty ./2025_01_21 - /Test_2025_01_21_02_59_59_cc8b1938-b9f7-4ccc-bd59-8d452f78eb4c.jpg: empty ./2025_01_21 - /Test_2025_01_21_02_59_59_d972e346-b707-46a5-9a98-ef0195d8d246.jpg: empty ./2025_01_21 - /Test_2025_01_21_02_59_59_d5a5b732-d38e-468c-9f22-08ef90a7b5a5.jpg: empty ./__OTHER: directory ./__OTHER/1f7edcd5-b0cb-4fb0-892f-38a324454eb9.jpg: empty ./__OTHER/2b3aa5b9-d80c-4f43-a370-82f4e14dac64.jpg: empty ./__OTHER/3f69d564-7c93-4105-ad2f-66678523693c.jpg: empty I can always manually fix one file, but I'm just confused why the first one does this? Not sure if it's the first file it's encountering at all based on how it's sorting? Or why that would matter?
VERY new to Perl overall, obviously, so it's probably something silly.
TIA.
[link] [comments]
cpan/Archive-Tar - Update to version 3.12 3.12 02/06/2026 (Stig Palmquist) - Allow '..' links in secure extract in parent path for symlinks and hardlinks
inline.h: fix missing C before < > in POD
Originally published at Perl Weekly 776
Hi there,
Recently, I came across an article, The Day I Decided Never to Learn Python by Randal L. Schwartz. Well, Randal doesn't need an introduction. He took us back to 2001, the same era when I first started learning Perl in 1999. He was a major guiding force during my early programming days.
Last week, I joined a live session by Gabor focussed on FalkorDB. It was fun watching him code and talk while I sat back as a silent spectator. You can learn a lot just by watching how he approaches coding. It reminded me of many years ago when I did pair programming with him and submitted a pull request to the Dancer2 project. Those were the golden days, when I had so much energy and time. That being said, I am still actively learning Perl and discovering how to do new things with it. These concepts may not be new to everyone, but they are new to me. For example, I recently played with GraphQL for the first time, and I've also been experimenting with RAG and JSON-RPC. I have shared my recent experiments down below.
The process of learning never stops. A few days ago, I noticed an update for HTTP::Message v7.02. Since it was released by Olaf Alders, I was curious to see what had changed. It turned to be something, I hadn't realised for all these years. While I am well-acquainted with HTTP methods like GET, POST, and PUT, I didn't know "0" could actually be a valid HTTP method name if you wanted it to be. This release added support for exactly that, thanks to contributor, Karen Etheridge. Amidst all of this, I am still trying to find time for my upcoming book on DBIx::Class. I recently shared a blog post demonstrating the power of DBIC components, and I am trying my best not to lose focus.
You might find that this edition is full of my own personal posts, as there was unfortunately very little community news to report this week. Regardless, I hope you enjoy the rest of the newsletter.
--
Your editor: Mohammad Sajid Anwar.
Announcements
This week in PSC (227) | 2026-06-01
This week we were back to full strength. We have now dealt with all of the belated issues and all of the blockers. Paul will be shipping 5.43.11 very shortly. With the amount of changes we have had to merge, we will not be able to rush the .11 cycle, but we intend to start the work on the 5.44 RC early, to ensure that we can release with as little additional delay as possible.
Articles
Result Class - Components
Continue with the series post on DBIx::Class, in this post I am showing the power of DBIC components dealing with date/timestamp column.
RAG with Perl
In this post, I try to decode what RAG is and how we can implement RAG engine in Perl talking to LLM and Vector database in a docker container.
CPAN Debian Package
First time managed to build a debian package for DBIx::Class::Async with step by step instructions.
JSON-RPC with Perl
After dealing with gRPC, next in line was JSON-RPC. The motivation was to explore MCP after getting to know JSON-RPC. It turned out to be too easy demonstrate.
Mojo meets GraphQL
The post is meant for beginners as I am exploring GraphQL for the first time as well. The best part is the use of GraphiQL for building and testing the API.
DBIx::Class and GraphQL
In this post, I shared how to integrate DBIx::Class with GraphQL. Having played with GraphQL, I can say it is not for weak heart. If you are coming from REST API background then it will take some time to get your head around it. I am telling this from my personal exprience.
CPAN and GraphQL
After DBIx::Class integration with GraphQL, my immediate task was to make the process easy as it doesn't have to this complicated. In the process, I released brand new CPAN distribution: DBIx::Class::Schema::GraphQL.
CPAN
FalkorDB
Perl client module for FalkorDB. This code was mostly generated using AI tools and has not been checked manually yet.
DBIx::Class::Schema::GraphQL
It was created while working on the DBIx::Class integration with GraphQL. Using this module, it is so easy to build GraphQL API.
Grants
Maintaining Perl (Tony Cook) May 2026
Maintaining Perl 5 Core (Dave Mitchell): May 2026
The Weekly Challenge
The Weekly Challenge by Mohammad Sajid Anwar will help you step out of your comfort-zone. You can even win prize money of $50 by participating in the weekly challenge. We pick one champion at the end of the month from among all of the contributors during the month, thanks to the sponsor Marc Perry.
The Weekly Challenge - 377
Welcome to a new week with a couple of fun tasks "Reverse Existence" and "Prefix Suffix". If you are new to the weekly challenge then why not join us and have fun every week. For more information, please read the FAQ.
RECAP - The Weekly Challenge - 376
Enjoy a quick recap of last week's contributions by Team PWC dealing with the "Chessboard Squares" and "Doubled Words" tasks in Perl and Raku. You will find plenty of solutions to keep you busy.
Doubled Chessboard
The post offers an orderly, aesthetically pleasing way of solving the Weekly Challenge problems using the modern-day features of Raku syntax. With the use of well-defined subset custom types to strongly validate input and a simple, tokenising object-oriented class structure for text processing, Arne creates a solution that is easy to read and maintain and takes care of the complex edge cases such as applying a look behind that crosses over multiple lines.
Doubled Words
Bob presents a creative solution to a challenging text processing issue by using a well known yet little used built-in feature of the Perl. The result is an elegant mechanism for bypassing tokens through a clearly documented and separated mechanism of backtracking while retaining context about the structure through the use of captured groups in the split regex.
Chess Solo
Jorg provides a very concise and mathematically elegant solution for the colorisation of a chessboard. By recognising that under mod 2 operations the coordinate bases are completely annihilated, implementations can be created in both Perl and J using only one line of code and they are both efficient and structurally minimalist.
The Weekly Challenge 376
An unusually clear yet detailed solution is provided in this blog post, as it combines clean, idiomatic code examples with short technical explanations about how to use regex to parse doubled words and how to compare two mathematical coordinates. Complex logic is presented so anyone can read easily and learn from.
Perl Weekly Challenge 376
The excellent demonstration of the capabilities of Perl as a function-based programming language is shown through this post with extremely short and idiomatic solutions. Luis MochƔn has used expressive, single-line statements to convert complicated constraints into small, clean, logical data flows in an extraordinary manner with Perl.
A 1-Bit Chessboard and Fancy Separators
Matthias presents an analytical and well-defined solution to the difficulties presented by Challenge 376 through the use of well-separated parsing and formatting, clear comments on the use of regular expressions, and the submission of a clear and clean solution suitable for production.
You might think Iām crazy, but I donāt even care
A well-structured and very interesting review of Perl Weekly Challenge 376 is presented with a reference to principles from a popular culture item. Packy clearly illustrates the elegant way to process text in the real world while exhibiting outstanding technical expertise by explaining the complexity of lookahead logic and the constraints of the boundary in a way that allows for simple, idiomatic, and highly maintainable solutions.
Squares and pairs
This solution offers a very neat, simple, and mathematically pleasing method for solving the chessboard parity problem. Peter has effectively utilised the ASCII values (ord) of the column letters as well as the numeric row values to reduce a spatial coordinate problem to a quick and efficient calculation of a bitwise parity (& 1). The source code is formatted well, clearly commented, and takes advantage of the mathematics behind the solution so as to avoid unnecessary conditional barriers.
The Weekly Challenge - 376: Chessboard Squares
This post gives a very clear, well-organized, and visually intuitive representation of how to solve the programming challenge "Chessboard Squares". Explicitly laying out the grid coordinates with their appropriate color will provide an excellent foundation for developing an accurate and efficient lookup algorithm.
The Weekly Challenge #376
By using an elegant mathematical approach to the parity of squares on a chessboard, along with an incredibly efficient, single-pass regular expression, this code shows immense technical skill. The code also uses multi-line lookaheads, HTML tags, and back references, all handled by a simple subroutine (double_double_toil_and_trouble), that is suitable for use at production level.
Half a Chessboard
A general description of a previously completed task, this post shows a real-life application of code reuse. Roger created a nice, modular solution to decode coordinate mathematics (on the basis of (x+y) mod 2) by cleanly wrapping the existing helper function from the previous task. Therefore, through this implementation, he provides a concise representation of the solution and eliminates any duplicate logic.
Double chessboard
In this blog post, Simon's clever use of the bitwise XOR operator (^) as a parity check for a chess board is very creative. Additionally, he shows a well-organised and simple method of utilising re.split and list comprehensions to find the position of words while also overcoming some challenging lookahead and HTML boundary situations.
Rakudo
2026.22 Some AST Fruit
Weekly collections
NICEPERL's lists
Great CPAN modules released last week.
The corner of Gabor
A couple of entries sneaked in by Gabor.
FalkorDB client in Perl using CLI AI Agents
This is the first hour of the live coding session using AI to write Perl we had last week. (Free, registration required)
FalkorDB using Perl
This is the second hour of the live coding session using AI to write Perl we had last week. (Free, registration required)
Next: Perl development using AI
The next session in which each participant will have the opportunity to show how s/he is using AI to write Perl code. I am sure we will learn a lot from each other!
Growing Pains: Scaling AI Coding across Team and Repo Boundaries with Graham Knapp
Video recordings from the session yesterday. It is not Perl specific, but helps you understand things. For more such event, sign up to the Code Mavens Meetup group
Events
Boston Perl Mongers virtual monthly
June 9, 2026
Paris.pm monthly meeting
June 10, 2026
Purdue Perl Mongers (HackLafayette) - TBA
June 11, 2026
Perl development using AI (online)
June 17, 2026
Berlin.pm - Naumanns Biergarten
June 24, 2026
Toronto.pm - June Social Evening
June 25, 2026
The Perl and Raku Conference 2026
June 26-29, 2026, Greenville, SC, USA
You joined the Perl Weekly to get weekly e-mails about the Perl programming language and related topics.
Want to see more? See the archives of all the issues.
Not yet subscribed to the newsletter? Join us free of charge!
(C) Copyright Gabor Szabo
The articles are copyright the respective authors.
perlclass.pod: Update KNOWN BUGS section
Weekly Challenge 376
Each week Mohammad S. Anwar sends out The Weekly Challenge, a chance for all of us to come up with solutions to two weekly tasks. My solutions are written in Python first, and then converted to Perl. Unless otherwise stated, Copilot (and other AI tools) have NOT been used to generate the solution. It's a great way for us all to practice some coding.
Task 1: Chessboard Squares
Task
You are given two coordinates of a square on 8x8 chessboard.
Write a script to find the given two coordinates have the same colour.
My solution
This - like most first tasks - is relatively straight forward. I have a function called is_black and it takes a cell variable as a parameter.
After checking the square is valid (first letter a-g, second number 1-8), it then uses the exclusive xor operator ^ to determine if a square is black.
import re
def is_black(cell: str) -> bool:
if not re.search('^[a-h][1-8]$', cell):
raise ValueError(f"Cell {cell} is not valid!")
return bool((cell[0]in "aceg") ^ (int(cell[1]) % 2))
The same_color function simply checks if the two specified squares are the same.
def same_color(c1: str, c2: str) -> bool:
return is_black(c1) == is_black(c2)
The Perl solution follows the same logic.
sub is_black($cell) {
if ( $cell !~ /^[a-h][1-8]$/ ) {
die "Cell '$cell' is not valid!\n";
}
return ( index( "aceg", substr( $cell, 0, 1 ) ) == -1 ? 0 : 1 )
^ ( substr( $cell, 1, 1 ) % 2 );
}
sub main ( $c1, $c2 ) {
say is_black($c1) == is_black($c2) ? 'true' : 'false';
}
Examples
$ ./ch-1.py a7 f4
true
$ ./ch-1.py c1 e8
false
$ ./ch-1.py b5 h2
false
$ ./ch-1.py f3 h1
true
$ ./ch-1.py a1 g7
true
Task 2: Doubled Words
Task
You are given a string (which may contain embedded newlines) which is taken from a page on a website. The string will not contain brackets qw{ [ ] }.
Write a script that will find doubled words (such as "this this") and highlight (wrap in brackets) each doubled word.
The script should:
- Work across lines, even finding situations where a word at the end of one line is repeated at the beginning of the next.
- Find doubled words despite capitalization differences, such as with 'The the...', as well as allow differing amounts of white-space (spaces, tabs, newlines, and the like) to lie between the words.
- Find doubled words even when separated by HTML tags. For example, to make a word bold: '...it is very very important...'. Only show lines containing doubled words.
My solution
For input from the command line I replace a literal \n (i.e. a backslash followed by the letter 'n') with a new line character. The output replaces new lines with a literal \n. This ensures that I can copy and paste the examples. The Python test suite calls the function directly, so does not need this functionality.
Wow. This one is really tricky. To me an English word is some letters, optionally with a hyphen or apostrophe in the middle (e.g. one-sided or don't). Annoyingly apostrophes can also be used before or after a word to indicate a quote.
My first attempt at solving this was so bad, I went back to the drawing board for my second attempt. I have a solution that words (all five tests pass), but I'm sure that there is a more efficient solution.
I start this task by separating HTML (regular expression <[^>]+>, an open angle bracket, multiple characters that aren't a close angle bracket, and a close angle bracket) and words (regular expression [a-z0-9]+(?:['-][a-z0-9]+)*, letters optionally with hyphen or apostrophe in the middle) from other content (white space, punctuation, etc). As the first parameter to re.split is surround by parentheses, the words and HTML are also returned.
import re
def doubled_words(input_string: str) -> str:
regexp = r"(<[^>]+>|[a-z]+(?:['-][a-z]+)*)"
words_and_html = re.split(regexp, input_string, flags=re.IGNORECASE)
The next step is to find the position of all words only. This uses list comprehension to exclude even (0-based) elements (white space and punctuation) and HTML elements.
parts = [
i
for i, word in enumerate(parts)
if i % 2 == 1 and word[0] != "<" and word[-1] != ">"
]
Next I go through the word_positions list and replace words that are the same as the next word (case insensitive). It should be noted that triple words will only have the first two occurrences replaced.
for pos in range(len(word_positions)-1):
this_pos = word_positions[pos]
next_pos = word_positions[pos+1]
if parts[this_pos].lower() == parts[next_pos].lower():
parts[this_pos] = f"[{parts[this_pos]}]"
parts[next_pos] = f"[{parts[next_pos]}]"
With that done, I then separate the original input and new parts into two lists separated by new lines.
input_lines = re.split(r"[\r\n]+", input_string)
output_lines = re.split(r"[\r\n]+", "".join(parts))
Finally, I find the lines that are different (using list comprehension again) and return only the lines that are different, separated by new lines.
different_lines = [
output
for i, output in enumerate(output_lines)
if output != input_lines[i]
]
return "\n".join(different_lines)
The Perl solution follows the same logic, and uses map and grep to mimic list comprehension.
Examples
$ ./ch-2.py "you're given the job of checking the pages on a\nweb server for doubled words (such as 'this this'), a common problem\nwith documents subject to heavy editing."
"web server for doubled words (such as '[this] [this]'), a common problem"
$ ./ch-2.py "Find doubled words despite capitalization differences, such as with 'The\nthe...', as well as allow differing amounts of whitespace (spaces,\ntabs, newlines, and the like) to lie between the words."
"Find doubled words despite capitalization differences, such as with '[The]\n[the]...', as well as allow differing amounts of whitespace (spaces,"
$ ./ch-2.py "to make a word bold: '...it is <B>very</B> very important...'."
"to make a word bold: '...it is <B>[very]</B> [very] important...'."
$ ./ch-2.py "Perl officially stands for Practical Extraction and Report Language, except when it doesn't."
""
$ ./ch-2.py "There's more than one one way to do it.\nEasy things should be easy and hard things should be possible."
"There's more than [one] [one] way to do it."
$ ./ch-2.py "I can't can't do this"
"I [can't] [can't] do this"
-
App::Netdisco - An open source web-based network management tool.
- Version: 2.099007 on 2026-06-05, with 877 votes
- Previous CPAN version: 2.099005 was released 5 days before
- Author: OLIVER
-
App::zipdetails - Display details about the internal structure of Zip files
- Version: 4.007 on 2026-06-01, with 66 votes
- Previous CPAN version: 4.006 was released 16 days before
- Author: PMQS
-
Archive::Tar - Manipulates TAR archives
- Version: 3.12 on 2026-06-02, with 16 votes
- Previous CPAN version: 3.10 was released 8 days before
- Author: BINGOS
-
Attean - A Semantic Web Framework
- Version: 0.039 on 2026-06-02, with 19 votes
- Previous CPAN version: 0.038_03 was released the same day
- Author: GWILLIAMS
-
CPANSA::DB - the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit
- Version: 20260531.001 on 2026-06-02, with 25 votes
- Previous CPAN version: 20260524.001 was released 9 days before
- Author: BRIANDFOY
-
Crypt::OpenSSL::X509 - Perl extension to OpenSSL's X509 API.
- Version: 2.1.1 on 2026-06-03, with 26 votes
- Previous CPAN version: 2.1.1 was released 6 days before
- Author: JONASBN
-
DBI - Database independent interface for Perl
- Version: 1.648 on 2026-06-04, with 283 votes
- Previous CPAN version: 1.647 was released 1 year, 4 months, 15 days before
- Author: HMBRAND
-
GD - Perl interface to the libgd graphics library
- Version: 2.85 on 2026-06-02, with 32 votes
- Previous CPAN version: 2.84 was released 4 months, 28 days before
- Author: RURBAN
-
HTTP::Message - HTTP style message (base class)
- Version: 7.02 on 2026-06-05, with 71 votes
- Previous CPAN version: 7.01 was released 7 months, 17 days before
- Author: OALDERS
-
JSON::Schema::Modern - Validate data against a schema using a JSON Schema
- Version: 0.640 on 2026-05-30, with 17 votes
- Previous CPAN version: 0.639 was released 20 days before
- Author: ETHER
-
JSON::Validator - Validate data against a JSON schema
- Version: 5.17 on 2026-06-04, with 59 votes
- Previous CPAN version: 5.16 was released the same day
- Author: JHTHORSEN
-
Module::CoreList - what modules shipped with versions of perl
- Version: 5.20260601 on 2026-06-01, with 45 votes
- Previous CPAN version: 5.20260420 was released 1 month, 11 days before
- Author: BINGOS
-
Mojo::JWT - JSON Web Token the Mojo way
- Version: 1.02 on 2026-06-03, with 18 votes
- Previous CPAN version: 1.01 was released 1 year, 7 months, 18 days before
- Author: JBERGER
-
Mojo::Redis - Redis driver based on Mojo::IOLoop
- Version: 3.31 on 2026-06-04, with 21 votes
- Previous CPAN version: 3.30 was released the same day
- Author: JHTHORSEN
-
Mojolicious - Real-time web framework
- Version: 9.46 on 2026-06-04, with 512 votes
- Previous CPAN version: 9.45 was released 29 days before
- Author: SRI
-
Mojolicious::Plugin::OpenAPI - OpenAPI / Swagger plugin for Mojolicious
- Version: 5.12 on 2026-06-06, with 56 votes
- Previous CPAN version: 5.11 was released 1 year, 2 months, 18 days before
- Author: JHTHORSEN
-
Net::Ping - check a remote host for reachability
- Version: 2.77 on 2026-06-02, with 15 votes
- Previous CPAN version: 2.76 was released 8 months, 24 days before
- Author: RURBAN
-
OpenAPI::Client - A client for talking to an Open API powered server
- Version: 1.09 on 2026-06-04, with 16 votes
- Previous CPAN version: 1.08 was released the same day
- Author: JHTHORSEN
-
SPVM - The SPVM Language
- Version: 0.990181 on 2026-06-03, with 36 votes
- Previous CPAN version: 0.990180 was released 2 days before
- Author: KIMOTO
-
Storable - persistence for Perl data structures
- Version: 3.41 on 2026-06-06, with 57 votes
- Previous CPAN version: 3.25 was released 4 years, 9 months, 7 days before
- Author: HAARG
-
Sys::Virt - libvirt Perl API
- Version: v12.4.0 on 2026-06-05, with 17 votes
- Previous CPAN version: v12.3.0 was released 30 days before
- Author: DANBERR
-
Tickit - Terminal Interface Construction KIT
- Version: 0.77 on 2026-06-01, with 29 votes
- Previous CPAN version: 0.76 was released the same day
- Author: PEVANS

Tony writes: ``` [Hours] [Activity] 2026/05/04 Monday 1.12 Daveās C11 _Atomic on p5p: testing, research and respond 0.35 #24402 research and comment 0.38 more _Atomic - research and respond some more 1.23 #24284 read, research and comment, work up a patch and push for CI 0.23 selfloader: check CI results and make PR 24404 0.68 more _Atomic: research and respond to leont 0.28 #24166 review discussion, research
0.95 #24166 work up a test case debugging
5.22
2026/05/05 Tuesday 0.15 #24284 minor fix 0.08 selfloader: apply to blead as suggested by PSC 0.65 #24406 review, find old problems that have already been reported. 0.52 das 0.55 #24166 work on a fix
1.05 #24166 more work on a fix - re-work
3.00
2026/05/06 Wednesday 1.68 #24407 testing, research, comments, work on a revert patch and push for smoke-me/CI 1.98 #24407 follow-up, work on a possible fix, push for smoke/CI and ask jkeenan to test 0.63 #24407 clean up, and add checks, testing and push to
update smoke-me
4.29
2026/05/07 Thursday 1.72 #24407 check smoke results, test the new fix branch elsewhere, open PRs #24409 (try to fix some more), #24410 (revert) 0.98 #24166 try to work out a fix
0.30 smoke-me branch cleanup
3.00
2026/05/11 Monday 0.53 #24413 review and comment 0.20 #24414 review, xenu covered it 0.83 #24416 review and comment 0.55 #24410 follow-up 1.58 security list - testing, debugging, research and reply 0.50 #24416 follow-up
1.05 utf8-strict rebase, re-check, start on line reading
5.24
2026/05/12 Tuesday 0.08 #24413 review update and approve 0.47 investigate detect conflicts failures and work up PR 24419 1.02 selfloader mixed io - rebase on blead updates, testing, push for CI/smoke-me 0.68 macos readdir, research, setup environment
2.03 security list
4.28
2026/05/13 Wednesday 0.55 research and work up a trivial patch for the vs2026 related build failures PR 24421
1.53 security list - stuff
2.08
2026/05/14 Thursday 0.90 #24412 review, comment 0.47 #24420 review and approve 0.17 #24423 review and approve 1.30 security list
0.88 security list
3.72
2026/05/15 Friday
0.50 security list
0.50
2026/05/18 Monday 2.58 security list
1.75 security list
4.33
2026/05/19 Tuesday 0.10 #24421 apply to blead on PSC approval 0.08 #24419 comment 0.08 security list
1.33 security list
1.59
2026/05/20 Wednesday
0.38 perl-security #147: perldelta, make PR 24433
0.38
2026/05/21 Thursday 0.08 #24433 apply to blead 1.30 #24434 review, research and comment 0.18 #24431 review, research history and comment 0.08 #24438 review and approve 0.25 #24437 review and approve 0.70 security list - sec 148
2.50 security list - debugging
5.09
2026/05/22 Friday 0.20 #24433 follow-up on security ticket 0.42 #24434 review and comment 0.12 #24431 review and approve
1.17 security list sec 148
1.91
2026/05/25 Monday 1.40 #24439 look into MacOS and Cygwin lockups (cygwin still running) 1.88 security list, more #24439 follow-up, security stuff re- work
1.27 security list - re-work, testing
4.55
2026/05/26 Tuesday 2.07 security list - debugging, fixes, debugging
1.98 security list - more debugging, fix some issues
4.05
2026/05/27 Wednesday 1.58 security list sec 148 0.85 security list
2.22 security list
4.65
Which I calculate is 57.88 hours.
Approximately 21 tickets were reviewed or worked on, and 2 patches were applied. ```

Dave writes:
Last month I continued looking into race conditions in threads and threads::shared. I have now reached the point where the threads-related test suite can run cleanly under 'valgrind --tool=helgrind / drd' and the threads-related tests can be repeatedly run in 40 terminals for days without issue.
I've pushed the work as GH ##24439.
Summary: * 20:21 GH #24258 dist/threads/t/free.t: Rare test failure in debugging build on FreeBSD
Total: * 20:21 (HH:MM)
Public Identifiers, UUIDs and a Tiny SEO Fix
A recent question from my friend and colleague Mohammad got me thinking about the way we identify data in web applications.
While working on the DBIC component of a REST API, he came across the term enumeration attack. In this type of attack, an attacker systematically guesses resource identifiers in order to access data they shouldn’t be able to see.
For example, if your API exposes URLs like this:
GET /users/123 GET /users/124 GET /users/125
then it’s easy for someone to try a large range of identifiers and see what they get back.
Mohammad’s question was simple:
Should we replace sequential IDs with UUIDs? And if we do, should we index the UUID column?
As is often the case, the answer turned out to be “it depends”.
Two Different Types of Data
The first thing I realised is that not all data objects have the same requirements.
Some objects are naturally public.
For example, books on a publishing website are intended to be discovered. In fact, you probably want people to be able to guess their URLs:
/books/design-patterns-in-modern-perl
In this case, a human-readable slug makes perfect sense. Other objects are private by nature. User accounts, orders, invoices and API resources generally shouldn’t be enumerable. In those cases, a UUID is often a better choice:
/users/550e8400-e29b-41d4-a716-446655440000
The important observation is that slugs and UUIDs solve different problems.
- Slugs are for humans (and, perhaps, search engines).
- UUIDs are for machines.
Database Design
A common question is whether a UUID should replace the primary key.
In most cases, I don’t think it should.
My preferred design is:
CREATE TABLE users ( id BIGINT PRIMARY KEY, uuid UUID NOT NULL UNIQUE );
The integer primary key remains the internal identifier used for joins and foreign keys.
The UUID becomes the public identifier exposed through APIs.
This gives you the best of both worlds:
- Small, efficient foreign keys.
- Fast joins.
- Unguessable public identifiers.
If the application regularly searches by UUID then the UUID column should be indexed. In practice, declaring it UNIQUE will usually create the appropriate index automatically.
The Hybrid Approach
Thinking about this reminded me that many large sites use a hybrid approach.
Amazon product URLs contain both a human-readable title and a stable identifier:
/Design-Patterns-Modern-Perl/dp/B0XXXXX123
The ASIN is what really identifies the product.
The title is there for humans.
Stack Overflow does something similar:
/questions/12345/how-do-i-index-a-uuid-column
Again, the question ID is authoritative. The title is helpful context.
My Line of Succession website uses the same idea.
A person page looks like this:
/p/2b5998-the-prince-william-prince-of-wales
The important part is the identifier:
2b5998
The rest is descriptive text.
This turns out to be particularly useful for royalty because titles change constantly. Someone might be “Prince William”, then “The Prince of Wales”, and eventually “King William V”.
By separating identity from presentation, old links continue to work regardless of title changes.
A Tiny Bug
While thinking about all of this, I discovered a small bug in Line of Succession.
The site allows any descriptive text after the identifier. These URLs all resolve to the same person:
/p/2b5998-the-prince-william-prince-of-wales /p/2b5998-prince-billy /p/2b5998-fred
The application correctly ignores the descriptive text and uses only the identifier.
However, there was a problem.
The page was generating its canonical URL from the incoming request path rather than from the person record.
That meant a request for:
/p/2b5998-prince-billy
generated:
<link rel="canonical"
href="https://lineofsuccession.co.uk/p/2b5998-prince-billy">which is obviously not the canonical URL.
The fix was surprisingly small:
sub canonical( $self ) {
if ($self->request->is_date_page) {
return '/' . $self->canonical_date;
+ } elsif($self->request->is_person_page) {
+ return '/p/' . $self->request->person->slug;
} else {
return $self->request->path;
}
}At the same time I simplified another method by making it reuse the canonical URL logic.
The result was a six-line patch that fixed the SEO issue and made the code slightly cleaner.
Those are my favourite kinds of fixes.
Future Improvements
The fix also revealed an emerging abstraction in the code.
At the moment, various parts of the application know how to construct URLs for different object types.
A cleaner approach would be to give objects responsibility for generating their own URLs.
I’m considering a HasURL role that would require an object to provide an identifier and optionally a prefix, and then build the URL automatically.
That’s a job for another day.
For now, a small question about UUIDs led to a useful discussion about public identifiers, a review of URL design, and a tiny production fix. Not bad for an afternoon’s work.
The post Public Identifiers, UUIDs and a Tiny SEO Fix first appeared on Perl Hacks.
Storable: fix bugtracker metadata
I am making a calculator and want to make it get the bitwise NOT as a function. How could I do that?
Here's the code:
my $val1 = <STDIN>;
print ~$val1, "\n";
If I enter 5, it produces "��".
RMG: separate email preparation from version bump - make the perlpolicy update more prominent by adding a specific section header - clarify the PERL_API constants instructions and make them release-specific - move the end-of-support email instructions to the email section Co-authored-by: Eric Herman <eric@freesa.org>
Someone on Reddit asked how you can maintain your own version of application while also being able to upgrade packages without getting conflicts between your changes and Debians changes.
Answer
This is normal in Debian life. Debian often solves this via three ways.
- .d config directories.
- Ordering
- dpkg-divert
Sudo, apache, nginx, and lightdm, all support the first way. Some packages like
unattended-upgrades use the 2nd way, ordering and some like minidnla only
allow for the third way.
LightDM might be a bit different, it follows order twice.
(A quick disclaimer before we begin: this session took place nearly 30 years ago. While the core structural concepts and my definitive…
This week we were back to full strength. We have now dealt with all of the belated issues and all of the blockers. Paul will be shipping 5.43.11 very shortly. With the amount of changes we have had to merge, we will not be able to rush the .11 cycle, but we intend to start the work on the 5.44 RC early, to ensure that we can release with as little additional delay as possible.
So I've created a programming language which blends a fairly JavaScript-like syntax with fairly Perl-like semantics, and a few other features that I haven't really seen in many programming languages.
This Perl:
use Path::Tiny;
my $str = uc(substr(Path::Tiny->new("myfile.txt")->slurp_utf8, 0, 80));
Becomes this in ZuzuScript:
from std/path import Path;
let str := new Path("myfile.txt")
ā· ^^.slurp_utf8
ā· ^^[0:80]
ā· uc ^^;
The ā· operator means "evaluate the left side, then evaluate the right side with ^^ set to the result of the left side". It's conceptually similar to | in shells and seems to make a lot of expressions so much easier to understand.
Other features I like:
Path/query operators for XPath/JSONPath-like deep access to nested objects.
Built-in
async/await.OO including roles/traits.
Runs in the browser!
PairLists (like hashes, but with ordered keys that allow duplicate keys)
for/else
Familiar things from Perl: documentation uses pod, variables are lexical (actually almost everything is lexical), there's a topic variable (but it's called ^^ instead of $_), different operators for different data types (> for numbers but gt for strings), weak typing, keywords like say and warn, first class regexps, and a CPAN-like site for sharing modules.
The primary implementation is in Perl, but there are alternative implementations in Rust and JavaScript. Yes, this is coded with AI assistance.
More info: https://zuzulang.org/.
Both are available from my Wiki Haven.
Next step will be the validation module for CPAN::MetaCurator, using the new:
use feature 'class'
code.
After that, back to the re-write of all *.pm in CPAN::MetaCurator.
-
App::cpm - a fast CPAN module installer
- Version: v1.1.1 on 2026-05-24, with 178 votes
- Previous CPAN version: v1.1.0 was released 16 days before
- Author: SKAJI
-
App::DBBrowser - Browse SQLite/MySQL/PostgreSQL databases and their tables interactively.
- Version: 2.442 on 2026-05-22, with 18 votes
- Previous CPAN version: 2.441 was released 7 days before
- Author: KUERBIS
-
App::Netdisco - An open source web-based network management tool.
- Version: 2.099004 on 2026-05-29, with 873 votes
- Previous CPAN version: 2.099003 was released 1 day before
- Author: OLIVER
-
Archive::Tar - Manipulates TAR archives
- Version: 3.10 on 2026-05-25, with 16 votes
- Previous CPAN version: 3.08 was released 3 days before
- Author: BINGOS
-
Cpanel::JSON::XS - cPanel fork of JSON::XS, fast and correct serializing
- Version: 4.41 on 2026-05-27, with 47 votes
- Previous CPAN version: 4.40 was released 8 months, 19 days before
- Author: RURBAN
-
CPANSA::DB - the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit
- Version: 20260524.001 on 2026-05-24, with 25 votes
- Previous CPAN version: 20260517.001 was released 7 days before
- Author: BRIANDFOY
-
DateTime::Format::Natural - Parse informal natural language date/time strings
- Version: 1.26 on 2026-05-28, with 19 votes
- Previous CPAN version: 1.25_04 was released 1 day before
- Author: SCHUBIGER
-
Minion::Backend::SQLite - SQLite backend for Minion job queue
- Version: v6.0.0 on 2026-05-26, with 14 votes
- Previous CPAN version: v5.0.7 was released 3 years, 9 months, 12 days before
- Author: DBOOK
-
Mojo::SQLite - A tiny Mojolicious wrapper for SQLite
- Version: v4.0.0 on 2026-05-25, with 28 votes
- Previous CPAN version: 3.009 was released 4 years, 2 months, 2 days before
- Author: DBOOK
-
SPVM - The SPVM Language
- Version: 0.990179 on 2026-05-29, with 36 votes
- Previous CPAN version: 0.990178 was released the same day
- Author: KIMOTO
-
Template::Toolkit - comprehensive template processing system
- Version: 3.106 on 2026-05-25, with 149 votes
- Previous CPAN version: 3.105 was released the same day
- Author: TODDR
-
Test::MockModule - Override subroutines in a module for unit testing
- Version: v0.185.2 on 2026-05-29, with 18 votes
- Previous CPAN version: v0.185.1 was released 2 days before
- Author: GFRANKS
-
YAML::Syck - Fast, lightweight YAML loader and dumper
- Version: 1.46 on 2026-05-25, with 18 votes
- Previous CPAN version: 1.45 was released 1 month, 1 day before
- Author: TODDR
One of the more interesting additions I’ve made recently to the Line of Succession website is support for the Model Context Protocol (MCP).
If you’ve spent any time around AI tooling recently, you’ve probably seen people talking about MCP. It’s often described as “USB for AI”, which is perhaps a little overblown, but the basic idea is sound. MCP provides a standard way for AI assistants to discover and use external tools and data sources.
In practical terms, it means that instead of building bespoke integrations for ChatGPT, Claude, Gemini and whatever comes next, you expose a standard MCP endpoint and let the AI clients do the rest.
For a data-driven site like Line of Succession, that seemed like an obvious experiment.
What is MCP?
The Model Context Protocol was originally developed by Anthropic and has rapidly become one of the emerging standards in the AI ecosystem.
An MCP server exposes:
- Information about itself
- A list of available tools
- Schemas describing how those tools should be called
- The results returned by those tools
An AI client can connect to the server, discover the available tools and invoke them when needed.
Instead of scraping web pages or attempting to infer information from HTML, the AI gets access to structured data.
That’s exactly the kind of thing Line of Succession is good at.
Why Add MCP?
The site already exposes information through a traditional web interface and a JSON API.
But those interfaces were designed for humans and developers respectively.
MCP gives AI systems a much cleaner integration point.
For example, an AI assistant can now answer questions like:
- Who was the British sovereign on 14 November 1948?
- What did the line of succession look like in 1980?
- Who was next in line when Queen Victoria died?
without having to scrape pages or understand the site’s internal URLs.
More importantly, it ensures that the information comes directly from the same database that powers the website.
The AI isn’t guessing.
It’s querying the source of truth.
As someone who runs a reference website, that’s a pretty attractive proposition.
The Initial Design
My first goal was to keep things simple.
Rather than exposing dozens of narrowly-focused tools, I started with just two:
sovereign_on_dateline_of_succession
Those two tools cover a surprisingly large proportion of the questions people are likely to ask.
The first returns the sovereign reigning on a given date. The second returns the line of succession for a specified date, with a configurable limit on the number of entries returned.
The implementation currently caps the list at thirty people. That’s enough for most use cases while preventing someone from accidentally asking for all six thousand people currently in the line of succession.
One thing I learned quite quickly is that MCP isn’t really about exposing huge amounts of data. It’s about exposing useful questions that can be answered from your data.
MCP Is Mostly JSON-RPC
One thing that surprised me when I first started reading the specification was how little protocol code is actually required.
At its core, MCP uses JSON-RPC.
A client sends requests like:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}
and the server responds with:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
...
}
}
Once I’d written helper methods for creating standard JSON-RPC responses, most of the complexity disappeared.
The MCP module contains methods like:
sub rpc_result ($self, $id, $result)
and:
sub rpc_error ($self, $id, $code, $message)
which means the Dancer route handlers remain pleasantly small.
The protocol logic lives in one place and the web application simply delegates to it.
Separating the MCP Logic
I didn’t want protocol-specific code scattered throughout the web application.
Instead, I created a dedicated module:
package Succession::MCP;
This module is responsible for:
- Initialisation
- Tool discovery
- Tool execution
- JSON-RPC response generation
- Error handling
That keeps the Dancer routes thin and makes the MCP implementation easier to test independently.
It also means that if I ever decide to expose the same MCP server through a different transport mechanism, most of the work is already done.
Tool Calls Are Mostly Adapters
One pleasant surprise was how little new application logic I actually had to write.
The MCP server needs to expose tools, but those tools ultimately just answer questions about the succession database. The code to answer those questions already existed.
For example, the application’s model layer already contained methods such as:
sovereign_on_date()line_of_succession()
These methods power parts of the website itself, so they already encapsulate all of the business rules and database queries.
The MCP implementation simply acts as an adapter.
When a tool call arrives, the server extracts the arguments, validates them and passes them to the existing model methods:
sub _call_tool ($self, $tool_name, $args) {
my $tool = $self->_tool_dispatch->{$tool_name};
return $tool->($args);
}
The tool implementations themselves are deliberately thin:
sub sovereign_on_date ($self, $args) {
my $date = $args->{date};
my $sovereign = $self->model->sovereign_on_date($date);
...
}
That’s exactly how I wanted it to work.
The MCP layer doesn’t know how to calculate a line of succession or determine who was sovereign on a particular date. It simply knows how to expose those capabilities through the protocol.
This is one of the advantages of adding MCP to an existing application. If your business logic is already cleanly separated from your web interface, an MCP server often becomes surprisingly straightforward to implement.
In many ways, adding MCP feels less like building a new application and more like adding another interface alongside the website and API.
The YAML Epiphany
The most interesting design decision came a little later.
Initially, the tool definitions lived in Perl data structures.
That worked, but it quickly became obvious that I was duplicating information.
The MCP server needed tool descriptions.
The documentation page needed tool descriptions.
The schemas needed to be defined somewhere.
And every change required updating multiple places.
The obvious answer was to move all of the tool definitions into a YAML file.
The MCP module now loads its tool definitions at startup:
sub _build__tools ($self) { return LoadFile($self->tools_file); }
The result is a single source of truth.
The same YAML file drives:
- The
tools/listresponse - Tool metadata
- JSON schemas
- Human-readable documentation
Adding a new tool now involves updating one file and writing the code that implements it.
Everything else follows automatically.
Here’s the current YAML file:
# data/mcp-tools.yml
- name: sovereign_on_date
description: Return the British sovereign on a given date.
documentation: |
Looks up the reigning British sovereign for the supplied date.
Use this when answering questions such as āWho was sovereign on
6 February 1952?ā
inputSchema:
type: object
properties:
date:
type: string
description: Date in YYYY-MM-DD format.
required:
- date
- name: line_of_succession
description: Return the line of succession on a given date.
documentation: |
Returns people in the line of succession.
If no date is supplied, the current line of succession is returned.
inputSchema:
type: object
properties:
date:
type: string
description: Optional date in YYYY-MM-DD format. Omit for the current line of succession.
limit:
type: integer
description: Maximum number of successors to return.
minimum: 1
maximum: 100
required: []
Looking back, this is probably the part of the design I’m happiest with. It feels very Perl-ish: keep configuration as data and avoid duplicating information wherever possible.
Human Documentation Matters
One thing I noticed while exploring other MCP servers is that many of them are effectively invisible to humans.
You know an endpoint exists.
You know it speaks MCP.
But unless you inspect the protocol responses manually, you don’t really know what it does.
I decided to add a conventional web page at /mcp.
The page lists all available tools, their descriptions and their schemas.
The nice part is that there is no duplicated documentation.
The page is generated from the same YAML definitions used by the MCP server itself.
If I add a new tool tomorrow, both the machine-readable and human-readable views update automatically.
Structured Data and Text Responses
Another nice feature of MCP is that tool results can include both structured data and human-readable text.
For example, a tool response might contain:
{
"content": [ {
"type": "text",
"text": "The sovereign on 14 November 1948 was George VI."
} ],
"structuredContent": {
...
}
}
The structured content is useful for software.
The text is useful for humans and language models.
Both are generated from the same underlying data.
That gives AI clients flexibility while ensuring consistency.
Getting Listed
Once everything was working, I submitted the server to the MCP directory at mcpservers.org.
That might seem like a small step, but discoverability is important.
An MCP server hidden on a random website isn’t much use if nobody knows it exists.
Directories like that are rapidly becoming the equivalent of API catalogues for the AI era.
Being listed means developers and AI enthusiasts can find the service without first discovering the website.
Was It Worth It?
Absolutely.
The amount of code required was surprisingly small. Most of the work wasn’t implementing the protocol; it was deciding how best to expose the data.
More importantly, it opens the site up to an entirely new audience: AI agents.
Historically, websites were built for humans and APIs were built for developers.
MCP introduces a third category: services designed specifically for AI systems.
For a structured-data site like Line of Succession, that’s a natural fit.
Will MCP still be the dominant standard in five years’ time? I have no idea. The AI industry changes too quickly to make confident predictions.
But right now it has significant momentum, broad industry support and a growing ecosystem of tools.
And if nothing else, it’s rather satisfying to ask an AI who was on the throne on a particular date and know that the answer came directly from my database rather than from whatever the model happened to remember.
The post Teaching AI About the British Monarchy with MCP first appeared on Perl Hacks.
Problem is Bad arg length for Socket::unpack_sockaddr_in, see below.
repro
file /tmp/demo/app.psgi
use 5.042;
use strictures;
use Plack::Request qw();
use experimental 'signatures';
my $app = sub($env) {
my $req = Plack::Request->new($env);
return $req->new_response(200, ['Content-Type' => 'text/plain'], ['Hello world'])->finalize;
};
file ~/.config/systemd/user/demo.service, replace ⦠with absolute path to plackup
[Unit]
Description=Socket-activated hello world service
Requires=demo.socket
After=network.target
[Service]
ExecStart=ā¦perl/5.42.2/bin/plackup -a /tmp/demo/app.psgi
Type=simple
file ~/.config/systemd/user/demo.socket
[Unit]
Description=Socket for hello world service activation
PartOf=demo.service
[Socket]
ListenStream=5000
NoDelay=true
Backlog=128
[Install]
WantedBy=sockets.target
patch Plack-1.0054
diff --git a/lib/HTTP/Server/PSGI.pm b/lib/HTTP/Server/PSGI.pm
index 225d4ca..f060463 100644
--- a/lib/HTTP/Server/PSGI.pm
+++ b/lib/HTTP/Server/PSGI.pm
@@ -90,6 +90,12 @@ sub prepare_socket_class {
sub setup_listener {
my $self = shift;
+ # TO DO: also handle LISTEN_PID LISTEN_PIDFDID
+ if ($ENV{LISTEN_FDS} && 1 == $ENV{LISTEN_FDS}) {
+ $self->{listen_sock} ||= IO::Socket::INET->new_from_fd(3, 'r+');
+ goto DONE;
+ }
+
$self->{listen_sock} ||= do {
my %args = (
Listen => SOMAXCONN,
@@ -104,6 +110,7 @@ sub setup_listener {
or die "failed to listen to port $self->{port}: $!";
};
+DONE:
$self->{server_ready}->({ %$self, proto => $self->{ssl} ? 'https' : 'http' });
}
- apply patch, install patched Plack where
app.psgican use it - make port 5000 available (perhaps is a Plack app already occupying?)
- run
systemctl --user daemon-reload - run
systemctl --user enable --now demo.socket
observed problem
Run xh -v :5000 or use any other HTTP client to GET http://localhost:5000, no response.
journalctl --user -e -u demo -f shows the error:
systemd: Started Socket-activated hello world service.
plackup: HTTP::Server::PSGI: Accepting connections at http://0:5000/
plackup: Bad arg length for Socket::unpack_sockaddr_in, length is 28, should be 16 at ā¦perl/5.42.2/lib/5.42.2/x86_64-linux-ld/Socket.pm line 858.
systemd: demo.service: Main process exited, code=exited, status=29/n/a
systemd: demo.service: Failed with result 'exit-code'.
To start over: systemctl --user stop demo.service ; systemctl --user restart demo.socket
expected result
HTTP request finishes
relevant
in dist IO: new_from_fd documentation, new_from_fd test
to clean up the system
systemctl --user disable --now demo.socket
rm ~/.config/systemd/user/demo.{service,socket} /tmp/demo/app.psgi
# rm -rf ā¦Plack-1.055
systemctl --user daemon-reload

TPRC hotel pricing and early-bird pricing end at midnight (Eastern Time) on May 28th. If you havenāt registered and reserved your hotel room, today is the day!
The conference schedule is now published. We have a lineup of great speakers! Check it out at https://tprc.us/tprc-2026-gsp/schedule-2/ .
We are also now accepting submissions for Lightning Talks, which are talks of no more than 5 minutes. They are a great way to participate, and make a contribution to the community! See https://tprc.us/tprc-2026-gsp/lightning-talks/ to submit a talk or to learn more about them.
Did you know that this conference is the Namer for Lightning Talks? It is true, way back in 2000, and we are proud to have an unbroken streak of Gongs ever since! See https://en.wikipedia.org/wiki/Lightning_talk , and https://www.youtube.com/@YAPCNA/search?query=lightning%20talk .
-
App::FatPacker::Simple - only fatpack a script
- Version: v1.0.2 on 2026-05-22, with 12 votes
- Previous CPAN version: v1.0.1 was released the same day
- Author: SKAJI
-
App::Netdisco - An open source web-based network management tool.
- Version: 2.099000 on 2026-05-20, with 868 votes
- Previous CPAN version: 2.098005 was released 10 days before
- Author: OLIVER
-
Archive::Tar - Manipulates TAR archives
- Version: 3.08 on 2026-05-22, with 16 votes
- Previous CPAN version: 3.06 was released 12 days before
- Author: BINGOS
-
Attean - A Semantic Web Framework
- Version: 0.038 on 2026-05-20, with 19 votes
- Previous CPAN version: 0.037 was released the same day
- Author: GWILLIAMS
-
Cache::FastMmap - Uses an mmap'ed file to act as a shared memory interprocess cache
- Version: 1.62 on 2026-05-18, with 25 votes
- Previous CPAN version: 1.61 was released 17 days before
- Author: ROBM
-
Catalyst::Plugin::Authentication - Infrastructure plugin for the Catalyst authentication framework.
- Version: 0.10026 on 2026-05-21, with 12 votes
- Previous CPAN version: 0.10_025 was released 1 day before
- Author: ETHER
-
CPANSA::DB - the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit
- Version: 20260517.001 on 2026-05-17, with 25 votes
- Previous CPAN version: 20260503.001 was released 12 days before
- Author: BRIANDFOY
-
HTML::Parser - HTML parser class
- Version: 3.85 on 2026-05-19, with 51 votes
- Previous CPAN version: 3.84 was released the same day
- Author: OALDERS
-
HTTP::Daemon - A simple http server class
- Version: 6.17 on 2026-05-19, with 13 votes
- Previous CPAN version: 6.16 was released 3 years, 2 months, 23 days before
- Author: OALDERS
-
HTTP::Tiny - A small, simple, correct HTTP/1.1 client
- Version: 0.094 on 2026-05-17, with 116 votes
- Previous CPAN version: 0.093 was released 5 days before
- Author: HAARG
-
IO::Compress - IO Interface to compressed data files/buffers
- Version: 2.220 on 2026-05-16, with 20 votes
- Previous CPAN version: 2.219 was released 2 months, 7 days before
- Author: PMQS
-
Minion - Job queue
- Version: 12.0 on 2026-05-18, with 236 votes
- Previous CPAN version: 11.0 was released 8 months, 26 days before
- Author: SRI
-
Role::Tiny - Roles: a nouvelle cuisine portion size slice of Moose
- Version: 2.002005 on 2026-05-17, with 72 votes
- Previous CPAN version: 2.002004 was released 5 years, 3 months, 24 days before
- Author: HAARG
-
Sereal - Fast, compact, powerful binary (de-)serialization
- Version: 5.006 on 2026-05-20, with 65 votes
- Previous CPAN version: 5.005 was released the same day
- Author: YVES
-
Sereal::Decoder - Fast, compact, powerful binary deserialization
- Version: 5.006 on 2026-05-20, with 26 votes
- Previous CPAN version: 5.005 was released the same day
- Author: YVES
-
Sereal::Encoder - Fast, compact, powerful binary serialization
- Version: 5.006 on 2026-05-20, with 25 votes
- Previous CPAN version: 5.005 was released the same day
- Author: YVES
-
Template::Toolkit - comprehensive template processing system
- Version: 3.103 on 2026-05-21, with 149 votes
- Previous CPAN version: 3.102 was released 1 year, 10 months, 29 days before
- Author: TODDR
-
WWW::Mechanize::Chrome - automate the Chrome browser
- Version: 0.77 on 2026-05-17, with 22 votes
- Previous CPAN version: 0.76 was released 2 months, 4 days before
- Author: CORION
-
XML::LibXML - Interface to Gnome libxml2 xml parsing and DOM library
- Version: 2.0213 on 2026-05-21, with 103 votes
- Previous CPAN version: 2.0212 was released 1 day before
- Author: TODDR

It is just over 30 days before the start of The Perl and Raku Conference. Even if you are a procrastinator, itās time to make your plans to attend!
Early bird pricing for registration will end on May 28. Special conference pricing for our block of hotel rooms also expires on May 28. So it is definitely time to get yourself registered and to get your room reserved.
Speakers have been notified, and the schedule is being built.
Go to https://tprc.us for all the information about how to register, how to reserve your room, and to see the schedule once it is posted.
When you are making your travel plans, consider the following: On Monday, June 29, we are offering a class: Teaching AI New Tricks: Perly MCPās for Claude. This class is described at https://tprc.us. Registration for the class is a separate ticket. Check out the details on our website!
Also, on Friday evening, June 26 after our first day of talks, we are offering our version of āPizza Planetā. We will provide a pizza supper, followed by transportation to the Roper Mountain Science Center and Planetarium show, and an opportunity to visit the Daniel Observatory. This experience is included as part of your conference fee, so come join the fun! You are guaranteed a ticket if you register by May 28!
The conference planning team needs to know if you are coming so we can make sure and have everything ready for you (t-shirt, lunches, name badge, planetarium tickets, etc). Please register today!
TL;DR
Twenty years of desktop environments ā WindowMaker, KDE3, fvwm, i3 ā and the lesson is always the same: consistency beats aesthetics. This is how I took one vim colorscheme (iceberg) and spread it across my entire desktop: terminal, prompt, window manager, launcher, screensaver, and even git. One palette aligns the whole UI. The only constraint you lay on yourself: timebox it. Because theming takes a fuck-ton of time.
Iceberg ahead
I’m no ricer by any means. But I can tell you my process. Nothing is planned, but everything I did, I did with a masterplan.
One of the defining characteristics of a good programmer is an instinct for keeping implementation details in the correct layer of an application.
That sounds abstract, but it turns out to explain a huge amount of the progress weāve made in software development over the last twenty-five years.
And nowhere is that clearer than in Perl web development.
Many of us who built web applications during the dotcom boom spent years learning this lesson the hard way.
We wrote CGI programs that:
- parsed HTTP requests
- generated HTML by hand
- connected directly to databases
- embedded SQL inline
- mixed business logic with presentation
- relied on Apache behaviour
- assumed specific filesystem layouts
- and often only worked on one particular server configuration
It all worked. Until it didnāt.
The history of Perl web development is, in many ways, the history of gradually moving implementation details into more appropriate architectural layers.
The Early CGI Years
Early Perl CGI applications were often a single giant script.
Youād open a file and see:
- request handling
- authentication
- HTML generation
- SQL queries
- business logic
- configuration
- deployment assumptions
ā¦all mixed together in a glorious ball of mud.
Something like this:
#!/usr/bin/perl
use CGI;
use DBI;
my $cgi = CGI->new;
print $cgi->header;
print "<html><body>";
my $dbh = DBI->connect(
"dbi:mysql:test",
"user",
"pass"
);
my $sth = $dbh->prepare(
"select * from users where id = ?"
);
$sth->execute($cgi->param('id'));
while (my $row = $sth->fetchrow_hashref) {
print "<h1>$row->{name}</h1>";
}
print "</body></html>";At the time, this felt perfectly normal.
And to be fair, it was a huge step forward from static HTML sites.
But the design had a fundamental problem:
Everything knew too much about everything else.
The application logic knew:
- how HTTP worked
- how HTML worked
- how Apache launched CGI scripts
- how the database worked
- how the operating system was configured
Every concern leaked into every other concern.
That made systems:
- hard to test
- hard to reuse
- hard to deploy
- hard to scale
- and terrifying to change
The First Big Lesson: Put Logic in Libraries
One of the first signs of a developer maturing is the realisation that application logic should live in reusable modules, not in front-end scripts.
Instead of this:
if ($user->{status} eq 'gold') {
$discount = 0.2;
}being embedded directly in a CGI script, it becomes:
my $discount = $user->discount_rate;
That sounds like a small change, but architecturally itās enormous.
Now the business logic lives in a library.
And once that happens, several good things follow automatically.
Multiple Interfaces Become Possible
If the logic is in modules, then:
- a web front-end
- a CLI tool
- a REST API
- a cron job
- a queue worker
ā¦can all use the same underlying code.
The interface layer becomes thin.
The application itself becomes independent of how users interact with it.
Thatās a huge increase in flexibility.
Testing Becomes Easier
Testing CGI scripts was always awkward.
Testing modules is straightforward.
You can instantiate objects, call methods, and inspect results without needing a web server or HTTP requests.
The easier code is to test, the more likely it is to be tested.
And tested code tends to survive longer.
Deployment Becomes Safer
Once the core behaviour is isolated from the interface layer, replacing the interface becomes far less risky.
You can redesign the UI without rewriting the application.
That separation is one of the foundations of maintainable software.
The PSGI Revolution
The next big architectural leap in Perl web development came with PSGI and Plack.
Younger developers may not fully appreciate how painful web deployment used to be.
In the early 2000s, moving an application between hosting environments could require substantial rewrites.
- A CGI application worked one way.
- A mod_perl application worked another way.
- FastCGI had its own quirks.
- Embedded Apache handlers behaved differently again.
Many Perl developers spent years repeatedly rewriting applications simply because deployment environments changed.
That was madness.
The deployment model is an operational concern.
It should not affect application architecture.
PSGI fixed this by defining a standard interface between web applications and web servers.
The core idea was beautifully simple:
A web application is just a function that receives an environment and returns a response.
Once that abstraction existed, applications no longer cared whether they were running:
- as CGI
- under mod_perl
- inside FastCGI
- under Starman
- behind nginx
- on a development laptop
- or inside a cloud container
The deployment details moved down a layer.
Exactly where they belonged.
This was one of the most important architectural improvements Perl web development ever made.
And it reflected a broader truth:
Good abstractions stop lower-level implementation details leaking upward.
The Transitional Era: FatPacker and cpanfile
There was also an interesting intermediate stage between traditional Perl deployments and full containerisation.
For years, one of the hardest parts of deploying Perl applications was dependency management.
Youād move an application to a new server and discover:
- the wrong module version
- missing XS libraries
- incompatible Perl versions
- or an entire dependency tree that worked perfectly on the developerās machine and nowhere else
Large parts of Perl deployment culture evolved around coping with this problem.
Tools like cpanfile improved things by making dependencies explicit and reproducible.
Instead of vaguely documenting requirements in a README, applications could formally declare:
requires 'Dancer2'; requires 'DBIx::Class'; requires 'Template';
That may seem obvious now, but it was a major improvement in deployment reliability.
Then tools like App::FatPacker went even further by packaging dependencies directly alongside applications.
Instead of relying on the target serverās Perl environment, applications could carry much of their runtime context with them.
These tools didnāt completely solve deployment portability:
- system libraries still mattered
- Perl versions still mattered
- operating system differences still mattered
ā¦but they represented an important shift in thinking.
The industry was gradually realising that:
- deployment environments were part of the application
- reproducibility mattered
- and infrastructure assumptions needed to be controlled
Containers eventually pushed this idea to its logical conclusion by packaging not just Perl dependencies, but the entire runtime environment.
In hindsight, tools like cpanfile and FatPacker were stepping stones toward modern container-based deployment models.
Containers Are the Same Idea Again
Docker and containers are simply the same architectural principle repeated one layer lower.
Before containers, deployments were often fragile and highly environment-specific.
Applications depended on:
- particular Linux distributions
- specific Perl versions
- installed system libraries
- hand-configured servers
- undocumented setup steps
Developers became experts in āworks on my machineā.
Operations teams became experts in swearing.
Containers changed the model.
Instead of deploying:
- source code
ā¦you deploy:
- a complete runtime environment
Now the application no longer cares whether it runs:
- on bare metal
- on a VPS
- in Kubernetes
- in ECS
- in Cloud Run
- or on someoneās laptop
Again:
- infrastructure concerns move downward
- application concerns stay upward
The boundaries become cleaner.
The Pattern Repeats Everywhere
Once you notice this pattern, you see it throughout software engineering.
Templates
Template systems separate:
- presentation
from
- application logic
HTML should not contain database code.
Business logic should not contain giant blobs of HTML.
ORMs and Database Layers
DBI separates applications from database engines.
ORMs separate applications from raw SQL structure.
Again:
- implementation details move downward
Configuration
Configuration belongs outside code.
Deployment-specific values should not be embedded in applications.
APIs
Clients should not care whether data comes from:
- PostgreSQL
- Redis
- another service
- a queue
- flat files
- or magic elves
Thatās the implementationās problem.
The Goal Is Not Abstraction for Its Own Sake
Of course, experienced developers also know that abstractions can become ridiculous.
Some abstractions simplify systems.
Others merely hide complexity behind six additional layers of YAML.
Joel Spolskyās āLaw of Leaky Abstractionsā remains painfully relevant.
The goal is not abstraction itself.
The goal is to isolate genuinely volatile details.
Good abstractions protect systems from change.
Bad abstractions merely obscure reality.
The Real Skill
The deeper lesson here is that software architecture is largely about deciding:
āWhat belongs where?ā
Experienced developers develop an instinct for:
- which details are likely to change
- which layers should know about which concerns
- and where boundaries should exist
That instinct is often more important than language choice, frameworks, or technology stacks.
And if you spent the early 2000s rewriting CGI applications to run under mod_perl, you probably learned that lesson the hard way.

The post The Long Road from CGI to Containers first appeared on Perl Hacks.
The unexpected intersection of maths literacy and coding confidence that every secondary educator should know about
-
App::DBBrowser - Browse SQLite/MySQL/PostgreSQL databases and their tables interactively.
- Version: 2.441 on 2026-05-15, with 18 votes
- Previous CPAN version: 2.440 was released 1 month, 3 days before
- Author: KUERBIS
-
App::Netdisco - An open source web-based network management tool.
- Version: 2.098005 on 2026-05-10, with 865 votes
- Previous CPAN version: 2.098004 was released 1 day before
- Author: OLIVER
-
App::zipdetails - Display details about the internal structure of Zip files
- Version: 4.006 on 2026-05-16, with 66 votes
- Previous CPAN version: 4.005 was released 2 months, 7 days before
- Author: PMQS
-
Archive::Tar - Manipulates TAR archives
- Version: 3.06 on 2026-05-10, with 16 votes
- Previous CPAN version: 3.04 was released 1 year, 2 months, 12 days before
- Author: BINGOS
-
Crypt::JWT - JSON Web Token
- Version: 0.038 on 2026-05-16, with 54 votes
- Previous CPAN version: 0.037_2 was released 5 days before
- Author: MIK
-
Crypt::Passphrase - A module for managing passwords in a cryptographically agile manner
- Version: 0.023 on 2026-05-15, with 17 votes
- Previous CPAN version: 0.022 was released 1 month, 24 days before
- Author: LEONT
-
CryptX - Cryptographic toolkit
- Version: 0.089 on 2026-05-10, with 53 votes
- Previous CPAN version: 0.088_005 was released 3 days before
- Author: MIK
-
Google::Ads::GoogleAds::Client - Google Ads API Client Library for Perl
- Version: v32.1.0 on 2026-05-13, with 20 votes
- Previous CPAN version: v32.0.0 was released 20 days before
- Author: CHEVALIER
-
Imager - Perl extension for Generating 24 bit Images
- Version: 1.031 on 2026-05-15, with 68 votes
- Previous CPAN version: 1.030 was released 1 month, 1 day before
- Author: TONYC
-
IO::Socket::IP - Family-neutral IP socket supporting both IPv4 and IPv6
- Version: 0.44 on 2026-05-13, with 22 votes
- Previous CPAN version: 0.43 was released 1 year, 5 months, 17 days before
- Author: PEVANS
-
JSON::Schema::Modern - Validate data against a schema using a JSON Schema
- Version: 0.639 on 2026-05-09, with 16 votes
- Previous CPAN version: 0.638 was released 21 days before
- Author: ETHER
-
LWP - The World-Wide Web library for Perl
- Version: 6.83 on 2026-05-12, with 211 votes
- Previous CPAN version: 6.82 was released 1 month, 13 days before
- Author: OALDERS
-
LWP::ConsoleLogger - LWP tracing and debugging
- Version: 1.000002 on 2026-05-12, with 12 votes
- Previous CPAN version: 1.000001 was released 2 years, 10 months, 21 days before
- Author: OALDERS
-
Mojo::Pg - Mojolicious ā„ PostgreSQL
- Version: 5.0 on 2026-05-12, with 98 votes
- Previous CPAN version: 4.29 was released 1 month, 19 days before
- Author: SRI
-
SPVM - The SPVM Language
- Version: 0.990172 on 2026-05-13, with 36 votes
- Previous CPAN version: 0.990171 was released 2 days before
- Author: KIMOTO
-
Win32 - Interfaces to some Win32 API Functions
- Version: 0.62 on 2026-05-11, with 13 votes
- Previous CPAN version: 0.61 was released the same day
- Author: JDB
-
YAML::LibYAML - Perl YAML Serialization using XS and libyaml
- Version: v0.907.0 on 2026-05-10, with 60 votes
- Previous CPAN version: v0.906.1 was released 14 days before
- Author: TINITA

