Published by /u/niceperl on Saturday 15 March 2025 23:12
Published by Unknown on Sunday 16 March 2025 00:11
Published by iabyn on Saturday 15 March 2025 14:49
perldelta for year of ExtUtils::ParseXS changes Add a perldelta entry mentioning that ParseXS has been heavily refactored over the last year. I haven't been adding entries for each monthly release as it was a work in progress, and most changes weren't externally visible. It's still a work in progress, but I'm intending to defer any further changes until after the 5.42.0 release, so now seems good time for the perldelta entry.
Have you heard of CPAN module Scientist?
Please find below a gentle introduction.
https://theweeklychallenge.org/blog/scientist-in-perl
Published by /u/Patentsmatter on Saturday 15 March 2025 09:19
Something that comes up in my work is keeping track of certain web pages that provide relevant data without providing an api. Think of it as a "recent news" page, where the text of some older news may change (e.g. replacement of attached files, added information, corrections, whatever). Each news item contains some core information (e.g. the date) and also some items that may or may not be present (e.g. attached files, keywords, etc.). Unfortunately there is no standard of optional items, so these have to be treated as an open-ended list.
I want to read the news page daily and the old news items every 6 months or so.
What would be a good way to compose a scheduler app, and what would be a recommended way to store such data?
My idea was to create an SQLite database table to keep track of the tasks to do:
I'd also envisage a set of database tables for the news items:
Would you create an object that handles both in-memory storage of news items and the methods to store the item in the database or read an item therefrom? Or would you rather separate storage methods from data structures?
Published by mauke on Saturday 15 March 2025 08:49
remove indirect calls to import() method ... i.e. turn `import Foo "bar"` into `Foo->import("bar")`.
Published by mauke on Saturday 15 March 2025 08:46
release_schedule: add Lukas Mai for dev release 5.41.10 <mauke> hydahy, LeoNerd: I'd be interested in doing the 5.41.10 release. (I might need some assistance as I've never released a perl before.) can I jump in? <LeoNerd> mauke: Ohyeah sure.. I can assist on that then
![]() | submitted by /u/oalders [link] [comments] |
Published by mauke on Friday 14 March 2025 18:19
release_schedule: remove Zefram from release engineer backup list RIP Zefram.
Published by leonerd on Friday 14 March 2025 15:54
Rename the 'any' and 'all' features and experimental warnings to 'keyword_any' and 'keyword_all' This avoids confusion with the `use feature ':all'` ability (As requested by https://github.com/Perl/perl5/issues/23104)
Published by Mohammad Sajid Anwar on Friday 14 March 2025 15:37
Ever wanted to extract text from an image in Perl?
Here is my story to do the same.
Enjoy !!!
Published by Powermaster Prime on Friday 14 March 2025 13:57
I haven't asked a question here in a long time, but I need some Perl expert opinions on the matter. I am working on a 2FA system for our ancient Perl based web application.
If you need a "minimal reproducible example", just skip this question and don't waste our time.
The login web page has an AJAX JSON "POST" JS script calling our API.pm script /api/userLogin
The original Userlogin sub is passed the login form credentials, checks the password hash and username, then returns successfully. The return hash is checked and handed off to the Perl CGI module, where the header is fed application/json. $self->header_add(-Content_Type => 'application/json');
Well my 2FA process simply uses this sub to check if a user has 2FA enabled, and returns a QR code, the 2FA type, and a secure session token if they have it.
After the user enters the authentication code on the front end, it calls the login sub again via ajax, except with our secret key instead of a password.
The key gets validated server side via our generated and saved secret code, our login "token" is spent, and we officially pass login.
However, upon return, it just prints out a hash and redirects to https://localhost/testapp/api/userLogin
I can't for the life of me can't find out why my standard AJAX login call returns fine, but the 2FA call returns a sharted out hash and the raw api file path. To add insult to injury, the 2FA process technically works, logs me in, and creates a valid login session via my browser. I can simply go to my correct testapp url, and it's golden. It seems to strictly be redirect related.
I have debugged and printed out the ENTIRE CGI object. You can see that in the second script I posted below. That postrun script essentially constructs and returns the CGI object to the browser. However, both the login AND 2FA header type seem to know its application/json
. Also, there is nothing in the original login process that calls any kind of "redirect", it simply returns the hash and the js ajax success handles that home redirect. So how it even directs to a raw hash and raw file path is beyond me.
sub api_userLogin($)
{
#here
print STDERR "[-----------------------]\n";
print STDERR "api.pm userLogin hit\n";
print STDERR "[-----------------------]\n";
my $self = shift;
my %qparams = $self->get_matched_query_params(qw/_resume authonly pwd u CSRFToken key/);
my $data = {};
print STDERR Dumper(\%qparams);
print STDERR "[-----------------------]\n";
my $protocol = $ENV{'HTTPS'} ? 'https' : 'http'; # Check for HTTPS protocol
my $host = $ENV{'HTTP_HOST'} || ''; # Get the host (domain:por
print STDERR "Here is the host: $host \n";
my $request_uri = $ENV{'REQUEST_URI'} // '';
print STDERR "Here is the uri upon calling userLogin: $request_uri \n";
# Get the User.
my $user = eval{ User::find_user('any', $qparams{'u'}) };
if ( $self->catch_exception() ) {
$data->{'error'} = User::LOGIN_ERROR();
}
my ($type, $keyGen);
if ( !$data->{'error'} ) {
# If we have the user, test the password.
# However, let's check for 2FA first.
if(defined($qparams{'key'}) && $qparams{'key'} ne ''){
print STDERR "api.pm key login triggered\n";
eval{$user->login($qparams{'pwd'}, {'2FA_key' => $qparams{'key'}});}
#set return to
}
else {
eval {($type, $keyGen) = $user->login($qparams{'pwd'}); };
}
if ($self->catch_exception()) {
$data->{'error'} = User::LOGIN_ERROR() unless ($user && $user->logged_in());
$self->logout();
$self->dbh()->commit(); # Required to preserve login lock and eventlog.
die($data->{'error'});
}
#check if passed back 2fa and keygrn
if($type eq '2FA') {
$self->session_recreate();
$self->send_session_cookie();
print STDERR "2FA returning login key to AJAX...\n";
#$data = {'success' => 1, 'twofactor' => '2FA', 'keyGen' => $keyGen, , 'CSRFToken'=>$qparams{'CSRFToken'}, $user->vars_hash([ qw/userid name abbr email companyid/ ])} unless ($data->{'error'});
$data = {'success' => 1, 'twofactor' => '2FA', 'keyGen' => $keyGen, , 'CSRFToken'=>$qparams{'CSRFToken'}} unless ($data->{'error'});
return $data;
}
}
$data = {'success' => 1, $user->vars_hash([ qw/userid name abbr email companyid/ ])} unless ($data->{'error'});
unless ( $qparams{'authonly'} || $data->{'error'} ) {
# Set the session cookie.
print STDERR "setting session cookie\n";
$self->session_recreate();
$self->session->param('userid' => $user->_id());
$self->session->param('uid' => $user->_id());
$self->session->param('abbr' => $user->{'abbr'});
$self->session->param('disclaimer', $user->{'companyabbr'});
if ( $qparams{'_resume'} ) {
print STDERR "qparams _resume triggered in api userlogin process\n";
$data->{'_resume'} = scalar $qparams{'_resume'};
}
else {
if(defined($qparams{'key'}) && $qparams{'key'} ne ''){
print STDERR "[-------------------------------------------------------]\n";
print STDERR "KEY CREDENTIAL DEFINED. Token passed. Returning session...\n";
print STDERR "[-------------------------------------------------------]\n";
$self->send_session_cookie();
# Temp solution was to manually redirect to the origin url.
#$self->header_add(-nph => 1);
#my $tempcgi = CGI::referer();
#return $self->redirect($tempcgi);
return $data;
}
}
$self->send_session_cookie();
}
# Return the JSON data.
print STDERR "returning JSON data\n";
return $data;
}
Here is our cgi_postrun sub that constructs and returns the page to the browser.
sub cgiapp_postrun {
print STDERR "cgiapp_postrun called. \n";
print STDERR "The calling function is: ", (caller 1)[3], "\n";
my ($self,$output_ref) = @_;
my $q = $self->query();
#the output ref is what is getting dumped out here.
#print STDERR "-----------------------------OUTPUT_CGI_DEBUG----------------------------\n";
#print STDERR Dumper($output_ref);
#my $tempcgi = CGI::referer();
#print STDERR Dumper($tempcgi);
#print STDERR "-----------------------------DUMPING HEADER----------------------------\n";
#print STDERR Dumper($self->header());
#print STDERR "-----------------------------DONE DUMPING POSTRUN--------------------------------\n";
eval {
Class::fulfill_all_promises();
};
Class::clear_cache();
if($@) {
$$output_ref = $self->error_handler();
} else {
commit();
}
my $headers = $self->header();
my $content_type = $headers->type() // 'text/html';
my $header_status = defined($headers->status())? pos_integer(substr($headers->status(),0,3)) : 200;
my $header_type = ($header_status == 304)? 'cache' : $self->header_type() // 'none';
#print STDERR "Post run headertype: $header_type \n";
print STDERR "Post run content_type: $content_type \n";
# No body for redirect or cache responses
$$output_ref = '' if in_array($header_type, [qw/cache redirect/]);
if( !$ENV{CGI_APP_RETURN_ONLY} && $header_type ne 'none') {
if($$output_ref && $header_type eq 'header') {
# Set, or unset inactivity monitoring cookies.
if ( $self->get_user() ) {
print STDERR "cgiapp_postrun getuser called. \n";
my $cookie = new CGI::Cookie({ -name=>'jobtracker_isactive', -secure=>1, -value=>1, -samesite=>"Strict" });
$self->header_add( -cookie => [ $cookie ] );
} else {
my $exp = '-10d';
my $cookie1 = new CGI::Cookie({ -name=>'jobtracker_blankscreen', -secure=>1, -value=>'', -expires=>$exp, -samesite=>"Strict" });
my $cookie2 = new CGI::Cookie({ -name=>'jobtracker_endsession', -secure=>1, -value=>'', -expires=>$exp, -samesite=>"Strict" });
my $cookie3 = new CGI::Cookie({ -name=>'jobtracker_isactive', -secure=>1, -value=>'', -expires=>$exp, -samesite=>"Strict" });
my $cookie4 = new CGI::Cookie({ -name=>'jobtracker_timeout', -secure=>1, -value=>'', -expires=>$exp, -samesite=>"Strict" });
my $cookie5 = new CGI::Cookie({ -name=>'jobtracker_scanmode', -secure=>1, -value=>'', -expires=>$exp });
my $cookie6 = new CGI::Cookie({ -name=>'jobtracker_scan_timeout', -secure=>1, -value=>'', -expires=>$exp });
$self->header_add( -cookie => [ $cookie1, $cookie2, $cookie3, $cookie4, $cookie5, $cookie6 ] );
}
# Add header and footer to HTML
if(!$self->param('no-html-header') && $content_type =~ qr'^text/html') {
print STDERR "add header hit. \n";
$$output_ref = $self->get_header().$$output_ref.$self->get_footer();
} elsif(ref $$output_ref) {
# Force refs to string
print STDERR "just shit out output as string. \n";
$$output_ref = $$output_ref.'';
}
#print STDERR "This is the output_ref in cgi post run:\n";
#print STDERR Dumper($$output_ref);
#print STDERR "---------------------------------------\n";
# Compress response, if allowed
if(!$headers->exists('-content-encoding') && !$headers->exists('-content-length') && in_string('gzip', $q->http('ACCEPT_ENCODING')) ) {
$$output_ref = Compress::Zlib::memGzip($$output_ref);
$self->header_add( -Content_Encoding => 'gzip' );
}
# Send Content-Length
if(!$headers->exists('-content-length') && !$headers->exists('-transfer-encoding')) {
$self->header_add( -Content_Length => length $$output_ref );
}
# Browser Etag caching
if(!$self->http_method('post') && !in_string('no-cache', $headers->get('CACHE_CONTROL')) ) {
unless($headers->exists('-last-modified') || $headers->exists('-etag')) {
my $etag = md5_base64($$output_ref);
if($self->cached_via_etag($etag)) {
$$output_ref = '';
$headers->delete('Content-Length');
}
}
}
}
if($self->http_method('head'))
{
print STDERR "No body for HEAD requests. \n";
}
$self->send_session_cookie() if $self->session_loaded();
}
$self->session()->flush();
$$output_ref = '' if $self->http_method('head'); # No body for HEAD requests
}
I need a Perl veteran for this one, especially someone who has delt with the ancient knowledge of Perl CGI. I am in unknown territory, and I'm missing something here. I'm missing some step, some call, something that I just can't seem to find.
I will continue debugging and running traces on these sub function calls, but I am at a loss. I feel like I should be asking what causes the browser to spit out a raw api file path and hash. If I can find out why a browser does that, maybe I can work backwards.
Oh and the Javascript AJAX call, just to be thorough.
$('#login_form').on('submit', function(event) {
event.preventDefault();
$.ajax({
url: 'api/userLogin',
method: 'post',
data: $(this).serialize(),
dataType: 'json',
error: function(event) {
alert(event.responseJSON.error);
},
success: function(data) {
console.log("normal success hit");
console.log("This is the data", data);
if (data.twofactor === '2FA') {
//show 2fa popup div with qr code.
var username = $('input[name="u"]').val(); // Directly grab the value from the username input field
TwoFactorPopUp(username, data.keyGen, data.CSRFToken);
return true;
}
if ( data._resume ) {
var resume = JSON.parse(data._resume);
window.location.replace(resume.url);
} else {
console.log("no resume, replacing window...");
window.location.replace(BASE_URL);
}
}
});
return false;
});
Published on Friday 14 March 2025 08:37
In our previous blog post, we detailed a clever external redirect
solution to address the Apache 2.4 regression that broke automatic
directory handling when DirectorySlash Off
was set. We teased the
question: Can we achieve the same behavior with an internal
redirect? Spoiler alert - Nope.
In this follow-up post, we’ll explore why internal rewrites fall short, our failed attempts to make them work, and why the external redirect remains the best and only solution.
/dir
without a trailing slash, Apache
would automatically redirect them to /dir/
.index.html
(or any file
specified by DirectoryIndex
).DirectorySlash Off
is set, Apache stops auto-redirecting
directories./dir
as /dir/
, Apache tries to serve
/dir
as a file, which leads to 403 Forbidden errors.DirectoryIndex
no longer inherits globally from the document
root - each directory must be explicitly configured to serve an
index file.DirectoryIndex
after an internal rewrite.Since Apache wasn’t redirecting directories automatically anymore, we thought we could internally rewrite requests like this:
RewriteEngine On
# If the request is missing a trailing slash and is a directory, rewrite it internally
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} -d
RewriteRule ^(.*)$ /$1/ [L]
Why This Doesn’t Work:
DirectoryIndex
after the rewrite.DirectoryIndex
is not explicitly defined for each
directory, Apache still refuses to serve the file and throws a
403 Forbidden./dir
as a raw file request instead of checking for an index page.To make it work, we tried to manually configure every directory:
<Directory "/var/www/vhosts/treasurersbriefcase/htdocs/setup/">
Require all granted
DirectoryIndex index.roc
</Directory>
DirectoryIndex
after an internal
rewrite. Even though DirectoryIndex index.roc
is explicitly set,
Apache never reaches this directive after rewriting /setup
to /setup/
./setup/
as an empty directory, leading to a 403
Forbidden error.This means that even if we were willing to configure every directory manually, it still wouldn’t work as expected.
We briefly considered whether FallbackResource
could help by
redirecting requests for directories to their respective index files:
<Directory "/var/www/vhosts/treasurersbriefcase/htdocs/setup/">
DirectoryIndex index.roc
Options -Indexes
Require all granted
FallbackResource /setup/index.roc
</Directory>
FallbackResource
is designed to handle 404 Not Found errors, not 403 Forbidden errors./setup/
as a valid directory but
refuses to serve it, FallbackResource
is never triggered.DirectoryIndex
after an internal rewrite.This was a red herring in our troubleshooting FallbackResource
was
never a viable solution.
Another way to handle this issue would be to explicitly rewrite
directory requests to their corresponding index file, bypassing
Apache’s DirectoryIndex
handling entirely:
RewriteEngine On
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} -d
RewriteRule ^(.*)$ /$1/index.roc [L]
DirectoryIndex
handling in Apache 2.4.DirectorySlash Off
, since the request is rewritten directly to a file.index.html
,
index.php
, etc.), additional rules would be required.While this approach technically works, it reinforces our main conclusion: - Apache 2.4 no longer restarts the request cycle after a rewrite, so we need to account for it manually. - The external redirect remains the only scalable solution.
Instead of fighting Apache’s new behavior, we can work with it using an external redirect:
RewriteEngine On
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} -d
RewriteRule ^(.*)$ http://%{HTTP_HOST}/$1/ [R=301,L]
/dir
to /dir/
before Apache even tries to serve it.DirectoryIndex
behavior globally, just like Apache 2.2.So, can we solve with an internal redirect?
DirectoryIndex
after an internal rewrite.DirectoryIndex
settings fail if Apache
has already decided the request is invalid.FallbackResource
never applied, since Apache rejected the request with 403 Forbidden, not 404
Not Found.Does our external redirect solution still hold up?
Yes!!, and in fact, it’s not just the best solution - it’s the only reliable one.
DirectorySlash Off
is set.If you haven’t read our original post yet, check it out here for the full explanation of our external redirect fix.
In part III of this series we’ll beat this dead horse and potentially explain why this was a problem in the first place…
Published by Robin Ashely on Friday 14 March 2025 08:16
Perl has long been a powerhouse in the programming world, known for its text-processing capabilities and versatility in scripting. While…
As an online trading company processing millions of transactions daily, we recognise technologies that stand the test of time. For 25 years, Perl has been integral to our operations, and we remain committed to the ecosystem that helped shape our technical foundation.
From our 1999 launch, Perl’s unmatched text processing and CPAN’s modular approach enabled us to evolve into a global platform. Core systems handling complex financial workflows leverage Perl’s stability, with key components still running the same battle-tested code developed in our early years.
Here are some of the activities we’ve done with Perl:
These implementations demonstrate Perl’s capacity to support robust, long-term solutions in demanding technical environments.
Our support of MetaCPAN stems from a clear conviction: foundational tools deserve sustained backing. While our new projects explore different technical approaches, we actively maintain:
“Great technologies create lasting value,” says Chris Horn, Head of Engineering. “Our support for Perl’s ecosystem honours its foundational role in our growth while helping sustain resources that benefit developers worldwide.”
Backing Perl’s ecosystem reflects Deriv’s long-standing belief in collaborative development. We’re proud to continue our MetaCPAN sponsorship through 2025 and support the Perl community as part of our ongoing commitment to open-source.
Published by /u/Grinnz on Thursday 13 March 2025 20:19
Published by Scott Lanning on Thursday 13 March 2025 19:54
(apologies for "promoting"(?) Perl obfuscation...)
Today I won a gift card at an in-office meeting with the following code. Challenge: print the numbers 1-100 in the most incomprehensible, inefficient way. My entry, edited for brevity:
#!/usr/bin/env perl
use v5.16;
splice @_, @_, -1, ++$_;
splice @_, @_, -1, ++$_;
splice @_, @_, -1, ++$_;
splice @_, @_, -1, ++$_;
splice @_, @_, -1, ++$_;
# plus 95 more of this
say join $/, @_;
Thinking about it more this evening, I came up with
$SIG {__DIE__} = sub { $_ = (pop)+0; chomp; $_%6?say:exit};
{ select undef,undef,undef,1; eval { die time-$^T }; redo; }
(where 6 instead of 101 so I don't have to wait 100 seconds (and to be honest I'm not sure if there'll be rounding errors)).
Wonder if any obfuscators could come up with better (the less inefficient, incomprehensible the better).
Published on Thursday 13 March 2025 10:39
I’m currently re-architecting my non-profit accounting SaaS (Treasurer’s Briefcase) to use Docker containers for a portable development environment and eventually for running under Kubernetes. The current architecture designed in 2012 uses an EC2 based environment for both development and run-time execution.
As part of that effort I’m migrating from Apache 2.2 to 2.4. In the
new development environment I’m using my Chromebook to access the
containerized application running on the EC2 dev server. I described
that setup in my previous
blog. In
that setup I’m accessing the application on port 8080 as http://local-dev:8080
.
If, as in the setup describe in my blog, you are running Apache on a
non-standard port (e.g., :8080
) - perhaps in Docker, EC2, or
via an SSH tunnel you may have noticed an annoying issue after
migrating from Apache 2.2 to Apache 2.4…
Previously, when requesting a directory without a trailing slash
(e.g., /setup
), Apache automatically redirected to /setup/
while preserving the port. However, in Apache 2.4, the redirect is
done externally AND drops the port, breaking relative URLs and
form submissions.
For example let’s suppose you have a form under the /setup
directory that
has as its action
“next-step.html”. The expected behavior on that page
would be to post to the page /setup/next-step.html
. But what really
happens is different. You can’t even get to the form in the first
place with the URL http://local-dev:8080/setup
!
http://yourserver:8080/setup
=> http://yourserver:8080/setup/
http://yourserver:8080/setup
=> http://yourserver/setup/
(port 8080
is missing!)This causes problems for some pages in web applications running behind Docker, SSH tunnels, and EC2 environments, where port forwarding is typically used.
If you’re experiencing this problem, you can confirm it by running:
curl -IL http://yourserver:8080/setup
You’ll likely see:
HTTP/1.1 301 Moved Permanently
Location: http://yourserver/setup/
Apache dropped 8080
from the redirect, causing requests to break.
Several workarounds exist, but they don’t work in our example.
DirectorySlash
: Prevents redirects but causes 403 Forbidden
errors when accessing directories.FallbackResource
: Works, but misroutes unrelated requests.Instead, we need a solution that dynamically preserves the port when necessary.
To restore Apache 2.2 behavior, we can use a rewrite rule that only preserves the port if it was in the original request.
<VirtualHost *:8080>
ServerName yourserver
DocumentRoot /var/www/html
<Directory /var/www/html>
Options -Indexes +FollowSymLinks
DirectoryIndex index.html index.php
Require all granted
DirectorySlash On # Keep normal Apache directory behavior
</Directory>
# Fix Apache 2.4 Directory Redirects: Preserve Non-Standard Ports
RewriteEngine On
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} -d
RewriteCond %{SERVER_PORT} !^80$ [OR]
RewriteCond %{SERVER_PORT} !^443$
RewriteRule ^(.*)$ http://%{HTTP_HOST}/$1/ [R=301,L]
UseCanonicalName Off
</VirtualHost>
/setup
=> /setup/
).!=80
, !=443
):8080
, making it flexible for any non-standard port8080
locally to 80
on the jump box./setup
redirected externally without the port, causing failures.mod_rewrite
and a RewriteRule
that ensures that port 8080
is preservedApache 2.4’s port-dropping behavior is an unexpected regression from 2.2, but we can fix it with a simple rewrite rule that restores the expected behavior without breaking anything.
If you’re running Docker, EC2, or SSH tunnels, this is a fix that will prevent you from jumping through hoops by altering the application or changing your networking setup.
Hmmmm…maybe we can use internal redirects instead of an external redirect??? Stay tuned.
Published on Thursday 13 March 2025 09:46
In our previous posts, we explored how Apache 2.4 changed its
handling of directory requests when DirectorySlash Off
is set,
breaking the implicit /dir → /dir/
redirect behavior that worked in
Apache 2.2. We concluded that while an external redirect is the only
reliable fix, this change in behavior led us to an even bigger
question:
Is this a bug or an intentional design change in Apache?
After digging deeper, we’ve uncovered something critically important that is not well-documented:
DirectoryIndex
.This post explores why this happens, whether it’s a feature or a bug, and why Apache’s documentation should explicitly clarify this behavior.
Let’s revisit the problem: we tried using an internal rewrite to append a trailing slash for directory requests:
RewriteEngine On
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} -d
RewriteRule ^(.*)$ /$1/ [L]
Expected Behavior:
- Apache should internally rewrite /setup
to /setup/
.
- Since DirectoryIndex index.roc
is set, Apache should serve index.roc
.
Actual Behavior:
- Apache internally rewrites /setup
to /setup/
, but then
immediately fails with a 403 Forbidden.
- The error log states:
AH01276: Cannot serve directory /var/www/vhosts/treasurersbriefcase/htdocs/setup/: No matching DirectoryIndex (none) found
- Apache is treating /setup/
as an empty directory instead of recognizing index.roc
.
Unlike what many admins assume, Apache does not start over after an internal rewrite.
/setup
).mod_rewrite
.index.html
, index.php
, etc.) exists, DirectoryIndex
resolves it.DirectoryIndex
.DirectoryIndex
after an internal rewrite./setup/
as an empty directory with no default file and denies access with 403 Forbidden.First, let’s discuss why this worked in Apache 2.2.
The key reason internal rewrites worked in Apache 2.2 is that Apache restarted the request processing cycle after a rewrite. This meant that:
index.html
, index.php
, or any configured default file.DirectorySlash
was handled earlier in the request cycle,
Apache 2.2 still applied directory handling rules properly, even after
an internal rewrite.In Apache 2.4, this behavior changed. Instead of restarting the
request cycle, Apache continues processing the request from where it
left off. This means that after an internal rewrite, DirectoryIndex
is
never reprocessed, leading to the 403 Forbidden errors we
encountered. This fundamental change explains why no internal solution
works the way it did in Apache 2.2.
mod_rewrite
or DirectoryIndex
docs.Since Apache won’t reprocess DirectoryIndex, the only way to guarantee correct behavior is to force a new request via an external redirect:
RewriteEngine On
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} -d
RewriteRule ^(.*)$ http://%{HTTP_HOST}/$1/ [R=301,L]
This forces Apache to start a completely new request cycle,
ensuring that DirectoryIndex
is evaluated properly.
We believe this behavior should be explicitly documented in: - mod_rewrite documentation (stating that rewrites do not restart request processing). - DirectoryIndex documentation (noting that it will not be re-evaluated after an internal rewrite).
This would prevent confusion and help developers troubleshoot these issues more efficiently.
![]() | submitted by /u/briandfoy [link] [comments] |
Published by Sam Dorfman on Wednesday 12 March 2025 19:37
I'm trying to make a perl terminal animation where an enitity reverses direction when it collides with another object. Here is the code I have so far:
use Term::Animation 2.0;
use Term::Animation::Entity;
use Time::HiRes qw(time);
use Data::Dumper;
use Curses;
use strict;
use warnings;
main();
sub add_animal {
my ($anim, $y) = @_;
# Add the animal to the animation
my $animal = $anim->new_entity(
type => 'animal',
position => [3, $y, 1],
shape => '*',
color => 'r',
callback_args => [1, 0, 0, 0],
die_offscreen => 0,
coll_handler => \&animal_collision,
);
}
sub animal_collision {
my ($animal, $anim) = @_;
my $collisions = $animal->collisions();
#return unless defined $collisions;
foreach my $col_obj (@{$collisions}) {
# Get the current x direction from the callback_args
my $x_dir = $animal->callback_args->[0];
# Reverse the direction
$animal->callback_args->[0] = -$x_dir;
}
}
sub add_background {
my ($anim, $screen_width, $screen_height) = @_;
my $half_width = int($screen_width / 2);
my $ground_level = int($screen_height * 0.7);
for my $y (0..($screen_height)) {
$anim->new_entity(
shape => ['|'],
position => [$half_width, $y, 21],
color => ['w'],
);
}
}
sub main {
my $anim = Term::Animation->new();
$anim->color(1);
halfdelay(1);
my $paused = 0;
while(1) {
my $screen_width = $anim->width();
my $screen_height = $anim->height();
my $half_width = int($screen_width / 2);
my $ground_level = int($screen_height * 0.7);
add_background($anim, $screen_width, $screen_height);
add_animal($anim, ($ground_level - 1));
# animation loop
while(1) {
my $current_time = time();
# run and display a single animation frame
$anim->animate() unless($paused);
# use getch to control the frame rate, and get input at the same time.
my $input = getch();
if($input eq 'q') { quit(); }
if($input eq 'r' || $input eq KEY_RESIZE()) { last; }
if($input eq 'p') { $paused = !$paused; }
}
$anim->update_term_size();
$anim->remove_all_entities();
}
$anim->end();
}
As of now, the animal object passes through the other object without any change of direction. Please let me know what is wrong with the collision detector or any other issues.
Have you worked with asynchronous task? If yes then this is one way of doing it in Perl.
Please checkout this post for more information.
Published by An5Drama on Wednesday 12 March 2025 08:29
While recently trying to get one algorithm for "Getting indices of matching parentheses". I can get what that algorithm means although with some problems with perl language.
The perl syntax is not obscure and can be got much with man perl...
doc.
But I am a bit confused about my
behavior with for
loop. man perlsyn
says:
If the variable is preceded with the keyword "my", then it is lexically scoped, and is therefore visible only within the loop. Otherwise, the variable is implicitly local to the loop and regains its former value upon exiting the loop. If the variable was previously declared with "my", it uses that variable instead of the global one, but it's still localized to the loop. This implicit localization occurs only for non C-style loops.
I know that "non C-style loops" mean those not like for (...;...;...){...}
.
The 1st sentence can be shown by this which is similar to the example shown in the doc:
$i = 'samba';
# If the variable is preceded with the keyword "my", then it is lexically scoped, and is therefore visible only within the loop.
for (my $i = 1; $i <= 4; $i++) {
print "$i\n";
}
print "$i\n";
# 1
# 2
# 3
# 4
# samba
But I can't understand what the 2nd means:
$inner = 'samba';
for ($i = 1; $i <= 4; $i++) {
$inner = $i + 1;
}
print "inner: $inner\n";
# inner: 5
Here the alleged "local" var $inner
seems to modify the outside var, and the "former value" 'samba'
can't be "regain"ed.
For the 3rd, we can do some small tweaks for the above example:
$inner = 'samba';
for ($i = 1; $i <= 4; $i++) {
my $inner = $i + 1;
}
print "inner: $inner\n";
# inner: samba
This works expectedly for "instead of the global one".
How to understand my
behavior in for
loop, especially for the above 2nd sentence in the quote?
Follow-up clarification with choroba's answer hints: When using the correct "non C-style loop" the 1st and 2nd sentence seem to mean that whether my
is used for var has the same effect. But actually it is not that case.
sub foo { print "foo: $x\n"; }
$x = 7;
for $x (1 .. 3) { # Implicit localisation happens here.
print "$x\n";
print "global $::x\n"; # Prints 1 .. 3 correspondingly.
foo(); # Prints 1 .. 3 correspondingly.
}
print $x; # Prints 7.
$x = 7;
for my $x (1 .. 3) { # Implicit localisation happens here.
print "$x\n";
print "global $::x\n"; # Always prints 7.
foo(); # Always prints 7.
}
print $x; # Prints 7.
This is just the difference between local
and my
which just means dynamic scope vs lexical scope as the top answer there shows which is also said in the doc.
A local just gives temporary values to global (meaning package) variables. It does not create a local variable. This is known as dynamic scoping. Lexical scoping is done with my ...
The 3rd sentence example can be updated based on this QA. Here sub
uses global package variable for $x
which won't be influenced by my $x = 7;
and also the latter assignments, i.e. $x = 7;
and $x (1 .. 3)
, for that new lexical.
sub foo { print "foo: $x\n"; }
my $x = 7;
# code block2
$x = 7;
for $x (1 .. 3) { # Implicit localisation happens here.
print "$x\n";
## difference 1
print "global $::x\n"; # Prints nothing for $::x.
# > it uses that variable instead of the global one
foo(); # Prints nothing for $x.
}
# > but it's still localized to the loop.
print $x; # Prints 7.
follow-up question for ikegami's answer:
If we prepend:
my $x = 7;
for $x (1 .. 3) { # Implicit localisation happens here.
print "$x\n";
print "global $::x\n"; # Prints nothing for $::x.
foo(); # Prints nothing for $x.
}
print $x; # Prints 7.
to the above sub foo { print "foo: $x\n"; } ...
example, then the latter $::x
also can't access the latter defined global var $x = 7;
. IMHO my
creates one new var which should not influence that global var.
But if we define the latter defined var as our $x = 7;
which is one "lexical alias to a package (i.e. global) variable" as the doc says. Then all works as before. What is the reason for that?
Published by Data Journal on Tuesday 11 March 2025 11:47
In this guide, I will cover everything you need to know to start web scraping in Perl, including the tools you’ll use, how to extract data…
Published by George Hunda on Monday 10 March 2025 23:17
I'm trying to use my batch script's file path as part of a Perl find and replace using the %0
variable, but since Windows uses backslashes this doesn't work as I'd hope. The string expands from s/^%~dp//gi
into "s/^C:\Users\user\Desktop\scripts\//gi"
with unescaped backslashes.
Custom delimiters like "m\\\gi"
wont do anything here. All I can think of is to echo the %0
into a file and find/replace it into my desired format there, but this feels like a terrible workaround.
Ideally, the format would be C:\\Users\\user\\Desktop\\scripts\\script.bat
in order for it to work plainly within the perl script.
I suppose I have two questions, can I somehow change the backslashes in the %0
batch variable to \\
or is there anything within Perl to workaround this?
Published by k_a_r_o_l on Monday 10 March 2025 18:07
I have a perl script.
I can use it in linux command-line be executing on a single file by specifying the input file and the name of an output.
perl removesmalls.pl 500 1.fasta > 1_500.fasta
In this example 500 stands for specified cutoff number.
How to use it for multiple fasta files in one folder.
I would like still to have an option to specify the cutoff number.
The script:
## removesmalls.pl
#!/usr/bin/perl
use strict;
use warnings;
my $minlen = shift or die "Error: `minlen` parameter not provided\n";
{
local $/=">";
while(<>) {
chomp;
next unless /\w/;
s/>$//gs;
my @chunk = split /\n/;
my $header = shift @chunk;
my $seqlen = length join "", @chunk;
print ">$_" if($seqlen >= $minlen);
}
local $/="\n";
}
So let say I have a folder "test_A
" full of fasta files.
1.fasta 2.fasta 3.fasta ect.
I would like to specify cutoff number as 500
I would like to have an output in the same test_A catalog named as:
1_500.fasta 2_500.fasta 3_500.fasta ect.
Published by Gabor Szabo on Monday 10 March 2025 06:57
Originally published at Perl Weekly 711
Hi there!
As you might know I used to teach Perl and helped companies using Perl. Unfortunately, in the last couple of years only a few companies asked for my help with Perl and in most cases in areas I am not that familiar with. So I referred them to other, well known consultants.
Instead of Perl most of my clients are asking for help with Python and Rust. Especially because of the latter I felt the need to know more about C and C++. At least to the level where I understand the questions C/C++ programmers might have about Rust. So I dusted off my copy of "The C programming language" and started to learn again.
This time it will be especially fun as my son - who has been a professional programmer for more than 6 years now - decided to get a degree in Computer Sciences so he is also taking classes about C.
The way I learn is by trying to implement things and trying to explain them. So I am going to post about my new journey with C on the C Maven web site. You have been warned.
In other news we are celebrating Purim this week, when everyone dresses up as someone else. I wonder if I should dress up as a C programmer?
Enjoy your week!
--
Your editor: Gabor Szabo.
Oh Oh
A one-liner
As brian d foy puts it: Fake loading locale to get around a wide character warning
Probably the best way to learn any programming language is to try writing some code and then get feedback from more experienced programmers.
Explicit is better than implicit? Automatic imports can cause hard-to-debug problems?
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.
Welcome to a new week with a couple of fun tasks "Minimum Time" and "Balls and Boxes". 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.
Enjoy a quick recap of last week's contributions by Team PWC dealing with the "Upper Lower" and "Group Digit Sum" tasks in Perl and Raku. You will find plenty of solutions to keep you busy.
Use of pack() and unpack(), very brave attempt. I still find it hard to get my head around. Thanks for making it look so easy.
My all time favourite, never miss the opportunity to surprise me. I love the clean and easy to read solution. Highly recommended.
Don't you love the method chaining of Raku? It allows you to create cute little one-liner. You must checkout to see it yourself.
A very special regex one-liner in Perl is my personal favourite. With it, we have detailed breakdown analysis and that is very handy. Great work, keep it up.
Once again, we have a very special contributions. Regex with the power of Unicode properties can be deadly combination. Plenty to learn every week, well done and keep it up.
Welcome back after a long break and what a comeback, I must admit. I am really impressed by story telling skill. Ofcourse the contribution is pretty too. Keep it up great work.
Unlike most weeks, this time we just have Raku one-liner magics for both task. Incredible, powerful Raku magics. Keep sharing knowledge with us.
Master of Perl one-liner is never going to miss the train and show the power of Perl regex. Well done and keep it up.
Plenty of Perl's regex magic every where. You really don't want to miss it. Great work, well done.
Our latest champion back with yet another gem of a solution. Don't forget to try DIY tool, impressive work. Keep it up.
Thanks for reminding us about the y operator as normally we see the use of tr// for such case. Keep sharing knowledge.
I was hoping Postscript to make a comeback in the post this week and I got my wish fulfilled. Thank you for your contribution as always.
Great CPAN modules released last week;
MetaCPAN weekly report.
Virtual event
Paris, France
Virtual Event
Virtual event
Munich, Germany
Greenville, South Carolina, 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.
Published by Bob Lied on Monday 10 March 2025 02:22
Another Perl Weekly Challenge, number 311 in a series of, as far as I can tell, infinity. We're gonna change some cases; we're gonna build some groups and tear them down. While we do it, we'll be singing badly out of key to Build Me Up, Buttercup, a Billboard Hot 100 hit from 1969, when I was a mere lad and none of the languages in the Weekly Challenge had yet been invented. Fun fact: the Foundations were the first multi-racial group to have a number one hit in the UK (Sri Lanka -- wasn't expecting that).
You are given a string that consists of English
letters only. Write a script to convert lower
case to upper and upper case to lower in the
given string.
$str = "pERl"
Output: "PerL"
$str = "rakU"
Output: "RAKu"$str = "PyThOn"
Output: "pYtHoN"
This is too easy if, by "English letters" we mean "stick to ASCII." It's considerably harder if we try to include anything that remotely smells of Unicode. But the cardinal virtue of Perl programming is laziness, so we'll stick to ASCII for a one-line program that tr
ansliterates as requested.
perl -E 'say tr/a-zA-Z/A-Za-z/r for @ARGV' pERL rakU PyThOn
Possibly notable: the trailing r on the tr///
function r
eturns a copy of the modified string. The default behavior is to return a count of substitutions made, which is perhaps unintuitive but certainly has its uses.
You are given a string, $str, made up of
digits,and an integer, $int, which is less
than the length of the given string.
Write a script to divide the given string
into consecutive groups of size $int (plus
one for leftovers if any). Then sum the
digits of each group, and concatenate all
group sums to create a new string. If the
length of the new string is less than or
equal to the given integer then return the
new string, otherwise continue the process.
$str = "111122333", $int = 3
"359"
$str = "1222312", $int = 2
$str = "100012121001", $int = 4
"162"
The task has a basic recursive nature: do something to the string, then do the same thing to the string that results. I take a moment to convince myself that every time I apply the group-sum steps, the resulting string is going to get smaller, and eventually we really will get a string shorter than $n
, and not a hike to an infinite loop.
Recursion has an ick factor greater than one. By "ick" I mean that I get confused when debugging. I'm instead going to use an overall structure that looks like
sub groupSum($str, $n)
{
while ( length($str) > $n )
{
$str = mangle($str, $n);
}
return $str;
}
Now we have some sub-problems to implement mangle()
. The first is how to group digits. The substr
function seems like an obvious choice, or we could exploit regular-expression matching. If we put matching in an array context and use the g
flag, it will return an array of matches:
my @group = ($str =~ m/.{1,$n}/g);
.{1,$n}
-- match any character 1 to n times. $n
is interpolated into the expression; the range doesn't have to be constant./.{$n}/
would not work. That would need to match n characters every time, so the shorter bit at the end would be left out.There's another way to do it which is more unique to Perl. The unpack
function is designed to deal with data that is in fixed formats (and with binary formats, but that's a whole different subject). With unpack
, you give it a format for the string. In our case, the format is n ASCII characters, repeated indefinitely.
my @group = unpack("(a$n)*", $str);
Once again, Perl does that do-what-I-mean thing, and this format also picks up the trailing characters that may be less than n long.
Okay, so we have a way to turn the string into an array of the appropriate-size groups. Each member of the group now needs to be separated into digits and added up. That could be a loop, but I choose map
.
use List::Util qw/sum/;
my @sums = map { sum( split(//, $_) ) } @group
And then those sums need to be combined into a new string.
return join("", @sums);
All those bits can be turned into concise code without intermediate variables that Perl haters like to disdain, but that connoisseurs of punctuation and fine Perl can appreciate.
sub mangle($str, $n)
{
return join "", map { sum( split(//, $_) ) } ($str =~ m/(.{1,$n})/g);
}
Well, now our mangle
function has been reduced to one line. We may as well re-factor it into our original loop:
sub groupDigitSum($str, $n)
{
while ( length($str) > $n )
{
$str = join "", map { sum( split("", $_) ) } unpack("(a$n)*", $str);
}
return $str;
}
Ta-da.
While I was putting this together, I was annoyed that the mangle
function, which would only be used inside of groupDigitSum
, was being defined at the same level and therefore entering (dare I say polluting) the global name space.
sub groupDigitSum { ... }
sub mangle { ... }
I grumbled that even Pascal allowed nested function definition. Surely Perl, the overflowing receptacle of the cast-off bits of so many languages that came before, could allow nested subs? Yes! Perl syntax allows the declaration of one sub
inside another:
sub groupDigitSum { ...
sub mangle { ... }
}
Wouldn't that be a nice way to encapsulate the utility function? Alas, no. Subroutines don't localize the same way that variables do. Even though it looks like mangle
is inside the scope of groupDigitSum
, it goes into the global name space and is still accessible from outside.
One way to make it truly local is to assign an anonymous sub to a scalar variable and use function de-referencing.
sub groupDigitSum { ...
my $mangle = sub { ... };
$mangle->($str, $n)
}
That solves the localization, but looks unnecessarily obfuscated, and makes it impossible to unit-test the mangle function.
Another way to hide it is to put these functions inside a package (or class), exporting one and not the other, but that's one step up and one step back -- we hide the mangle
name, but we introduce a package/class name.
Published on Saturday 08 March 2025 22:30
The examples used here are from the weekly challenge problem statement and demonstrate the working solution.
You are given a string consists of english letters only. Write a script to convert lower case to upper and upper case to lower in the given string.
The complete solution is contained in one file that has a simple structure.
For this problem we do not need to include very much. We’re just specifying to use the current version of Perl, for all the latest features in the language. This fragment is also used in Part 2.
All the work is in one subroutine. We use the ASCII values of each character to compute the new value.
sub upper_lower{
my($s) =
@_;
my
@c = split //, $s;
return join q//, map{
my $x = ord($_);
if($x >= 65 && $x <= 90){
chr($x + 32);
}
elsif($x >= 97 && $x <= 122){
chr($x - 32);
}
}
@c;
}
◇
Fragment referenced in 1.
Now all we need are a few lines of code for running some tests.
MAIN:{
say upper_lower q/pERl/;
say upper_lower q/rakU/;
say upper_lower q/PyThOn/;
}
◇
Fragment referenced in 1.
$ perl perl/ch-1.pl PerL RAKu pYtHoN
You are given a string, $str, made up of digits, and an integer, $int, which is less than the length of the given string. Write a script to divide the given string into consecutive groups of size $int (plus one for leftovers if any). Then sum the digits of each group, and concatenate all group sums to create a new string. If the length of the new string is less than or equal to the given integer then return the new string, otherwise continue the process.
To solve this problem we need to do the following
Let’s look at each of those pieces individually and then combine them together into one subroutine.
my $g = [];
my $groups;
for my $i (0 ..
@{$c} - 1){
my $n = $i % $size;
if($n == 0){
$g = [];
push
@{$g}, $c->[$i];
}
elsif($n == $size - 1){
push
@{$g}, $c->[$i];
push
@{$groups}, $g;
$g = [];
}
else{
push
@{$g}, $c->[$i];
}
}
push
@{$groups}, $g if
@{$g} > 0;
◇
With that work take care of, let’s combine all these pieces into one subroutine.
Finally, here’s a few tests to confirm everything is working right.
MAIN:{
say group_digit_sum q/111122333/, 3;
say group_digit_sum q/1222312/, 2;
say group_digit_sum q/100012121001/, 4;
}
◇
Fragment referenced in 5.
$ perl perl/ch-2.pl 359 76 162
Published on Sunday 09 March 2025 00:00
Published by Unknown on Saturday 08 March 2025 23:09
This is the weekly favourites list of CPAN distributions. Votes count: 56
Week's winner: DBI (+2)
Build date: 2025/03/08 22:03:54 GMT
Clicked for first time:
Increasing its reputation:
Published on Friday 07 March 2025 19:21
If you’re doing some development on a remote server that you access via a bastion host (e.g., an EC2 instance), and you want a seamless way to work from a Chromebook, you can set up an SSH tunnel through the bastion host to access your development server.
This guide outlines how to configure a secure and efficient development workflow using Docker, SSH tunnels, and a Chromebook.
Your Chromebook is a great development environment, but truth be told, the cloud is better. Why? Because you can leverage a bucket load of functionality, resources and infrastructure that is powerful yet inexpensive. Did I mention backups? My Chromebook running a version of debian rocks, but in general I use it as a conduit to the cloud.
So here’s the best of both worlds. I can use a kick-butt terminal
(terminator
) on my Chromie and use its networking mojo to access my
web servers running in the cloud.
In this setup:
Here’s what it looks like in ASCII art…
+--------------+ +--------------+ +--------------+
| Chromebook | | Bastion Host | | EC2 |
| | 22 | | 22 | |
| Local SSH |----| Jump Box |----| Development |
| Tunnel:8080 | 80 | (Accessible) | 80 | Server |
+--------------+ +--------------+ +--------------+
To create an SSH tunnel through the bastion host:
bash
ssh -N -L 8080:EC2_PRIVATE_IP:80 user@bastion-host
-N
: Do not execute remote commands, just forward ports.-L 8080:EC2_PRIVATE_IP:80
: Forwards local port 8080 to port 80 on the development server (EC2 instance).user@bastion-host
: SSH into the bastion host as user
.Once connected, any request to localhost:8080
on the Chromebook will
be forwarded to port 80 on the EC2 instance.
To maintain the tunnel connection automatically:
~/.ssh/config
):Host bastion
HostName bastion-host
User your-user
IdentityFile ~/.ssh/id_rsa
LocalForward 8080 EC2_PRIVATE_IP:80
ServerAliveInterval 60
ServerAliveCountMax 3
ssh -fN bastion
curl -I http://localhost:8080
You should see a response from your EC2 instance’s web server.
If your EC2 instance runs a Dockerized web application, expose port 80 from the container:
docker run -d -p 80:80 my-web-app
Now, accessing http://localhost:8080
on your Chromebook browser will
open the web app running inside the Docker container on EC2.
This setup allows you to securely access a remote development environment from a Chromebook, leveraging SSH tunneling through a bastion host.
localhost:8080
) to a remote web app.Now you can develop on remote servers with a Chromebook, as if they were local!
Published by Robertoazaar on Friday 07 March 2025 05:40
One of the most important abilities of the programmers is ETL (Extract Transform and Load) file. In the old days like 2018 or something we…
Published by Perl Steering Council on Thursday 06 March 2025 21:45
All three of us attended, but none of us had the time for significant discussion, so we decided to reclaim the time and make some progress on our various to-do list items.
Published by Meir Michanie on Tuesday 04 March 2025 21:18
If you’re juggling multiple remote servers and you often find yourself running the same commands or transferring files across all of them…
Published by Gabor Szabo on Monday 03 March 2025 09:20
Originally published at Perl Weekly 710
Hi there,
Do you remember, the weekly newsletter where I mentioned about the work carried out for PPC web portal?
Well, it is now fully functional and operative: PPC. It has everything that you need to know about the future plan of Perl. Not only that, you can also involve in the new feature request. It made the process smooth and easy to understand. You can also take part in the discussion as well. This is your opportunity to share your view on the work being carried out. Or if you have any suggestion, you can use the template to submit your ideas. Isn't it simple? The best part for me is the ability to join the discussion in the work being done before it becomes the part of future release.
In the last edition of newsletter, there was an announcement about German Perl/Raku Workshop and Perl Toolchain Summit. I encourage every community members to come forward and help the organisers to make it reach wider audiences. There is another announcement about The North American Perl and Raku Conference. Please do share the details with your colleagues and friends.
Are you new to Perl and want to learn in 2025 then checkout this post. Here is another: Step-by-Step Guide to Learning PERL Programming: From Novice to Expert.
Today is the Day 3 of Ramadan in England. May ALLAH s.w.t bless you with peace, happiness and prosperity.
Enjoy rest of the newsletter.
--
Your editor: Mohammad Sajid Anwar.
The North American Perl and Raku Conference will soon be upon us, and that means now is a great time to show your support for this very special event. This year there are many available sponsorship tiers and lots of opportunities for your organization to support the conference.
For Perl Wiki fan, this is for you.
Looks like a busy meet, nice to see things are moving quickly.
Nice collections, new to Perl then this is for you.
Worth reading even if you already know the language.
Nice blog series sharing behind the scene secrets. If you are into cloud tech then you must checkout.
Part II of the series carried forward.
Third installment of the series for you.
Time for the final word on the series. Highly recommended.
Refresh the newly added command line flag '-g' in Perl v5.36.
A gentle introduction to MCE (Many-Core Engine) about parallel processing.
Fun story telling skill with plenty of tech talk in between. I also liked the use of Object::Pad.
Are you a user of MailBox? If yes then please hekp Mark during the test phase of the rework.
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.
Welcome to a new week with a couple of fun tasks "Upper Lower" and "Group Digit Sum". 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.
Enjoy a quick recap of last week's contributions by Team PWC dealing with the "Arrays Intersection" and "Sort Odd Even" tasks in Perl and Raku. You will find plenty of solutions to keep you busy.
Love the use of v5.38 but surprised not to see method signature in action. Thanks for sharing knowledge.
Good use of CPAN modules. As always we have a compact and cute solutions. Keep it up great work.
Raku one-liner by the master himself. Good to see Raku flexing muscles.
Perl and Raku side by side as always. You will clearly see the similarities. Great work.
I am a big fan of PDL. I just love the final product. Keep sharing the magic.
Here you have another fan of CPAN modules. Checkout the use of CPAN in one-liner. Keep it up great work.
Love the story around the solution. It makes it fun reading, well done and keep it up.
As the title suggests, plenty of loops. Why not? Go for it and don't forget to use DIY tool.
Smart use of 'mesh' from CPAN module List::SomeUtils. Clever move, well done.
Nice demo of Scalar solution. You get to learn something new every week by just reading the post. Highly recommended.
Great CPAN modules released last week.
Virtual event
Paris, France
Virtual Event
Virtual event
Munich, Germany
Greenville, South Carolina, 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.
The North American Perl and Raku Conference will soon be upon us, and that means now is a great time to show your support for this very special event. This year there are many available sponsorship tiers and lots of opportunities for your organization to support the conference.
The Perl and Raku Conference 2025 is a community-led gathering of developers, enthusiasts, and industry professionals. Taking place June 27-29, 2025, in Greenville/Spartanburg, South Carolina, the conference features technical talks, training sessions, and networking opportunities that bring together the Perl and Raku communities.
Support our local community by contributing to our charity raffle benefiting a local Greenville/Spartanburg area food bank:
Exclusive naming rights available for:
Proceeds beyond conference expenses support The Perl and Raku Foundation, a non-profit organization dedicated to advancing the Perl and Raku programming languages through open source development, education, and community building.
For more information on to become a sponsor, please contact: olaf@perlfoundation.org
Published on Saturday 01 March 2025 22:17
The examples used here are from the weekly challenge problem statement and demonstrate the working solution.
You are given a list of array of integers. Write a script to return the common elements in all the arrays.
The complete solution is contained in one file that has a simple structure.
For this problem we do not need to include very much. We’re just specifying to use the current version of Perl, for all the latest features in the language. This fragment is also used in Part 2.
We’ll take the arrays in pairs and build up a list of common elements. First we compute the common elements of the first two arrays and then proceed to check these against the remaining arrays. If any of these initial common elements are not found in subsequent arrays they are not included in future checks. Finally the remaining common elements are returned. If there are no common elements we return n/a.
sub array_intersections{
my
@common_elements;
my $x = shift
@_;
my $y = shift
@_;
if($x && $y){
my
@common = map {
my $x = $_;
grep {$x == $_}
@{$y}
}
@{$x};
push
@common_elements,
@common;
}
{
$x = shift
@_;
my
@common = map {
my $x = $_;
grep {$x == $_}
@{$y}
}
@common_elements;
@common_elements =
@common;
redo if
@_ > 1;
}
return (join q/, /,
@common_elements) || q#n/a#;
}
◇
Fragment referenced in 1.
Now all we need are a few lines of code for running some tests.
MAIN:{
say array_intersections [1, 2, 3, 4], [4, 5, 6, 1], [4, 2, 1, 3];
say array_intersections [1, 0, 2, 3], [2, 4, 5];
say array_intersections [1, 2, 3], [4, 5], [6];
}
◇
Fragment referenced in 1.
$ perl perl/ch-1.pl 1, 4 2 n/a
You are given an array of integers. Write a script to sort odd index elements in decreasing order and even index elements in increasing order in the given array.
To solve this problem we need to do the following
Much of this work can be written concisely using map and grep.
sub sort_odd_even{
my
@i =
@_;
my
@odds = map { $i[$_] } grep {$_ % 2 != 0} 0 ..
@_ - 1;
my
@evens = map { $i[$_] } grep {$_ % 2 == 0} 0 ..
@_ - 1;
my
@odds_sorted = sort {$b <=> $a}
@odds;
my
@evens_sorted = sort {$a <=> $b}
@evens;
my
@common_elements;
do {
$common_elements[$_] = shift
@odds_sorted if $_ % 2 != 0;
$common_elements[$_] = shift
@evens_sorted if $_ % 2 == 0;
} for 0 ..
@_ - 1;
return
@common_elements;
}
◇
Fragment referenced in 5.
Finally, here’s a few tests to confirm everything is working right.
MAIN:{
say join q/, /, sort_odd_even 4, 1, 2, 3;
say join q/, /, sort_odd_even 3, 1;
say join q/, /, sort_odd_even 5, 3, 2, 1, 4;
}
◇
Fragment referenced in 5.
$ perl perl/ch-2.pl 2, 3, 4, 1 3, 1 2, 3, 4, 1, 5
Published by Unknown on Saturday 01 March 2025 22:46
Published by Bob Lied on Thursday 27 February 2025 14:46
PWC 310 Task 1, Arrays Intersection asks us to implement set intersection from lists of numbers. For musical accompaniment, I suggest Everywhere by Fleetwood Mac.
You are given a list of array of integers.
Write a script to return the common elements
in all the arrays.
Example 1
$list = ( [1, 2, 3, 4], [4, 5, 6, 1], [4, 2, 1, 3] )
(1, 4)
Example 2
$list = ( [1, 0, 2, 3], [2, 4, 5] )
(2)
Example 3
$list = ( [1, 2, 3], [4, 5], [6] )
()
Use a hash as a set. If we knew that all the lists have only distinct elements, then we could just check that an element occurs the right number of times. But if a number can repeat in a list, then counting is not enough.
In the hash, map the number to a bit map. If the number occurs in list i, then bit i will be set. At the end, if the number of bits set is equal to the number of lists, then that number must occur in every list.
A possible limitation of using a bit map is that it limits us to 64 lists, which seems like an acceptable compromise. Perl also has the vec
function for bit vectors, but that would require an hour of review and experimentation. Maybe I'll come back to it.
sub asect(@list)
{
my %common;
my $allBits = 2 ** scalar(@list) - 1;
for ( 0 .. $#list )
{
my $bit = 1 << $_;
$common{$_} |= $bit for $list[$_]->@*;
}
return [ grep { $common{$_} == $allBits } sort { $a <=> $b } keys %common ];
}
my %common
-- the keys of this hash are the numbers from the lists.
my $allBits = 2 ** scalar(@list) - 1
-- This creates a mask of 1s for the number of lists. For instance, if there are 3 lists, '2**3-1` is 7, which is 111 in binary.
for ( 0 .. $#list )
the indexes of the lists.
my $bit = 1 << $_
An integer where exactly one bit is set: the one corresponding to the list number
$common{$_} |= $bit for $list[$_]->@*
$_
has two different contexts. In for $list[$_]->@*
, $_
is the list index from the top-level for loop, but in $common{$_}
it refers to the number taken from the list.|=
operator sets bit i if it occurs in list i, even if it occurs more than once.At exit of the loop, every number from all the lists is present as a key in %common
. That key maps to a value which has bits set corresponding to the lists in which it occurred. We want to return the numbers that have all the bits set. Breaking down the last statement from right to left:
sort { $a <=> $b } keys %common
I'm adding a sort to create a predictable output order, which is useful in unit test.grep { $common{$_} == $allBits }
Selecting only the numbers that have all the bits set.[ ... ]
enclosing the result in square brackets returns an array reference. I prefer returning references for three reasons:
say
or in a unit-test is()
test).Published on Tuesday 25 February 2025 20:15
I’m excited to announce the release of OrePAN2::S3, a new Perl distribution designed to streamline the creation and management of private CPAN (DarkPAN) repositories using Amazon S3 and CloudFront. This tool simplifies the deployment of your own Perl module repository, ensuring efficient distribution and scaling capabilities.
This effort is the terminal event (I hope) in my adventure in Perl packaging that led me down the rabbit hole of CloudFront distributions?
My adventure in packaging started many years ago when it seemed like a good idea to use RPMs. No really, it was!
The distribtions embraced Perl and life was good…until of course it wasn’t. Slowly but surely, those modules we wanted weren’t available in any of the repos. Well, here we are in 2025 and you can’t even grovel enough to get AWS to include Perl in its images? Sheesh, really? I have to
yum install perl-core
? Seems like someone has specifically put Perl on the shit list.I’ve been swimming upstream for years using
cpanspec
and learning how to create my ownyum
repos with all of the stuff Amazon just didn’t want to package. I’ve finally given up. CPAN forever!cpanm
is awesome! Okay, yeah, but I’m still not all in withcarton
. Maybe some day?
Key Features of OrePAN2::S3
Seamless Integration with AWS: OrePAN2::S3
leverages Amazon S3
for storage and CloudFront for content delivery, providing a highly
available, scalable solution for hosting your private CPAN repository.
User-Friendly Setup: The distribution offers straightforward scripts and configurations, enabling you to set up your DarkPAN with minimal effort…I hope. If you find some points of friction, log an issue.
Flexible Deployment Options: Whether you prefer a simple
S3-backed website or a full-fledged CloudFront distribution,
OrePAN2::S3
accommodates both setups to suit your specific
needs. If you really just want a website enable S3 bucket to serve
as your DarkPAN we got your back. But be careful…
Getting Started:
To begin using OrePAN2::S3
, ensure you have the following
prerequisites:
AWS Account: An active Amazon Web Services account.
S3 Bucket: A designated S3 bucket to store your CPAN modules.
CloudFront Distribution (optional): For enhanced content delivery and caching.
Detailed instructions for setting up your S3 bucket and CloudFront distribution are available in the project’s repository.
Why Choose OrePAN2::S3
?
Managing a private CPAN repository can be complex, but with
OrePAN2::S3
, the process becomes efficient and scalable. By harnessing
the power of AWS services, this distribution ensures your Perl modules
are readily accessible and securely stored.
Oh, and let’s give credit where credit is due. This is all based on
OrePAN2
.
For more information and to access the repository, visit: https://github.com/rlauer6/OrePAN2-S3
We look forward to your
feedback and
contributions to make OrePAN2::S3
even more robust and
user-friendly.
Published by Stig Palmquist, Timothy Legge and Breno G. de Oliveira on Tuesday 25 February 2025 21:00
The CPAN Security Group was authorized by the CVE Program as a CVE Numbering Authority (CNA) on Feb 25, 2025. A CNA assigns and manages CVE identifiers for projects in their scope.
Our scope is vulnerabilities in Perl and CPAN Modules (including End-of-Life Perl versions) found at perl.org, cpan.org or metacpan.org, excluding distributions of Perl or CPAN Modules maintained by third-party redistributors.
CVE is an international, community-based effort to identify, define and catalog publicly disclosed software vulnerabilities. To learn more about the CVE program, visit www.cve.org.
Vulnerabilities should be reported according to the security policy of the affected project.
For more details, see our guide on how to Report a Security Issue in Perl and the CPAN ecosystem.
To request a CVE identifier, or to update a CVE we have issued, please send an email to cve-request@security.metacpan.org.
Subscribe to the cve-announce mailing list to be notified of new CVEs published by us.
For questions, disputes or other CNA related queries please use cna@security.metacpan.org. Disputes are handled according to the CNA rules.
Published by Gabor Szabo on Monday 24 February 2025 06:49
Originally published at Perl Weekly 709
A reminder: The 27th German Perl/Raku Workshop will take place between May 12-14, 2025, in Munich, Germany with "ticketing systems" as the main theme. The focus will be on building ticketing software, addressing real-world challenges, and exploring practical solutions. In addition to this deep dive, attendees can look forward to a variety of cutting-edge and exciting topics presented by renowned speakers. Whether you're a seasoned developer or just curious about the latest in Perl and Raku, this event promises insights and inspiration for everyone!. See official website for more info.
Philippe Bruhat sent me a message: The Perl Toolchain Summit is happening again in 2025. This year, we're going to be in Leipzig, hosted by Daniel Böhmer and Tina Müller, from May 1st to May 4th, 2025. This year again, we're going to need the support of sponsors to be able to bring our 30+ attendees in the same place at the same time to work on the Perl and CPAN toolchain. Download our sponsor prospectus. See the announcement
After several weeks in Hungary I am now back to Israel. We are having in-person events in the other language communities. Unfortunately not Perl.
However, I still offer help to companies that would like to reduce their bugs in their programming environment in Perl, Python, or Rust or that would like help introducing either of those languages.
Have a nice week!
--
Your editor: Gabor Szabo.
The 15th Perl Toolchain Summit will be held in Leipzig, Germany, from Thursday May 1st till Sunday May 4th, 2025.
Isn't it super-exciting that perl can be run in a browser these days?
Cellgraph 0.7 is out. (App::GUI::Cellgraph - draw pattern by cellular automata)
A curated look at last month’s new CPAN uploads for your reading and programming pleasure.
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.
Welcome to a new week with a couple of fun tasks "Arrays Intersection" and "Sort Odd Even". 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.
Enjoy a quick recap of last week's contributions by Team PWC dealing with the "Min Gap" and "Min Diff" tasks in Perl and Raku. You will find plenty of solutions to keep you busy.
Another cute and compact solutions. Keep it up great work.
Reduction Operator of Raku showing off the power. Raku Rocks !!!
New to PDL? PDL is the talk of the town. Thanks for promoting PDL.
Back to complete solutions in multiple languages. Great work, keep it up.
Schwartzian transform? Yes, you can do it in one-liner, incredible. Thanks for sharing knowledge.
Line by line narration is my favourite. It makes it so fun, getting the idea behind. Highly recommended.
A complete solution that anyone can understand and follow. Bonus, you get DIY tool to try as well.
Loops is the key to solve the task this week. Cleverly used, well done.
Crystal is pick this week? Nice to see about new language being discussed in a blog post. Keep it up great work.
Great CPAN modules released last week;
MetaCPAN weekly report.
Virtual event
Paris, France
Virtual event
Munich, Germany
Greenville, South Carolina, 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.
Published on Sunday 23 February 2025 19:58
The examples used here are from the weekly challenge problem statement and demonstrate the working solution.
You are given an array of integers, @ints, increasing order. Write a script to return the element before which you find the smallest gap.
The complete solution is contained in one file that has a simple structure.
For this problem we do not need to include very much. We’re just specifying to use the current version of Perl, for all the latest features in the language. This fragment is also used in Part 2.
Let’s not even store anything. Instead go down the list and update two variables: one to store the current minimum gap, and the other to store the element before the current smallest gap found.
I use a small trick here. A way of saying The Maximum Integer is 0 + q/inf/.
sub min_gap{
my($min_gap, $element_min_gap) = (0 + q/inf/, 0 + q/inf/);
{
my $x = shift
@_;
my $y = shift
@_;
if($x && $y){
my $gap = $y - $x;
if($gap < $min_gap){
$min_gap = $gap;
$element_min_gap = $y;
}
}
unshift
@_, $y;
redo if
@_ > 1;
}
return $element_min_gap;
}
◇
Fragment referenced in 1.
Now all we need are a few lines of code for running some tests.
MAIN:{
say min_gap 2, 8, 10, 11, 15;
say min_gap 1, 5, 6, 7, 14;
say min_gap 8, 20, 25, 28;
}
◇
Fragment referenced in 1.
$ perl perl/ch-1.pl 11 6 28
You are given an array of integers, @ints. Write a script to find the minimum difference between any two elements.
From Part 1 we know that if we sort the list we know we need to only check adjacent elements to find the minimum difference.
sub min_diff{
my $min_gap = 0 + q/inf/;
my
@i = sort {$a <=> $b}
@_;
{
my $x = shift
@i;
my $y = shift
@i;
if($x && $y){
my $gap = $y - $x;
$min_gap = $gap if $gap < $min_gap;
}
unshift
@i, $y;
redo if
@i > 1;
}
return $min_gap;
}
◇
Fragment referenced in 5.
Finally, here’s a few tests to confirm everything is working right.
$ perl ch-2.pl 1 2
Published by Unknown on Sunday 23 February 2025 08:21
Published by Gathering Insight on Saturday 22 February 2025 03:15
Welcome to “What’s new on CPAN”, a curated look at last month’s new CPAN uploads for your reading and programming pleasure. Enjoy!
which
Unix command, but takes a regex as argument