pp_ctl.c: Remove code which is no longer reachable This block was effectively superseded in abf573d165 in Nov/Dec 2022. Fixes: GH #23958.
Revert "win32/perllib.c: Omit unused formal parameter name" This reverts commit 6fb21d69a83e1c194e85e936ab62dea5c142a51d and changes to use PERL_UNUSED_ARG to silence the compiler warning that commit was intended to silence Fixes #23952.
I am trying to implement a Mojolicious application that (also) acts as a proxy in front or rclone serve.
To that purpose I am trying to get Mojolicious to act as a proxy, and request from rclone serve and serve the response. Pure redirecting won't do for authentication reasons.
This is the minimal working example I got thus far:
use Mojolicious::Lite -signatures;
use Mojo::Util qw/dumper/;
use Time::HiRes qw/time/;
helper "handle" => sub {
my $self = shift;
my $start = time(); my $start_all = $start;
my $req = $self->req->clone;
$req->url->scheme("http")->host("127.0.0.1")->port("3002");
my $ua = $self->ua;
my $tx = $ua->start(Mojo::Transaction::HTTP->new(req => $req));
$self->res->headers->from_hash($tx->res->headers->to_hash);
my $body = $tx->res->body;
$self->render(data => $body);
};
get '/*reqpath' => sub ($c) {
return $c->handle();
};
app->start;
It works fine - but is very slow, as getting the same content directly vs. via Mojolicious takes about 5 times as long.
The culprit seems to be
my $tx = $ua->start(Mojo::Transaction::HTTP->new(req => $req));
What's the reason? what should I do differently?
I have also tried to do that asynchronously, but as I expected it did not speed up the single transaction, it just became more responsive across a bunch of them.
PWC 349 Task 2: Meeting Point
Musical Interlude
We're going deep cut from the classic Blood on the Tracks album by Bob Dylan, Meet Me in the Morning
The Task
You are given an instruction string made up of U (up), D (down), L (left) and R (right). Write a script to return true if following the instructions, you meet (0,0) at any point along the sequence.
-
Example 1:
-
Input:
$path = "ULD" -
Output:
false - U -> (0,1), L -> (-1,1), D -> (-1, 0)
-
Input:
-
Example 2:
-
Input:
$path = "ULDR" -
Output:
true - U -> (0,1), L -> (-1,1), D -> (-1, 0), R -> (0,0)
-
Input:
-
Example 5:
-
Input:
$path = "RRUULLDDRRUU" -
Output:
true - RRUULLDD -> (0,0), RRUU -> (2,2)
-
Input:
The Deliberation
The first thought is emulate the movement on the grid and see if we end up at (0,0).
A clever thought arises: to end up back where we began, every Up must be matched with a Down, and every Right must be matched with a Left. So all we really have to do is count moves, right?
But Example 5 throws a wrench. The path makes a big square and passes through (0,0), but doesn't end there. I guess I really do need to check every move in the path.
Second clever thought (this one might be worth keeping): moving around a grid can be like moving in the complex plane. A horizontal move is along the real axis, and a vertical move is along the imaginary axis. If we think of the location as a complex number, then a move is an increment of either the real or the imaginary part.
The Code
Nice try, no cigar
Let's dispense with what didn't work, because it looked good until we hit Example 5.
sub meet($path)
{
my @m = map { scalar(()= $path =~ m/$_/g) } qw(U D L R);
return $m[0] == $m[1] && $m[2] == $m[3];
}
Notes:
- We'll try to count the number of each direction and see if Ups cancel Downs and Rights cancel Lefts.
-
my @m = map {...} qw(U D L R)-- for each move, map to the number of occurrences of that move in$path -
$path =~ m/$_/g--$_is one of (U,D,L,R). The regular expression match with agflag can return a list of all the occurrences of that letter in$path. -
scalar(()= ...)-- assigning the match to an empty list will create an array context, so thatm//will yield a list. Then taking thescalarof that will give a count. This is a variant of the=()=Saturn secret operator - Now it only remains to check if the numbers align.
Moving on to something that works
sub meetComplex($path)
{
use Math::Complex;
state $origin = (0 + 0*i);
state %move = ( R => (1 + 0*i), L => (-1 + 0*i),
U => (0 + 1*i), D => ( 0 + -1*i), );
my $place = $origin;
for ( split(//, uc $path) )
{
$place += $move{$_};
return true if $place == $origin;
}
return false;
}
Notes:
-
use Math::Complex-- Yes, Perl does complex arithmetic. -
state $origin = (0 + 0*i)-- Set up a "constant" for the origin.statemakes a variable that is initialized only the first time the function is entered, and limits the scope to the function.Math::Complexmakes it possible to write complex constants in a natural way. -
state %move-- Set up "constants" for movements. Moving Right and Left affects the real part of the complex number; moving Up and Down affects the imaginary part. -
for ( split(...) )-- Take the path apart to process each move. I threw in anucto make sure we would only have uppercase input. -
$place += $move{$_}-- For each move, change$placein the complex plane.Math::Complexoverloads operators as expected. -
return true if $place == $origin-- Complex numbers can be compared, of course. - If we didn't find (0,0), then the answer is
false.
All three of us met.
- The refalias draft PPC on the mailing list is looking good. We encourage Gianni to turn it into a full PPC doc PR
- We still would like more automation around making real CPAN distributions out of
dist/dirs. Paul will write an email requesting assistance on that subject specifically - Briefly discussed the subject of the
metamodule handling signatures with named parameters. Further discussion will continue on the email thread.
Announcing JSON::Schema::Validate: a lightweight, fast, 2020-12–compliant JSON Schema validator for Perl, with a mode for 'compiled' Perl and JavaScript
Hi everyone,
After a lof of work (a lot of testing and iteration), I would like to share with you my new module: JSON::Schema::Validate
It aims to provide a lean, fully self-contained implementation of JSON Schema Draft 2020-12, designed for production use: fast, predictable, and minimal dependencies, while still covering the real-world parts of the specifications that developers actually rely on, like optional extensions and client-side validation.
And in addition to the Perl engine, the module can now also compile your schema into a standalone JavaScript validator, so you can reuse the same validation logic both server-side and client-side. This is very useful to save server resources while improving user experience by providing immediate feedback to the end-user.
Why write another JSON Schema validator?
Perl's existing options either target older drafts or come with large dependencies. Many ecosystems (Python, Go, JS) have fast, modern validators, but Perl did not have an independent, lightweight, and modern 2020-12 implementation.
This module tries to fill that gap:
- Lightweight and Self-Contained: No XS, no heavy dependencies.
- Performance-Oriented: Optional ahead-of-time compilation to Perl closures reduces runtime overhead (hash lookups, branching, etc.), making it faster for repeated or large-scale validations.
- Spec Compliance: Full Draft 2020-12 support, including anchors/dynamic refs, annotation-driven
unevaluatedItems/Properties, conditionals (if/then/else), combinators (allOf/anyOf/oneOf/not), and recursion safety. - Practical Tools: Built-in format validators (date-time, email, IP, URI, etc.), content assertions (base64, media types like JSON), optional pruning of unknown fields, and traceable validation for debugging.
- Error Handling: Predictable error objects with instance path, schema pointer, keyword, and message—great for logging or user feedback.
- Extensions (Opt-In): Like
uniqueKeysfor enforcing unique property values/tuples in arrays. - JavaScript Export: Compile your schema to a standalone JS validator for browser-side checks, reusing the same logic client-side to offload server work and improve UX.
- Vocabulary Awareness: Honors
$vocabularydeclarations; unknown required vocabs can be ignored if needed.
It is designed to stay small, and extensible, with hooks for custom resolvers, formats, and decoders.
Basic Perl Usage Example
```perl use JSON::Schema::Validate; use JSON ();
my $schema = { '$schema' => 'https://json-schema.org/draft/2020-12/schema', type => 'object', required => ['name'], properties => { name => { type => 'string', minLength => 1 }, age => { type => 'integer', minimum => 0 } }, additionalProperties => JSON::false, }; my $js = JSON::Schema::Validate->new( $schema ) ->compile # Enable ahead-of-time compilation ->register_builtin_formats; # Activate date/email/IP/etc. checks my $ok = $js->validate({ name => "Alice", age => 30 }); if( !$ok ) { my $err = $js->error; # First error object warn "$err"; # e.g., "#/name: string shorter than minLength 1" } ```
[Error objects (JSON::Schema::Validate::Error) contain:
perl { message => "string shorter than minLength 1", path => "#/name", schema_pointer => "#/properties/name/minLength", keyword => "minLength", }
For pruning unknown fields (e.g., in APIs):
perl $js->prune_unknown(1); # Or via constructor my $cleaned = $js->prune_instance( $data ); # Returns a pruned copy
Ahead-of-time compilation to Perl closures
Calling ->compile walks the schema and generates a Perl closure for each node. This reduces:
- hash lookups
- branching
- keyword checks
- repeated child schema compilation
It typically gives a noticeable speedup for large schemas or repeated validations.
Additional feature: Compile your schema to pure JavaScript
This is a new feature I am quite excited about:
perl use Class::File qw( file ); my $js_source = $js->compile_js( name => 'validateCompany', # Custom function name ecma => 2018, # Assume modern browser features max_errors => 50 # Client-side error cap ); # Write to file: validator.js file("validator.js")->unload_utf8( $js_source );
This produces a self-contained JS file that you can load in any browser:
html <script src="validator.js"></script> <script> const inst = { name: "", age: "5" }; // Form data const errors = validateCompany(inst); if( errors.length ) { console.log(errors[0].path + ": " + errors[0].message); } </script>
The output validator supports most of the JSON Schema 2020-12 specifications:
- types, numbers, strings, patterns
- arrays,
items,contains/minContains/maxContains properties,requiredallOf,anyOf,oneOf,notif/then/else- extension:
uniqueKeys - detailed errors identical in structure to the Perl side
Unsupported keywords simply fail open on the JS side and remain enforced on the server side.
Recent updates (v0.6.0) improved regexp conversion (\p{...} to Script Extensions) and error consistency.
CLI Tool: jsonvalidate (App::jsonvalidate)
For quick checks or scripting:
``` jsonvalidate --schema schema.json --instance data.json
Or batch: --jsonl instances.jsonl
With tracing: --trace --trace-limit=100
Output errors as JSON: --json
```
It mirrors the module's options, like --compile, --content-checks, and --register-formats.
How compliance compares to other ecosystems?
- Python (jsonschema) is renown for being excellent for full spec depth, extensions, and vocabularies—but heavier and slower in some cases.
- Python (fastjsonschema) is significantly faster, but its JS output is not browser-portable.
- AJV (Node.js) is extremely fast and feature-rich, but depends on bundlers and a larger ecosystem.
JSON::Schema::Validate aims for a middle ground:
- Very strong correctness for the core 2020-12 features
- Clean handling of anchors/dynamicRefs (many libraries struggle here)
- Annotation-aware
unevaluatedItems,unevaluatedProperties - Extremely predictable error reporting
- Lightweight and dependency-free
- Built for Perl developers who want modern validation with minimal dependencies
- Unique ability to generate a portable JS validator directly from Perl
It aims to be a practical, modern, easy-to-use tool the Perl way.
Documentation & test suite
The module comes with detailed POD describing:
- constructor options
- pruning rules
- compilation strategy
- combinator behaviours
- vocabulary management
- content assertions
- JS code generation
And a large test suite covering almost all keywords plus numerous edge cases.
Feedbacks are welcome !
Thanks for reading, and I hope this is useful to our Perl community !
[link] [comments]
pp_ctl.c: Remove extra whitespace in inline comments This will enable an accurate search for 'Check for defer'.
I have filenames with this pattern [a-zA-Z0-9]{10} and .txt extension. They might also contain a pattern like this [super] and it could be either before or after [a-zA-Z0-9]{10} with '_' separating optionally. I want to select the filenames that do not contain the string [super] either before or after [a-zA-Z0-9]{10}. My mwe is not working of course.
mwe:
#!/usr/bin/env perl
use strict; use warnings;
my @filenames = ( "0001_abc_[super][abcdefghij].txt",
"0002_abc_[acegikmoqs]_[super].txt",
"0008_cde_[zyxwvutsrq].txt" );
foreach (@filenames) {
if ($_ =~ /^(?!.*\[super])_?\[[a-zA-Z0-9]]\.txt$/) {print "match 1\n";}
elsif ($_ =~ /\[[a-zA-Z0-9]]_?(?!\[super])\.txt$/) {print "match 2\n";}
}
If you haven't already done so, please fill out the Perl IDE Developer survey for 2025!
The survey has returned after ~12 years of inactivity, we've already got a good turn out, let's make it a great one!
Results will be published on December 1st, there will also be a Perl Advent article about it, thanks!
[link] [comments]
| Saw this in the application form for an API key for active.com [link] [comments] |
Hello all, old time perl gal checking in. In the 90s I churned out some pretty decent stuff from scratch, and resurrected several of my scripts around 2013. But I've slept since then and am super rusty.
Being put on unpaid leave Thursday didn't help matters. My brain is WTF-ing nonstop right now.
Anyway, does anyone have a snippet of code that would do the following:
3 text files exist in 3 subdirectories -
A/Afile1.txt B/Bfile2.txt C/Cfile3.txt
Afile1.txt contains Name A CityA ZipA StateA
Bfile2.txt contains Name B CityB ZipB StateB
Cfile3.txt contains Name C CityC ZipC StateA <-- yes StateA, that's intentional
I want to search for StateA instances and print the filenames that contain StateA to the HTML result page.
I appreciate your assistance!! I think once I see a legit, relatively simple way to do this then things will click for me and I can run with it.
ETA: I apologize for the formatting, the example files show up in my post as single lines without returns 😖
[link] [comments]
Exercise statement in pp_ctl.c: S_unwind_loop() Per suggestion by Dave Mitchell in GH #23948, including comment re pp_return(). Per review: Ensure that only one warning was generated in newly added test block. Also, improve two test descriptions.
Originally published at Perl Weekly 748
Hi there,
Just couple of days ago, we had another development release: Perl v5.43.5. Among the many changes in this release, my favourite is Named Parameters in Signatures. For further details, please check out the perldelta page.
The LPW 2025 is finally happening on 29th Nov 2025. So if you are available then please do join us for the tech meet. I would request you to register if you are planning to attend as this will help the organisers schedule the day accordingly. It's a great opportunity to meet friends and attend talks from speakers like Sawyer, Paul Evans and Stevan Little.
I have submitted a talk, Design Patterns in Modern Perl, and it has been accepted. I am excited to share my ideas with fellow tech friends. Here is the list of talks for you. One last thing, please register your favourite talks, this will help the organisers with scheduling. Someone also shared a very interesting post about Lunch options - LPW 2025.
The Advent Calendar is back and we need more articles for Perl Advent Calendar 2025. Please submit your proposal. I have proposed one about OpenAPI::Linter and it has already been accepted already. Soon, I'll submit the first draft. I know, time is running out - too many things to do in too little time.
Holiday season is kicking in, so take it easy and enjoy rest of the newsletter.
--
Your editor: Mohammad Sajid Anwar.
Announcements
You're invited: LPW 2025
You're welcome to attend this week's free London Perl and Raku Workshop 2025 on Saturday the 29th of November.
Articles
This week in PSC (207) | 2025-11-17
Talking about changes in the latest development release.
When does February 29th next land on a Monday?
Just released Cron::Toolkit 0.09 to CPAN to help answer this and other puzzlers. Over 400 tests.
Elderly Camels in the Cloud
Dave continues to share his recent work with Google Cloud and Google Run.
Grants
Maintaining Perl 5 Core (Dave Mitchell): October 2025
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 Lance Wicks.
The Weekly Challenge - 349
Welcome to a new week with a couple of fun tasks "Power String" and "Meeting Point". 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 - 348
Enjoy a quick recap of last week's contributions by Team PWC dealing with the "String Alike" and "Convert Time" tasks in Perl and Raku. You will find plenty of solutions to keep you busy.
TWC348
This is professional-grade Perl code that demonstrates deep understanding of the language. The solutions are both correct and elegantly implemented.
Time Alike
This is an excellent technical write-up and solid Raku code. Both scripts are idiomatic, correct, and easy to follow.
String Alike, Convert Time
The post is a clear, concise, and practical walkthrough of solving two weekly challenge problems. The code is functional, readable, and demonstrates a good understanding of Perl. The explanations are direct and easy to follow.
Perl Weekly Challenge: Week 348
Jaldhar demonstrates good programming fundamentals and the ability to think critically about language design. The Perl context discussion alone makes this submission valuable, as it highlights a real learning barrier that affects many developers. The experienced-based commentary is exactly what helps improve programming languages and educational resources.
The Times They Are a-Like
Jorg demonstrates exceptional mathematical talent - the mixed radix solution to Task 2 is one of the most elegant I've seen for this problem. The J implementation is actually idiomatic and shows good understanding of array programming.
splitting, counting and multiplying
Excellent work! This showcases exceptional versatility in programming languages and database integration. The solutions are consistently correct across all implementations.
Perl Weekly Challenge 348
The combination of optimal algorithms, comprehensive error handling, and critical problem analysis places this submission in the top tier of challenge solutions. This is the quality of work one would expect from a senior software engineer.
Get Those Strings and Times Covered
This is professional-grade Perl programming that demonstrates both technical competence and thoughtful problem-solving. The solutions are not just correct but elegantly efficient.
Ticking away the moments that make up a challenge…
This is an excellent and highly polished multi-language implementation. Packy demonstrates exceptional attention to detail, pedagogical clarity, and cross-language consistency.
String time
This represents solid, practical Perl programming from someone with considerable experience. It demonstrates a focus on correctness, testing, and maintainability that is valuable in real-world programming.
The Weekly Challenge #348
This represents the work of an experienced software engineer who understands both the technical challenges and the importance of documentation. Excellent work that balances correctness, clarity, and practical considerations.
Time for Strings
This represents solid practical programming with good problem-solving skills. The mathematical approach to Task 2 is particularly commendable and shows Roger can think abstractly about problems.
Alike Time
This is an excellent, well-explained, and pedagogically focused post. Simon demonstrates a strong grasp of Python fundamentals and a clear, methodical approach to problem-solving. The code is clean, correct, and accompanied by reasoning that is easy to follow.
Rakudo
2025.46 Advent Alert & Release #187
Weekly collections
NICEPERL's lists
Great CPAN modules released last week.
Events
London Perl and Raku Workshop
November 29, 2025
Perl Maven online: Code-reading and testing
December 1, 2025
Toronto.pm - online - How SUSE is using Perl
December 6, 2025
Paris.pm monthly meeting
December 10, 2025
German Perl/Raku Workshop 2026 in Berlin
March 16-18, 2025
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.
Demonstrate exceptions for goto &NAME Provide simple examples of goto &NAME syntax used incorrectly. Addresses: GH #23811 Remove two superfluous statements, per review by @mauke. Remove some superfluous whitespace.
It’s not every day that cybersecurity and ’90s rock collide, but when they do, the bugs “come back like before”. Welcome to the Perl Jam…
After adding the endpoint to get data from database in part 2, now I would like to add the ability to insert and delete todos from the…
In last week’s post I showed how to run a modern Dancer2 app on Google Cloud Run. That’s lovely if your codebase already speaks PSGI and lives in a nice, testable, framework-shaped box.
But that’s not where a lot of Perl lives.
Plenty of useful Perl on the internet is still stuck in old-school CGI – the kind of thing you’d drop into cgi-bin on a shared host in 2003 and then try not to think about too much.
So in this post, I want to show that:
If you can run a Dancer2 app on Cloud Run, you can also run ancient CGI on Cloud Run – without rewriting it.
To keep things on the right side of history, we’ll use nms FormMail rather than Matt Wright’s original script, but the principle is exactly the same.
Prerequisites: Google Cloud and Cloud Run
If you already followed the Dancer2 post and have Cloud Run working, you can skip this section and go straight to “Wrapping nms FormMail in PSGI”.
If not, here’s the minimum you need.
-
Google account and project
-
Go to the Google Cloud Console.
-
Create a new project (e.g. “perl-cgi-cloud-run-demo”).
-
-
Enable billing
-
Cloud Run is pay-as-you-go with a generous free tier, but you must attach a billing account to your project.
-
-
Install the
gcloudCLI-
Install the Google Cloud SDK for your platform.
-
Run:
and follow the prompts to:
-
log in
-
select your project
-
pick a default region (I’ll assume “europe-west1” below).
-
-
-
Enable required APIs
In your project:
-
Create a Docker repository in Artifact Registry
That’s all the GCP groundwork. Now we can worry about Perl.
The starting point: an old CGI FormMail
Our starting assumption:
-
You already have a CGI script like nms FormMail
-
It’s a single “.pl” file, intended to be dropped into “cgi-bin”
-
It expects to be called via the CGI interface and send mail using:
On a traditional host, Apache (or similar) would:
-
parse the HTTP request
-
set CGI environment variables (
REQUEST_METHOD,QUERY_STRING, etc.) -
run
formmail.plas a process -
let it call
/usr/sbin/sendmail
Cloud Run gives us none of that. It gives us:
-
a HTTP endpoint
-
backed by a container
-
listening on a port (
$PORT)
Our job is to recreate just enough of that old environment inside a container.
We’ll do that in two small pieces:
-
A PSGI wrapper that emulates CGI.
-
A sendmail shim so the script can still “talk” sendmail.
Architecture in one paragraph
Inside the container we’ll have:
-
nms FormMail – unchanged CGI script at
/app/formmail.pl -
PSGI wrapper (
app.psgi) – usingCGI::CompileandCGI::Emulate::PSGI -
Plack/Starlet – a simple HTTP server exposing
app.psgion$PORT -
msmtp-mta – providing
/usr/sbin/sendmailand relaying mail to a real SMTP server
Cloud Run just sees “HTTP service running in a container”. Our CGI script still thinks it’s on a early-2000s shared host.
Step 1 – Wrapping nms FormMail in PSGI
First we write a tiny PSGI wrapper. This is the only new Perl we need:
-
CGI::Compileloads the CGI script and turns itsmainpackage into a coderef. -
CGI::Emulate::PSGIfakes the CGI environment for each request. -
The CGI script doesn’t know or care that it’s no longer being run by Apache.
Later, we’ll run this with:
Step 2 – Adding a sendmail shim
Next problem: Cloud Run doesn’t give you a local mail transfer agent.
There is no real /usr/sbin/sendmail, and you wouldn’t want to run a full MTA in a stateless container anyway.
Instead, we’ll install msmtp-mta, a light-weight SMTP client that includes a sendmail-compatible wrapper. It gives you a /usr/sbin/sendmail binary that forwards mail to a remote SMTP server (Mailgun, SES, your mail provider, etc.).
From the CGI script’s point of view, nothing changes:
We’ll configure msmtp from environment variables at container start-up, so Cloud Run’s --set-env-vars values are actually used.
Step 3 – Dockerfile (+ entrypoint) for Perl, PSGI and sendmail shim
Here’s a complete Dockerfile that pulls this together.
-
We never touch
formmail.pl. It goes into/appand that’s it. -
msmtp gives us
/usr/sbin/sendmail, so the CGI script stays in its 1990s comfort zone. -
The entrypoint writes
/etc/msmtprcat runtime, so Cloud Run’s environment variables are actually used.
Step 4 – Building and pushing the image
With the Dockerfile and docker-entrypoint.sh in place, we can build and push the image to Artifact Registry.
I’ll assume:
-
Project ID:
PROJECT_ID -
Region:
europe-west1 -
Repository:
formmail-repo -
Image name:
nms-formmail
First, build the image locally:
The post Elderly Camels in the Cloud first appeared on Perl Hacks.
Weekly Challenge 348
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. It's a great way for us all to practice some coding.
Task 1: String Alike
Tasks
You are given a string of even length.
Write a script to find if the given string split into two halves of equal lengths and they both have same number of vowels.
My solution
The main logic for this challenge is a function called count_vowels. The takes a string and returns the number of vowels. The Python code for this function is
def count_vowels(s: str) -> int:
vowels = 'aeiouAEIOU'
return sum(1 for char in s if char in vowels)
Not to be outdone, it's a single line in Perl.
sub count_vowels ($s) {
return $s =~ tr/aeiouAEIOU/aeiouAEIOU/;
}
With that out of the way, the main function
- Throws an error if there there there is not an even number of characters in the
input_string. - Return False if there is are no vowels in the
input_string. - Set the variables
first_halfandsecond_half. - Compare the number of vowels in each half.
- Return True if the two string have the same number of vowels, else False.
def string_alike(input_string: str) -> bool:
if len(input_string) % 2 == 1:
raise ValueError("Input string must have an even length.")
if count_vowels(input_string) == 0:
return False
first_half = input_string[:len(input_string)//2]
second_half = input_string[len(input_string)//2:]
return count_vowels(first_half) == count_vowels(second_half)
The Perl solution follows the same logic.
sub string_alike ($input_string) {
die "Input string must have an even length."
if length($input_string) % 2 == 1;
if ( count_vowels($input_string) == 0 ) {
say "False";
return;
}
my $mid = length($input_string) / 2;
my $first_half = substr( $input_string, 0, $mid );
my $second_half = substr( $input_string, $mid );
say count_vowels($first_half) == count_vowels($second_half)
? "True"
: "False";
}
Examples
$ ./ch-1.py textbook
False
$ ./ch-1.py book
True
$ ./ch-1.py AbCdEfGh
True
$ ./ch-1.py rhythmmyth
False
$ ./ch-1.py UmpireeAudio
False
Task 2: Covert Time
Task
You are given two strings, $source and $target, containing time in 24-hour time form.
Write a script to convert the source into target by performing one of the following operations:
- Add 1 minute
- Add 5 minutes
- Add 15 minutes
- Add 60 minutes
Find the total operations needed to get to the target.
My solution
For this task I have created a function called time_to_minutes that takes the time in HH:MM format, and returns the minutes since midnight. It also checks that the time is in a valid format, and the hours (0-23) and minutes (0-59) minutes are in range.
def time_to_minute(s: str) -> int:
if not re.match(r'^\d{1,2}:\d{2}$', s):
raise ValueError("Invalid time format, should be HH:MM")
hour, minute = map(int, s.split(':'))
if hour < 0 or hour > 23 or minute < 0 or minute > 59:
raise ValueError("Invalid time format")
return hour * 60 + minute
In the main function, I set a variable duration that is the minutes difference between the source and target. If this value is negative, the time crosses midnight, so I add 1440 (60 × 24) minutes to compute the correct duration.
def convert_time(source: str, target: str) -> int:
duration = time_to_minute(target) - time_to_minute(source)
if duration < 0:
duration += 24 * 60
I set the moves list (array in Perl) to the possible moves, with the largest first. For each move, I make as many moves as possible, adding the number of moves to the count variable, and reducing duration to what remains.
moves = [60, 15, 5, 1]
count = 0
for move in moves:
count += duration // move
duration %= move
return count
The Perl solution follows the same logic.
Examples
$ ./ch-2.py 02:30 02:45
1
$ ./ch-2.py 11:55 12:15
2
$ ./ch-2.py 09:00 13:00
4
$ ./ch-2.py 23:45 00:30
3
$ ./ch-2.py 14:20 15:25
2
This release follows up on release 2.08 and hopefully stabilizes the test suite even further.
At the same time we are taking a first step towards the next major release by deprecating the use of XML configuration files. The preferred format for configuration files is now YAML. The XML support will be removed in a future major release, for now this is just a deprecation notice and several warnings from the test suite.
When we did the last major release to version 2, see the blog post:
We caused some grievances for one of our users, afterwards we discussed that perhaps the distribution should have been renamed to Workflow2 to better reflect the breaking changes and to make a more clear distinction and separation. However, at the time we did not expect this to be a problem since we wanted to keep the distribution name simple and easy to remember. Instead we will try to communicate breaking changes more clearly in future major releases.
But let's see how this pans out as we are slowly approaching the next major release and what we decide on moving forward.
The challenge with Perl and version numbers is complex and now that the mentioned problems got sorted out, I do not believe we would experience them again and I for one want to avoid renaming the distribution in future major releases.
The release is available on CPAN and GitHub:
Change log for Perl Workflow release 2.09.
2.09 2025-11-23 maintenance release, update not required
-
Deprecation notice of use of XML configuration files, issue #125 and deprecation notices implementation by @ehuelsmann via PR #256.
- YAML configuration files are now the preferred format for configuration of Workflow instances. The XML implementation was based on XML::Simple, which itself has been discouraged for use for several years. The distribution still supports XML configuration files, but this support will be removed in a future unscheduled major release.
Improvements to test suite, PR #275 by @ehuelsmann. This is a follow up on release 2.08, since we spotted the issue in the tests running as part of the CI pipeline on GitHub
-
App::Netdisco - An open source web-based network management tool.
- Version: 2.095003 on 2025-11-18, with 799 votes
- Previous CPAN version: 2.095002 was 2 days before
- Author: OLIVER
-
JSON::Schema::Modern - Validate data against a schema using a JSON Schema
- Version: 0.623 on 2025-11-17, with 13 votes
- Previous CPAN version: 0.622 was 8 days before
- Author: ETHER
-
Module::CoreList - what modules shipped with versions of perl
- Version: 5.20251120 on 2025-11-20, with 44 votes
- Previous CPAN version: 5.20251022 was 27 days before
- Author: BINGOS
-
Net::Amazon::S3 - Use the Amazon S3 - Simple Storage Service
- Version: 0.992 on 2025-11-22, with 13 votes
- Previous CPAN version: 0.991 was 3 years, 4 months, 5 days before
- Author: BARNEY
-
OpenTelemetry - A Perl implementation of the OpenTelemetry standard
- Version: 0.033 on 2025-11-21, with 30 votes
- Previous CPAN version: 0.032 was 1 day before
- Author: JJATRIA
-
SPVM - The SPVM Language
- Version: 0.990107 on 2025-11-18, with 36 votes
- Previous CPAN version: 0.990106 was 6 days before
- Author: KIMOTO
-
Type::Tiny - tiny, yet Moo(se)-compatible type constraint
- Version: 2.008005 on 2025-11-20, with 145 votes
- Previous CPAN version: 2.008004 was 1 month, 3 days before
- Author: TOBYINK
-
XML::Feed - XML Syndication Feed Support
- Version: v1.0.0 on 2025-11-17, with 19 votes
- Previous CPAN version: 0.65 was 1 year, 4 months, 8 days before
- Author: DAVECROSS

Dave writes:
Last month was mostly spent doing a second big refactor of ExtUtils::ParseXS. My previous refactor converted the parser to assemble each XSUB into an Abstract Syntax Tree (AST) and only then emit the C code for it (previously the parsing and C code emitting were interleaved on the fly). This new work extends that so that the whole XS file is now one big AST, and the C code is only generated once all parsing is complete.
As well as fixing lots of minor parsing bugs along the way, another benefit of this big refactoring is that ExtUtils::ParseXS becomes manageable once again. Rather than one big 1400-line parsing loop, the parsing and code generating is split up into lots of little methods in subclasses which represent the nodes of the AST and which process just one thing.
As an example, the logic which handled (permissible) duplicate XSUB declarations in different C processor branches, such as
#ifdef USE_2ARG
int foo(int i, int j)
#else
int foo(int i)
#endif
used to be spread over many parts of the program; it's now almost all concentrated into the parsing and code-emitting methods of a single Node subclass.
This branch is currently pushed and undergoing review.
My earlier work on rewriting the XS reference manual, perlxs.pod, was made into a PR a month ago, and this month I revised it based on reviewers' feedback.
Summary: * 11:39 modernise perlxs.pod * 64:57 refactor Extutils::ParseXS: file-scoped AST
Total: * 76:36 (HH::MM)
Do you thrive in a fast-paced scale-up environment, surrounded by an ambitious and creative team?
We’re on a mission to make payments simple, secure, and accessible for every business. With powerful in-house technology and deep expertise, our modular platform brings online, in-person, and cross-border payments together in one place — giving merchants the flexibility to scale on their own terms. Through a partnership-first approach, we tackle complexity head-on, keep payments running smoothly, and boost success rates. It’s how we level the playing field for businesses of all sizes and ambitions.
Join a leading tech company driving innovation in the payments industry. You’ll work with global leaders like Visa and Mastercard, as well as next generation “pay later” solutions such as Klarna and Afterpay. Our engineering teams apply Domain-Driven Design (DDD) principles, microservices architecture to build scalable and maintainable systems.
•Develop and maintain Perl-based applications and systems to handle risk management, monitoring, and onboarding processes
•Collaborate with other developers, and cross-functional teams to define, design, and deliver new features and functionalities
•Assist in the migration of projects from Perl to other languages, such as Java, while ensuring the smooth operation and transition of systems
•Contribute to code reviews and provide valuable insights to uphold coding standards and best practices
•Stay up to date with the latest industry trends and technologies to drive innovation and enhance our products
Company policy is on-site with 1/2 workday from home depending on your location.
-
Long story short I have written my own custom HTML template system that I am very happy with. However I have done that with manually concatenating HTML tags, and it is a bit messy. There has to be a perl library that will help you build a static HTML document and then output the result. But when I google for a solution I get plenty of options about parsing HTML documents, using templates to generate HTML, or frameworks to generate HTML pages dynamically, but I can't seem to find a perl library that you can generate an HTML document with. Does anybody have any suggestions?
In part 1, we had created a GET endpoint for the backend with static data. We will now move to connect the backend API to database, so we…
In some perl code I'm evaluating $v1 $op $v2 to make sure the result is valid and the program does not abort.
However when $op eq '/' and $v2 == 0 I noticed that a user-defined die handler is called (which was not expected).
Without a user-defined die handler, the usual die message is suppressed, however.
I reproduced like this
DB<5> $v1=1
DB<6> $v2=0
DB<12> x $r = eval { $v1 / $v2 }, $r, $@
0 undef
1 undef
2 'Illegal division by zero at (eval 17)[/usr/lib/perl5/5.26.1/perl5db.pl:738] line 2.
'
DB<17> $SIG{__DIE__} = sub { print "Oops!\n" }
DB<18> x $r = eval { $v1 / $v2 }, $r, $@
Oops!
0 undef
1 undef
2 'Illegal division by zero at (eval 23)[/usr/lib/perl5/5.26.1/perl5db.pl:738] line 2.
'
So why is the user-defined die handler called from within eval? Without such handler Perl seems to suppress calling the default die handler, so why doesn't it do so for a user-defined die handler (defined outside the eval block)?
Seen in perl-5.26.1,
PWC 348 Task 1: String Alike
The Task
You are given a string of even length. Write a script to find out whether the given string can be split into two halves of equal lengths, each with the same non-zero number of vowels.
-
Example 1:
-
Input:
$str = "textbook"text | book (1, 2) - Output: false
-
Input:
-
Example 2:
-
Input:
$str = "book"bo | ok (1, 1) - Output: true
-
Input:
-
Example 3:
-
Input:
$str = "AbCdEfGh"AbCd | EfGh (1, 1) - Output: true
-
Input:
-
Example 4:
-
Input:
$str = "rhythmmyth"rhyth | mmyth (0, 0) - Output: false
-
Input:
-
Example 5:
-
Input:
$str = "UmpireeAudio"Umpire | eAudio (3, 5) - Output: false
-
Input:
Thoughts
Two steps are required: dividing the string in half, and counting vowels in each half. Let's do the obvious thing with substr to get the halves, and we can exploit the quirks of tr to count.
The examples give us a couple of important test cases: the string can contain uppercase and lowercase; and the string might not have any vowels at all. Also notice that vowels are strictly "aeiou" -- "y" is not counted as a vowel.
Code
sub strAlike($str)
{
$str = lc($str);
my $mid = length($str) / 2;
my @count = map { $_ =~ tr/aeiou// }
substr($str, 0, $mid), substr($str, $mid);
return $count[0] == $count[1] && $count[0] > 0;
}
Notes:
-
lc($str)-- eliminate uppercase letters -
$mid = ...-- assuming even length given, as specified -
substr($str, 0, mid)-- the left half -
substr($str, $mid)-- the right half (third argument not needed). The twosubstrcalls result in a list of two strings, which we will pass to ... -
@count = map { }-- count vowels in each substring and assign into an array of vowel counts -
$_ =~ tr/aeiou//-- Ah,tr. A quirk oftris that it returns a count of replacements made. Replace all the vowels of interest, and there's our count. Another quirk oftris that the first argument is not a regular expression, so there are no brackets for character class here. Note the literal use ofaeiou. Yet another quirk oftris that there is no variable interpolation, so it would be awkward to try to parameterize this. -
return ...-- The description specifies non-zero counts, so the extra condition is needed to make Example 4 work.
PWC 348 Task 2: Convert Time
The Task
You are given two strings, $source and $target, containing time in 24-hour time form. Write a script to convert the source into target by performing one of the following operations:
- Add 1 minute
- Add 5 minutes
- Add 15 minutes
- Add 60 minutes
Find the total operations needed to get to the target.
-
Example 1:
-
Input:
$source = "02:30"$target = "02:45" -
Output:
1(Add 15 minutes)
-
Input:
-
Example 2:
-
Input:
$source = "11:55"$target = "12:15" -
Output:
2(Add 15 minutes, Add 5 minutes)
-
Input:
-
Example 3:
-
Input:
$source = "09:00"$target = "13:00" -
Output:
4(Add 60 minutes four times)
-
Input:
-
Example 4:
-
Input:
$source = "23:45"$target = "00:30" -
Output:
3(Add 15 minutes three times)
-
Input:
-
Example 5:
-
Input:
$source = "14:20"$target = "15:25" -
Output:
2(Add 60 minutes, Add 5 minutes)
-
Input:
Thoughts
This is a variation on the problem of making change from a set of coins, or finding the combination of stamps to pay postage.
We need to find the difference in minutes from $source to $target. Example 4 shows that we might have to cross midnight.
I could take the hours and minutes apart and do the math. But I think I'd rather use well-tested modules, so I'll use Time::Piece to parse the times and find the difference.
The operation count will be taking the biggest number of chunks possible, so in reverse order: 60, 15, 5, 1.
Code
sub convert($source, $target)
{
use Time::Piece;
my $s = Time::Piece->strptime($source, "%H:%M");
my $t = Time::Piece->strptime($target, "%H:%M");
my $min = ($t - $s)->minutes;
if ( $min < 0 ) { $min += 24*60; }
my $count = 0;
for my $period ( 60, 15, 5, 1 )
{
$count += int($min / $period);
$min %= $period;
}
return $count;
}
Notes:
-
strptime()-- parse the time strings and create aTime::Pieceobject. Without any day information, this will be a time on January 1, 1970, but we don't care. -
($t-$s)->minutes--Time::Pieceobjects can be subtracted to get a duration. That duration is actually aTime::Secondsobject, which has a method that returns the duration in minutes. -
if ( $min < 0 )-- This covers the case where$targetis after midnight. We'll add 24 hours of minutes to wrap it around back to positive. - The rest is math. Reduce by hours, then quarter-hours, then 5-minute blocks. Since the last block is 1 minute, this will always eventually terminate.
I needed to have some defaults available in my i3 configuration and was using LightDM. I asked in the i3 github discussion pages if people knew why it was failing. It appears Debian stripped some functionality. So how do you solve this?
Answer
You want to have your own session wrapper for lightdm. I stole this recipe from Ubuntu:
#!/usr/bin/sh
for file in "/etc/profile" "$HOME/.profile" "/etc/xprofile" "$HOME/.xprofile"; do
[ ! -f "$file" ] && continue
. $file
done
/etc/X11/Xsession $@
I install this in /usr/local/bin/lightdm-session. And then dpkg-divert the
Debian version of lightdm.conf:
Just Paul and Leon this week. We discussed:
- The newly-added
signature_named_parametersexperiment needs adding toperlexperiment.pod, andexperimental.pm. Hopefully before 5.43.5 release on Thursday - The
experimentaldist would be easier to manage if it lived indist/, but it would also be nice if we had more automated tooling to create real CPAN distribution tarballs out of those directories. Would help for makingModule-Corelistupdates every month as well. - We await a full write-up of a PPC document following on from the “refalias in signatures” Pre-PPC discussion
This is the weekly favourites list of CPAN distributions. Votes count: 25
This week there isn't any remarkable distribution
Build date: 2025/11/16 11:03:31 GMT
Clicked for first time:
- App::jsonvalidate - App harness for the jsonvalidate CLI
- Bitcoin::Secp256k1 - Perl interface to libsecp256k1
- minion::task - A task boilerplate for Minion
- Pod::Abstract - Abstract document tree for Perl POD documents
Increasing its reputation:
- BioX::Seq (+1=3)
- Bitcoin::Crypto (+1=8)
- CGI::Tiny (+1=9)
- Dancer2 (+1=139)
- DBD::DuckDB (+1=6)
- Devel::MAT (+1=30)
- File::Slurp (+1=78)
- Git::Repository (+1=27)
- IO::Compress (+1=19)
- mojo::debugbar (+1=2)
- mojo::util::collection (+1=2)
- Mojolicious::Plugin::Debugbar (+1=2)
- Net::OpenSSH (+1=43)
- Perl::Critic (+1=134)
- Perl::Tidy (+1=146)
- Pod::Parser (+1=14)
- Readonly (+1=24)
- Scalar::List::Utils (+1=183)
- Set::Object (+1=13)
- Task::Kensho (+1=121)
- Time::Piece (+1=65)
Get them from the usual place.
And no, I have still not had time to update CPAN::MetaCustodian so that it properly parses these wikis. But that time is approaching...
-
App::cpm - a fast CPAN module installer
- Version: 0.998001 on 2025-11-13, with 176 votes
- Previous CPAN version: 0.998000 was 5 days before
- Author: SKAJI
-
App::Netdisco - An open source web-based network management tool.
- Version: 2.095001 on 2025-11-15, with 794 votes
- Previous CPAN version: 2.095000
- Author: OLIVER
-
App::Rakubrew - Raku environment manager
- Version: 45 on 2025-11-13, with 28 votes
- Previous CPAN version: 44 was 2 days before
- Author: PATRICKB
-
Bitcoin::Crypto - Bitcoin cryptography in Perl
- Version: 4.002 on 2025-11-14, with 14 votes
- Previous CPAN version: 4.001 was 2 days before
- Author: BRTASTIC
-
CPANSA::DB - the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit
- Version: 20251116.001 on 2025-11-16, with 25 votes
- Previous CPAN version: 20251109.001 was 6 days before
- Author: BRIANDFOY
-
Dist::Zilla - distribution builder; installer not included!
- Version: 6.036 on 2025-11-09, with 189 votes
- Previous CPAN version: 6.035 was before
- Author: RJBS
-
JSON::Schema::Modern - Validate data against a schema using a JSON Schema
- Version: 0.622 on 2025-11-08, with 12 votes
- Previous CPAN version: 0.621 was 9 days before
- Author: ETHER
-
Net::SIP - Framework SIP (Voice Over IP, RFC3261)
- Version: 0.840 on 2025-11-10, with 16 votes
- Previous CPAN version: 0.839 was 2 months, 5 days before
- Author: SULLR
-
SPVM - The SPVM Language
- Version: 0.990106 on 2025-11-11, with 36 votes
- Previous CPAN version: 0.990105 was 26 days before
- Author: KIMOTO
-
Test::Simple - Basic utilities for writing tests.
- Version: 1.302216 on 2025-11-16, with 199 votes
- Previous CPAN version: 1.302215 was 2 days before
- Author: EXODIST
-
Time::Piece - Object Oriented time objects
- Version: 1.41 on 2025-11-12, with 65 votes
- Previous CPAN version: 1.40 was 4 days before
- Author: ESAYM
-
Workflow - Simple, flexible system to implement workflows
- Version: 2.08 on 2025-11-12, with 34 votes
- Previous CPAN version: 2.07 was 4 days before
- Author: JONASBN
The Perl and Raku Foundation has announced a £1,000 sponsorship of the upcoming London Perl and Raku Workshop, reinforcing its ongoing commitment to supporting community-driven technical events. The workshop, one of the longest-running grassroots Perl gatherings in the UK, brings together developers, educators, and open-source enthusiasts for a day of talks, hands-on sessions, and collaborative learning centered on Perl, Raku, and related technologies.
The foundation’s contribution will help cover venue expenses, accessibility measures, and attendee resources. Organizers intend to use the support received from sponsros to keep the event free to attend, maintaining its tradition of lowering barriers for both newcomers and experienced programmers.
This year’s workshop is expected to feature a broad program, including presentations on language internals, modern development practices, and applied use cases within industry and research. Community members from across Europe are anticipated to participate, reflecting the workshop’s reputation as a focal point for Perl activity. The workshop is scheduled for November 29, 2025, in the heart of London at ISH Venues, near Regent's Park. Several speakers are already confirmed for this year's workshop, including Stevan Little and TPRF White Camel Award winners Sawyer X and Mohammad Sajid Anwar. For more information about the event, visit https://www.londonperlworkshop.com/.
By backing the event, The Perl and Raku Foundation continues its broader mission to foster growth, education, and innovation across both language communities. The London Perl Workshop remains one of the foundation’s key community touchpoints, offering a collaborative space for developers to share knowledge and help shape the future of the languages.

For all DevOps enthusiasts, here is a quick introduction to Grafana and Prometheus.
Please check out the link for more information:
https://theweeklychallenge.org/blog/grafana-prometheus
Go Ahead ‘make’ My Day (Part III)
This is the last in a 3 part series on Scriptlets. You can catch up by reading our introduction and dissection of Scriptlets.
In this final part, we talk about restraint - the discipline that keeps a clever trick from turning into a maintenance hazard.
That uneasy feeling…
So you are starting to write a few scriptlets and it seems pretty cool. But something doesn’t feel quite right…
You’re editing a Makefile and suddenly you feel anxious. Ah, you
expected syntax highlighting, linting, proper indentation, and maybe
that warm blanket of static analysis. So when we drop a 20 - line
chunk of Perl or Python into our Makefile, our inner OCD alarms go
off. No highlighting. No linting. Just raw text.
The discomfort isn’t a flaw - it’s feedback. It tells you when you’ve added too much salt to the soup.
A scriptlet is not a script!
A scriptlet is a small, focused snippet of code embedded inside a
Makefile that performs one job quickly and
deterministically. The “-let” suffix matters. It’s not a standalone
program. It’s a helper function, a convenience, a single brushstroke
that belongs in the same canvas as the build logic it supports.
If you ever feel the urge to bite your nails, pick at your skin, or start counting the spaces in your indentation - stop. You’ve crossed the line. What you’ve written is no longer a scriptlet; it’s a script. Give it a real file, a shebang, and a test harness. Keep the build clean.
Why we use them
Scriptlets shine where proximity and simplicity matter more than reuse
(not that we can’t throw it in a separate file and include it in our
Makefile).
- Cleanliness: prevents a recipe from looking like a shell script.
- Locality: live where they’re used. No path lookups, no installs.
- Determinism: transform well-defined input into output. Nothing more.
- Portability (of the idea): every CI/CD system that can run
makecan run a one-liner.
A Makefile that can generate its own dependency file, extract version
numbers, or rewrite a cpanfile doesn’t need a constellation of helper
scripts. It just needs a few lines of inline glue.
Why they’re sometimes painful
We lose the comforts that make us feel like professional developers:
- No syntax highlighting.
- No linting or type hints.
- No indentation guides.
- No “Format on Save.”
The trick is to accept that pain as a necessary check on the limits of
the scriptlet. If you’re constantly wishing for linting and editor
help, it’s your subconscious telling you: this doesn’t belong inline
anymore. You’ve outgrown the -let.
When to promote your scriplet to a script…
Promote a scriptlet to a full-blown script when:
- It exceeds 30-50 lines.
- It gains conditionals or error handling.
- You need to test it independently.
- It uses more than 1 or 2 non-core features.
- It’s used by more than one target or project.
- You’re debugging quoting more than logic.
- You’re spending more time fixing indentation, than working on the build
At that point, you’re writing software, not glue. Give it a name, a
shebang, and a home in your tools/ directory.
When to keep it inside your Makefile…
Keep it inline when:
- It’s short, pure, and single-use.
- It depends primarily on the environment already assumed by your build (Perl, Python, awk, etc.).
- It’s faster to read than to reference.
A good scriptlet reads like a make recipe: do this transformation right here, right now.
define create_cpanfile =
while (<STDIN>) {
s/[#].*//; s/^\s+|\s+$//g; next if $_ eq q{};
my ($mod,$v) = split /\s+/, $_, 2;
print qq{requires "$mod", "$v";\n};
}
endef
export s_create_cpanfile = $(value create_cpanfile)
That’s a perfect scriptlet: small, readable, deterministic, and local.
Rule of Thumb: If it fits on one screen, keep it inline. If it scrolls, promote it.
Tools for the OCD developer
If you must relieve the OCD symptoms without promotion of your scriptlet to a script…
- Add a
lint-scriptletstarget:perl -c -e '$(s_create_requires)'checks syntax without running it. - Some editors (Emacs
mmm-mode, Vimpolyglot) can treat marked sections as sub-languages to enable localized language specific editing features. - Use
includeto include a scriptlet into yourMakefile
…however try to resist the urge to over-optimize the tooling. Feeling the uneasiness grow helps identify the boundary between scriptlets and scripts.
You’ve been warned!
Because scriptlets are powerful, flexible, and fast, it’s easy to
reach for them too often or make them the focus of your project. They
start as a cure for friction - a way to express a small transformation
inline - but left unchecked, they can sometimes grow arms and
legs. Before long, your Makefile turns into a Frankenstein monster.
The great philosopher Basho (or at least I think it was him) once said:
A single aspirin tablet eases pain. A whole bottle sends you to the hospital.
Thanks for reading.
Learn More
For years, most of my Perl web apps lived happily enough on a VPS. I had full control of the box, I could install whatever I liked, and I knew where everything lived.
In fact, over the last eighteen months or so, I wrote a series of blog posts explaining how I developed a system for deploying Dancer2 apps and, eventually, controlling them using systemd. I’m slightly embarrassed by those posts now.
Because the control that my VPS gave me also came with a price: I also had to worry about OS upgrades, SSL renewals, kernel updates, and the occasional morning waking up to automatic notifications that one of my apps had been offline since midnight.
Back in 2019, I started writing a series of blog posts called Into the Cloud that would follow my progress as I moved all my apps into Docker containers. But real life intruded and I never made much progress on the project.
Recently, I returned to this idea (yes, I’m at least five years late here!) I’ve been working on migrating those old Dancer2 applications from my IONOS VPS to Google Cloud Run. The difference has been amazing. My apps now run in their own containers, scale automatically, and the server infrastructure requires almost no maintenance.
This post walks through how I made the jump – and how you can too – using Perl, Dancer2, Docker, GitHub Actions, and Google Cloud Run.
Why move away from a VPS?
Running everything on a single VPS used to make sense. You could ssh in, restart services, and feel like you were in control. But over time, the drawbacks grow:
-
You have to maintain the OS and packages yourself.
-
One bad app or memory leak can affect everything else.
-
You’re paying for full-time CPU and RAM even when nothing’s happening.
-
Scaling means provisioning a new server — not something you do in a coffee break.
Cloud Run, on the other hand, runs each app as a container and only charges you while requests are being served. When no-one’s using your app, it scales to zero and costs nothing.
Even better: no servers to patch, no ports to open, no SSL certificates to renew — Google does all of that for you.
What we’ll build
Here’s the plan. We’ll take a simple Dancer2 app and:
-
Package it as a Docker container.
-
Build that container automatically in GitHub Actions.
-
Deploy it to Google Cloud Run, where it runs securely and scales automatically.
-
Map a custom domain to it and forget about server admin forever.
If you’ve never touched Docker or Cloud Run before, don’t worry – I’ll explain what’s going on as we go.
Why Cloud Run fits Perl surprisingly well
Perl’s ecosystem has always valued stability and control. Containers give you both: you can lock in a Perl version, CPAN modules, and any shared libraries your app needs. The image you build today will still work next year.
Cloud Run runs those containers on demand. It’s effectively a managed starman farm where Google handles the hard parts – scaling, routing, and HTTPS.
You pay for CPU and memory per request, not per server. For small or moderate-traffic Perl apps, it’s often well under £1/month.
Step 1: Dockerising a Dancer2 app
If you’re new to Docker, think of it as a way of bundling your whole environment — Perl, modules, and configuration — into a portable image. It’s like freezing a working copy of your app so it can run identically anywhere.
Here’s a minimal Dockerfile for a Dancer2 app:
-
FROM perl:5.42— starts from an official Perl image on Docker Hub. -
Cartonkeeps dependencies consistent between environments. -
The app is copied into
/app, andcarton install --deploymentinstalls exactly what’s in yourcpanfile.snapshot. -
The container exposes port 8080 (Cloud Run’s default).
-
The
CMDruns Starman, serving your Dancer2 app.
To test it locally:
Then visit http://localhost:8080. If you see your Dancer2 homepage, you’ve successfully containerised your app.
Step 2: Building the image in GitHub Actions
Once it works locally, we can automate it. GitHub Actions will build and push our image to Google Artifact Registry whenever we push to main or tag a release.
Here’s a simplified workflow file (.github/workflows/build.yml):
Once that’s set up, every push builds a fresh, versioned container image.
Step 3: Deploying to Cloud Run
Now we’re ready to run it in the cloud. We’ll do that using Google’s command line program, gcloud. It’s available from Google’s official downloads or through most Linux package managers — for example:
# Fedora, RedHat or similar sudo dnf install google-cloud-cli # or on Debian/Ubuntu: sudo apt install google-cloud-cli
Once installed, authenticate it with your Google account:
Once that’s done, you can deploy manually from the command line:
This tells Cloud Run to start a new service called myapp, using the image we just built.
After a minute or two, Google will give you a live HTTPS URL, like:
Visit it — and if all went well, you’ll see your familiar Dancer2 app, running happily on Cloud Run.
To connect your own domain, run:
gcloud run domain-mappings create \ --service=myapp \ --domain=myapp.example.com
Then update your DNS records as instructed. Within an hour or so, Cloud Run will issue a free SSL certificate for you.
Step 4: Automating the deployment
Once the manual deployment works, we can automate it too.
Here’s a second GitHub Actions workflow (deploy.yml) that triggers after a successful build:
You can take it further by splitting environments — e.g. main deploys to staging, tagged releases to production — but even this simple setup is a big step forward from ssh and git pull.
Step 5: Environment variables and configuration
Each Cloud Run service can have its own configuration and secrets. You can set these from the console or CLI:
gcloud run services update myapp \ --set-env-vars="DANCER_ENV=production,DATABASE_URL=postgres://..."
In your Dancer2 app, you can then access them with:
$ENV{DATABASE_URL}
It’s a good idea to keep database credentials and API keys out of your code and inject them at deploy time like this.
Step 6: Monitoring and logs
Cloud Run integrates neatly with Google Cloud’s logging tools.
To see recent logs from your app:
If you prefer a UI, you can use the Cloud Console’s Log Explorer to filter by service or severity.
Step 7: The payoff
Once you’ve done one migration, the next becomes almost trivial. Each Dancer2 app gets:
-
Its own Dockerfile and GitHub workflows.
-
Its own Cloud Run service and domain.
-
Its own scaling and logging.
And none of them share a single byte of RAM with each other.
Here’s how the experience compares:
| Aspect | Old VPS | Cloud Run |
|---|---|---|
| OS maintenance | Manual upgrades | Managed |
| Scaling | Fixed size | Automatic |
| SSL | Let’s Encrypt renewals | Automatic |
| Deployment | SSH + git pull | Push to GitHub |
| Cost | Fixed monthly | Pay-per-request |
| Downtime risk | One app can crash all | Each isolated |
For small apps with light traffic, Cloud Run often costs pennies per month – less than the price of a coffee for peace of mind.
Lessons learned
After a few migrations, a few patterns emerged:
-
Keep apps self-contained. Don’t share config or code across services; treat each app as a unit.
-
Use digest-based deploys. Deploy by image digest (
@sha256:...) rather than tag for true immutability. -
Logs are your friend. Cloud Run’s logs are rich; you rarely need to
sshanywhere again. -
Cold starts exist, but aren’t scary. If your app is infrequently used, expect the first request after a while to take a second longer.
-
CI/CD is liberating. Once the pipeline’s in place, deployment becomes a non-event.
Costs and practicalities
One of the most pleasant surprises was the cost. My smallest Dancer2 app, which only gets a handful of requests each day, usually costs under £0.50/month on Cloud Run. Heavier ones rarely top a few pounds.
Compare that to the £10–£15/month I was paying for the old VPS — and the VPS didn’t scale, didn’t auto-restart cleanly, and didn’t come with HTTPS certificates for free.
What’s next
This post covers the essentials: containerising a Dancer2 app and deploying it to Cloud Run via GitHub Actions.
In future articles, I’ll look at:
-
Connecting to persistent databases.
-
Using caching.
-
Adding monitoring and dashboards.
-
Managing secrets with Google Secret Manager.
Conclusion
After two decades of running Perl web apps on traditional servers, Cloud Run feels like the future has finally caught up with me.
You still get to write your code in Dancer2 – the framework that’s made Perl web development fun for years – but you deploy it in a way that’s modern, repeatable, and blissfully low-maintenance.
No more patching kernels. No more 3 a.m. alerts. Just code, commit, and dance in the clouds.
The post Dancing in the Clouds: Moving Dancer2 Apps from a VPS to Cloud Run first appeared on Perl Hacks.
Go Ahead ‘make’ My Day (Part II)
In our previous blog post “Go Ahead ‘make’ My
Day” we
presented the scriptlet, an advanced make technique for spicing up
your Makefile recipes. In this follow-up, we’ll deconstruct the
scriptlet and detail the ingredients that make up the secret sauce.
Introducing the Scriptlet
Makefile scriptlets are an advanced technique that uses
GNU make’s powerful functions to safely embed a multi-line script
(Perl, in our example) into a single, clean shell command. It turns a
complex block of logic into an easily executable template.
An Example Scriptlet
#-*- mode: makefile; -*-
DARKPAN_TEMPLATE="https://cpan.openbedrock.net/orepan2/authors/D/DU/DUMMY/%s-%s.tar.gz"
define create_requires =
# scriptlet to create cpanfile from an list of required Perl modules
# skip comments
my $DARKPAN_TEMPLATE=$ENV{DARKPAN_TEMPLATE};
while (s/^#[^\n]+\n//g){};
# skip blank lines
while (s/\n\n/\n/) {};
for (split/\n/) {
my ($mod, $v) = split /\s+/;
next if !$mod;
my $dist = $mod;
$dist =~s/::/\-/g;
my $url = sprintf $DARKPAN_TEMPLATE, $dist, $v;
print <<"EOF";
requires \"$mod\", \"$v\",
url => \"$url\";
EOF
}
endef
export s_create_requires = $(value create_requires)
cpanfile.darkpan: requires.darkpan
DARKPAN_TEMPLATE=$(DARKPAN_TEMPLATE); \
DARKPAN_TEMPLATE=$$DARKPAN_TEMPLATE perl -0ne "$$s_create_requires" $< > $@ || rm $@
Dissecting the Scriptlet
1. The Container: Defining the Script (define / endef)
This section creates the multi-line variable that holds your entire Perl program.
define create_requires =
# Perl code here...
endef
define ... endef: This is GNU Make’s mechanism for defining a recursively expanded variable that spans multiple lines. The content is not processed by the shell yet; it’s simply stored bymake.- The Advantage: This is the only clean way to write readable,
indented code (like your
whileloop andifstatements) directly inside aMakefile.
2. The Bridge: Passing Environment Data (my $ENV{...})
This is a critical step for making your script template portable and configurable.
my $DARKPAN_TEMPLATE=$ENV{DARKPAN_TEMPLATE};
- The Problem: Your Perl script needs dynamic values (like the
template URL) that are set by
make. - The Solution: Instead of hardcoding the URL, the Perl code is
designed to read from the shell environment variable
$ENV{DARKPAN_TEMPLATE}. This makes the script agnostic to its calling environment, delegating the data management back to theMakefile.
3. The Transformer: Shell Preparation (export and $(value))
This is the “magic” that turns the multi-line Make variable into a single, clean shell command.
export s_create_requires = $(value create_requires)
$(value create_requires): This is a specific Make function that performs a direct, single-pass expansion of the variable’s raw content. Crucially, it converts the entire multi-line block into a single-line string suitable for export, preserving special characters and line breaks that the shell will execute.export s_create_requires = ...: This exports the multi-line Perl script content as an environment variable (s_create_requires) that will be accessible to any shell process running in the recipe’s environment.
4. The Execution: Atomic Execution ($$ and perl -0ne)
The final recipe executes the entire, complex process as a single, atomic operation, which is the goal of robust Makefiles.
cpanfile.darkpan: requires.darkpan
DARKPAN_TEMPLATE=$(DARKPAN_TEMPLATE); \
DARKPAN_TEMPLATE=$$DARKPAN_TEMPLATE perl -0ne "$$s_create_requires" $< > $@ || rm $@
DARKPAN_TEMPLATE=$(DARKPAN_TEMPLATE): This creates the local shell variable.DARKPAN_TEMPLATE=$$DARKPAN_TEMPLATE perl...: This is the clean execution. The firstDARKPAN_TEMPLATE=passes the newly created shell variable’s value as an environment variable to theperlprocess. The$$ensures the shell variable is properly expanded before the Perl interpreter runs it.perl -0ne "...": Runs the Perl script:* `-n` and `-e` (Execute script on input) * `-0`: Tells Perl to read the input as one single block (slurping the file), which is necessary for your multi-line regex and `split/\n/` logic.|| rm $@: This is the final mark of quality. It makes the entire command transactional—if the Perl script fails, the half-written target file ($@) is deleted, forcingmaketo try again later.
Hey Now! You’re a Rockstar!
(..get your game on!)
Mastering build automation using make will transform you from
being an average DevOps engineer into a rockstar. GNU make is a Swiss
Army knife with more tools than you might think! The knives are sharp
and the tools are highly targeted to handle all the real-world issues
build automation has encountered over the decades. Learning to use
make effectively will put you head and shoulders above the herd (see
what I did there? 😉).
Calling All Pythonistas!
The scriptlet technique creates a powerful, universal pattern for clean, atomic builds:
- It’s Language Agnostic: Pythonistas! Join the fun! The same
define/exporttechnique works perfectly withpython -c. - The Win: This ensures that every developer - regardless of their preferred language - can achieve the same clean, atomic build and avoid external script chaos.
Learn more about GNU
make
and move your Makefiles from simple shell commands to precision
instruments of automation.
Thanks for reading.
Learn More
A long time ago I used Shutter and found it as an excellent tool. Now I get all kinds of crashes.
Actually "Now" was a while ago, since then I upgraded Ubuntu and now I get all kinds of other error messages.
However, I wonder.
Why are there so many errors?
Who's fault is it?
-
A failure of the Perl community?
-
A failure of the Ubuntu or the Debian developers?
-
A failure of the whole idea of Open Source?
-
Maybe I broke the system?
It starts so badly and then it crashes. I don't want to spend time figuring out what is the problem. I don't even have the energy to open a ticket. I am not even sure where should I do it. On Ubuntu? On the Shutter project?
Here is the output:
$ shutter
Subroutine Pango::Layout::set_text redefined at /usr/share/perl5/Gtk3.pm line 2299.
require Gtk3.pm called at /usr/bin/shutter line 72
Shutter::App::BEGIN() called at /usr/bin/shutter line 72
eval {...} called at /usr/bin/shutter line 72
Subroutine Pango::Layout::set_markup redefined at /usr/share/perl5/Gtk3.pm line 2305.
require Gtk3.pm called at /usr/bin/shutter line 72
Shutter::App::BEGIN() called at /usr/bin/shutter line 72
eval {...} called at /usr/bin/shutter line 72
GLib-GObject-CRITICAL **: g_boxed_type_register_static: assertion 'g_type_from_name (name) == 0' failed at /usr/lib/x86_64-linux-gnu/perl5/5.36/Glib/Object/Introspection.pm line 110.
at /usr/share/perl5/Gtk3.pm line 489.
Gtk3::import("Gtk3", "-init") called at /usr/bin/shutter line 72
Shutter::App::BEGIN() called at /usr/bin/shutter line 72
eval {...} called at /usr/bin/shutter line 72
GLib-CRITICAL **: g_once_init_leave: assertion 'result != 0' failed at /usr/lib/x86_64-linux-gnu/perl5/5.36/Glib/Object/Introspection.pm line 110.
at /usr/share/perl5/Gtk3.pm line 489.
Gtk3::import("Gtk3", "-init") called at /usr/bin/shutter line 72
Shutter::App::BEGIN() called at /usr/bin/shutter line 72
eval {...} called at /usr/bin/shutter line 72
GLib-GObject-CRITICAL **: g_boxed_type_register_static: assertion 'g_type_from_name (name) == 0' failed at /usr/lib/x86_64-linux-gnu/perl5/5.36/Glib/Object/Introspection.pm line 110.
at /usr/share/perl5/Gtk3.pm line 489.
Gtk3::import("Gtk3", "-init") called at /usr/bin/shutter line 72
Shutter::App::BEGIN() called at /usr/bin/shutter line 72
eval {...} called at /usr/bin/shutter line 72
GLib-CRITICAL **: g_once_init_leave: assertion 'result != 0' failed at /usr/lib/x86_64-linux-gnu/perl5/5.36/Glib/Object/Introspection.pm line 110.
at /usr/share/perl5/Gtk3.pm line 489.
Gtk3::import("Gtk3", "-init") called at /usr/bin/shutter line 72
Shutter::App::BEGIN() called at /usr/bin/shutter line 72
eval {...} called at /usr/bin/shutter line 72
GLib-GObject-CRITICAL **: g_boxed_type_register_static: assertion 'g_type_from_name (name) == 0' failed at /usr/lib/x86_64-linux-gnu/perl5/5.36/Glib/Object/Introspection.pm line 110.
at /usr/share/perl5/Gtk3.pm line 489.
Gtk3::import("Gtk3", "-init") called at /usr/bin/shutter line 72
Shutter::App::BEGIN() called at /usr/bin/shutter line 72
eval {...} called at /usr/bin/shutter line 72
GLib-CRITICAL **: g_once_init_leave: assertion 'result != 0' failed at /usr/lib/x86_64-linux-gnu/perl5/5.36/Glib/Object/Introspection.pm line 110.
at /usr/share/perl5/Gtk3.pm line 489.
Gtk3::import("Gtk3", "-init") called at /usr/bin/shutter line 72
Shutter::App::BEGIN() called at /usr/bin/shutter line 72
eval {...} called at /usr/bin/shutter line 72
Variable "$progname_active" will not stay shared at /usr/bin/shutter line 2778.
Variable "$progname" will not stay shared at /usr/bin/shutter line 2779.
Variable "$im_colors_active" will not stay shared at /usr/bin/shutter line 2787.
Variable "$combobox_im_colors" will not stay shared at /usr/bin/shutter line 2788.
Variable "$trans_check" will not stay shared at /usr/bin/shutter line 2798.
... About 700 similar error messages ...
Name "Gtk3::Gdk::SELECTION_CLIPBOARD" used only once: possible typo at /usr/bin/shutter line 291.
WARNING: gnome-web-photo is missing --> screenshots of websites will be disabled!
at /usr/bin/shutter line 9038.
Shutter::App::fct_init_depend() called at /usr/bin/shutter line 195
Useless use of hash element in void context at /usr/share/perl5/Shutter/App/Common.pm line 77.
require Shutter/App/Common.pm called at /usr/bin/shutter line 206
Useless use of hash element in void context at /usr/share/perl5/Shutter/App/Common.pm line 80.
require Shutter/App/Common.pm called at /usr/bin/shutter line 206
Subroutine lookup redefined at /usr/share/perl5/Shutter/Draw/DrawingTool.pm line 28.
require Shutter/Draw/DrawingTool.pm called at /usr/bin/shutter line 228
Variable "$self" will not stay shared at /usr/share/perl5/Shutter/Draw/DrawingTool.pm line 671.
require Shutter/Draw/DrawingTool.pm called at /usr/bin/shutter line 228
Variable "$self" will not stay shared at /usr/share/perl5/Shutter/Screenshot/SelectorAdvanced.pm line 840.
require Shutter/Screenshot/SelectorAdvanced.pm called at /usr/bin/shutter line 233
Failed to register: GDBus.Error:org.freedesktop.DBus.Error.NoReply: Message recipient disconnected from message bus without replying
We have 7 high quality and exciting talks, we're looking for many more - as many as we can back into 2 days. Virtual presentations are accepted.
![]()
- **Sign up to attend**
- **Submit Talk** (virtual talks permitted!)
Notes from the live-coding session (part of the Perl Maven live events.
-
SVG the module for which we wrote tests.
-
Devl-Cover to generate test coverage report run
cover -test. -
done_testing()ordone_testing(2)
Meeting summary
Quick recap
The meeting began with informal introductions and discussions about Perl programming, including experiences with maintaining Perl codebases and the challenges of the language's syntax. The main technical focus was on testing and code coverage, with detailed demonstrations of using Devel::Cover and various testing modules in Perl, including examples of testing SVG functionality and handling exceptions. The session concluded with discussions about testing practices, code coverage implementation, and the benefits of automated testing, while also touching on practical aspects of Perl's object-oriented programming and error handling features.
SVG Test Coverage Analysis
Gabor demonstrated how to use Devel::Cover to generate test coverage reports for the SVG.pm module. He showed that the main module has 98% coverage, while some submodules have lower coverage. Gabor explained how to interpret the coverage reports, including statement, branch, and condition coverage. He also discussed the importance of identifying and removing unused code that appears uncovered by tests. Gabor then walked through some example tests in the SVG distribution, explaining how they verify different aspects of the SVG module's functionality.
Original announcement
Adding tests to legacy Perl code
During this live coding event we'll take a Perl module from CPAN and add some tests to it.
Further events
Register on the Perl Maven Luma calendar.

Tony writes:
``` [Hours] [Activity] 2025/10/02 Thursday 1.03 #23782 testing, comments 0.23 #23794 review change, research and comment 0.32 #23787 review and approve 0.27 #23777 review, research and comment 0.17 #23775 review and comment
0.48 #23608 research and comment
2.50
2025/10/03 Friday 1.30 #21877 code review, find another possible bug 0.08 #23787 review updates, has other approval, apply to blead 0.68 #21877 bug report rcatline - #23798 0.08 #23794 review updates and approve 0.08 #16865 follow-up
0.90 #23704 research and comment
3.12
2025/10/06 Monday 0.27 #23728 review and comment 0.60 #23752 review, testing and comment 0.15 #23813 review, but nothing further to say 0.18 #23809 comment 0.18 #21877 write more tests 0.07 #23817 review, got merged as I looked at it 0.22 #23795 start review
1.95 #23795 more review
3.62
2025/10/07 Tuesday 0.60 #23774 review 0.68 #23796 review and approve 0.37 #23797 review 0.08 #23797 finish review and approve 0.25 #23799 review and comment 0.12 #23800 review and approve 0.10 #23801 review and comment 0.17 #23802 comment 0.08 #23752 review and approve
0.60 #23795 more review
3.05
2025/10/08 Wednesday 0.55 #23795 more review 0.72 #23795 more review 0.12 #23782 marked resolved comments resolved
0.08 #23801 review updates and approve
1.47
2025/10/09 Thursday 0.45 #23799 comment 0.08 #23821 review and approve 0.08 #23824 review and approve 0.12 #23827 review, research and approve 0.10 #23820 review, research and comment 0.30 #23812 review, existing comment matches my opinion 0.10 #23805 briefly comment 2.30 #21877 add #23798 tests, testing, more work on re- implementation 1.67 #21877 work on re-implementation
0.55 #21877 more work
5.75
2025/10/10 Friday
0.23 #23828 review discussion and failing message, comment
0.23
2025/10/13 Monday 0.23 #23829 review discussion and comment 0.22 #23833 comment 0.20 #23834 review and approve with comment 0.42 #23837 review and approve 0.30 #23838 review and comment 0.42 #23840 review and comment 0.08 #23843 review and approve 0.15 #23842 review and comment 0.23 #23836 review test failures and comment 0.17 #23841 review discussion, research and comment 0.52 #23676 search for other possible modules, review and comment 0.13 #23833 review and comment 0.47 #21877 more tests, debugging 0.32 #23802 research, comment
1.18 #21877 debugging
5.04
2025/10/14 Tuesday 1.20 #23844 review, comments 0.15 #23845 review and approve 0.33 #21877 debugging 0.72 #21877 debugging, testing
0.67 #21877 debugging
3.07
2025/10/15 Wednesday
0.23 check coverity scan reports
0.23
2025/10/16 Thursday 0.55 #23847 review, #p5p discussion re 5.42.1, approve 0.53 #23851 review, research and comment, more maint-5.42 discussion 0.98 #23850 review, comments 0.53 #23852 research and comment
0.37 maint votes: vote and look for anything else
2.96
2025/10/20 Monday 0.35 #23840 review updates and approve 0.78 #23838 review updates, review build logs, comments 0.33 #23833 investigate build errors and warnings, restart mingw64 CI 0.08 #23818 review updates and approve 0.28 #23851 research and comments
1.97 #23795 more review
3.79
2025/10/21 Tuesday 0.12 #23838 check updates, restart failed CI job 0.38 #23853 review, research and comment 0.62 #23865 review, coverage testing and approve 1.13 #23858 review, testing, comments 0.08 #23838 check CI results and approve 1.07 #23795 more review and let dave know I’m done for now 1.08 #23852 work on re-working docs, research equivalence of
sigprocmask and pthread_sigmask, comment
4.48
2025/10/22 Wednesday 0.27 #23858 review updates and conditionally approve 0.27 #23868 review and approve 0.47 update t/test_pl.pod with the new PREAMBLE directive PR 23869 1.43 #23782 try to understand the code, minor change and testing
0.42 #23782 more testing, debugging
2.86
2025/10/23 Thursday
2.78 #23871 review, testing, comments
2.78
2025/10/27 Monday 2.80 #23871 review updates, comment, testing
0.43 #23795 comments
3.23
2025/10/28 Tuesday 0.40 #23879 review changes and research, comment on referred ticket 0.10 #23781 comment 0.08 #23809 briefly comment 0.67 #23867 review 0.45 #23867 comments
1.30 #23872 review
3.00
2025/10/29 Wednesday 1.10 #23782 testing and follow-up
0.53 #23781 re-check
1.63
2025/10/30 Thursday 0.35 #23882 review and comment 1.70 #23873 review, testing and approve 0.33 #23614 comment
1.55 #21877 debugging - fix one issue
3.93
Which I calculate is 56.74 hours.
Approximately 59 tickets were reviewed or worked on, and 1 patches were applied. ```
-
App::cpm - a fast CPAN module installer
- Version: 0.998000 on 2025-11-07, with 176 votes
- Previous CPAN version: 0.997024 was 3 months, 27 days before
- Author: SKAJI
-
App::Netdisco - An open source web-based network management tool.
- Version: 2.094003 on 2025-11-03, with 777 votes
- Previous CPAN version: 2.094002 was 5 days before
- Author: OLIVER
-
CPANSA::DB - the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit
- Version: 20251102.001 on 2025-11-02, with 25 votes
- Previous CPAN version: 20251026.001 was 7 days before
- Author: BRIANDFOY
-
Dist::Zilla - distribution builder; installer not included!
- Version: 6.034 on 2025-11-07, with 189 votes
- Previous CPAN version: 6.033 was 6 months, 5 days before
- Author: RJBS
-
PerlPowerTools - BSD utilities written in pure Perl
- Version: 1.053 on 2025-11-04, with 223 votes
- Previous CPAN version: 1.052 was 3 months, 17 days before
- Author: BRIANDFOY
-
Sys::Virt - libvirt Perl API
- Version: v11.8.0 on 2025-11-07, with 17 votes
- Previous CPAN version: v11.6.0 was 3 months, 3 days before
- Author: DANBERR
-
Test::Fatal - incredibly simple helpers for testing code with exceptions
- Version: 0.018 on 2025-11-06, with 40 votes
- Previous CPAN version: 0.017 was 2 years, 10 months, 5 days before
- Author: RJBS
-
Time::Piece - Object Oriented time objects
- Version: 1.40 on 2025-11-08, with 64 votes
- Previous CPAN version: 1.39 was 14 days before
- Author: ESAYM
-
Workflow - Simple, flexible system to implement workflows
- Version: 2.07 on 2025-11-08, with 34 votes
- Previous CPAN version: 2.06 was 2 months, 26 days before
- Author: JONASBN
This is the weekly favourites list of CPAN distributions. Votes count: 70
Week's winners (+5): DBD::DuckDB
Build date: 2025/11/08 17:43:57 GMT
Clicked for first time:
- App::Test::Generator - Generate fuzz and corpus-driven test harnesses
- Chart::ECharts - Apache ECharts wrapper for Perl
- Cwd::Guard - Temporary changing working directory (chdir)
- Eval::Context - Evalute perl code in context wrapper
- Image::WebP - binding to Google's libwebp.
- JSON::Schema::Validate - Lean, recursion-safe JSON Schema validator (Draft 2020-12)
- LaTeX::Replicase - Perl extension implementing a minimalistic engine for filling real TeX-LaTeX files that act as templates.
- OpenAPI::Linter - Lint and validate OpenAPI specification files
- Plack::Middleware::ConsoleLogger - Write logs to Firebug or Webkit Inspector
- Win32API::Process - Perl extension for handling the processes using the plain Win32 API
Increasing its reputation:
- abbreviation (+1=2)
- Alien (+1=5)
- App::cpanminus (+1=285)
- App::cpm (+1=77)
- App::perlimports (+1=21)
- Const::Fast (+1=36)
- CPAN::Meta (+1=27)
- Crypt::URandom (+1=7)
- Data::Dump (+1=42)
- Data::Password::zxcvbn (+1=8)
- DBD::DuckDB (+4=6)
- Devel::Cycle (+1=17)
- Devel::NYTProf (+1=195)
- Dist::Milla (+1=39)
- Dist::Zilla (+1=188)
- experimentals (+1=5)
- HTML::T5 (+1=2)
- HTTP::BrowserDetect (+1=26)
- HTTP::Message (+1=71)
- import (+1=3)
- Import::Into (+1=39)
- IO::Compress (+1=18)
- JQ::Lite (+3=7)
- Keyword::Declare (+1=24)
- Lexical::Persistence (+1=4)
- libwww::perl (+1=173)
- Mojo::PDF (+1=9)
- Mojo::Reactor::UV (+1=3)
- Mojolicious (+1=509)
- MooseX::Types::Set::Object (+1=2)
- namespace (+1=3)
- PAR::Packer (+1=45)
- Path::Iterator::Rule (+1=25)
- Path::Tiny (+1=193)
- Plack::Middleware::Debug (+1=20)
- Plack::Middleware::ETag (+1=4)
- Plack::Middleware::ReverseProxy (+1=9)
- Regexp::Common::net::CIDR (+1=2)
- Regexp::Debugger (+1=59)
- Reply (+1=61)
- Rex (+1=88)
- RT::Extension::CustomFieldsOnUpdate (+1=4)
- Safe (+1=13)
- strictures (+1=26)
- String::Random (+1=24)
- Syntax::Keyword::Match (+1=14)
- Syntax::Keyword::MultiSub (+1=5)
- Syntax::Keyword::Try (+1=46)
- Test2::Plugin::NoWarnings (+1=4)
- Test2::Plugin::SubtestFilter (+2=2)
- Test::Class (+1=24)
- Test::Simple (+1=199)
- Test::Simpler (+1=4)
- WWW::Mechanize (+1=103)

Paul writes:
The main event from this month has been getting the signature named parameters (PPC0024) branch finalised and merged.
- 4 = Improvements to unit tests around subroutine signatures
- https://github.com/Perl/perl5/pull/23822
- https://github.com/Perl/perl5/pull/23868
- 11 = Signature Named Parameters branch
- https://github.com/Perl/perl5/pull/23871
- 2 = Investigations into possible performance enhancements of pp_multiparam
- https://www.nntp.perl.org/group/perl.perl5.porters/2025/10/msg270428.html
- 1 = Improvements to named parameter error reporting
- https://github.com/Perl/perl5/pull/23888
- 2 = Other github code reviews
Total: 20 hours
My aim for November is to let the dust settle a bit on signature parameters, and turn my attention to either Magic v2 or Attributes v2, aiming to have something that can interact better with signatures in time for the 5.44 release.

