Published by FreonPSandoz on Monday 20 January 2025 20:06
The pragma use feature 'unicode_strings'
is now strongly recommended.
How does one follow that recommendation in a library module that may be used by Perl versions older than v5.12? I tried several variations of
use if $PERL_VERSION >= v5.12, "feature 'unicode_strings'";
and I always get an error like
Can't locate feature 'unicode_strings'.pm in @INC.
The 'feature' pragma is listed in perlmodlib as a standard pragma, so I assume that it works with use if
and that I simply don't have the correct syntax.
Published by simone on Monday 20 January 2025 19:22
(ADDITIONAL EDITS START HERE)
ntext: I'm trying to build a module that "guesses" data types by example.
It should work like this (adding Type::Tiny
types is a role on top of plain Type::Guess
):
use Type::Guess;
use Types::Standard qw( Int Num Str );
use strict;
$\ = "\n"; $, = "\t";
my @data = (
[qw/a b cd efg hijk/],
[qw/1 23 456 12000 12.0/],
[qw/1.12345 23 456 12000 12.0/],
[qw/-100% -13% 12.1%/],
[23, +16, -100],
[ "2022-12-30", "1923-12-30" ]
);
for my $l (@data) {
print Type::Guess->with_roles("+Tiny")->new($l->@*)->type->name
}
and if one needs custom types:
my $Date = Type::Tiny->new(
name => "Date",
constraint => sub { /^\d{4,4}-\d{2,2}-\d{2,2}$/ },
message => sub { "$_ ain't a date" },
);
for my $l (@data) {
print Type::Guess->with_roles("+Tiny")->new($l->@*, { types => [$Date, Int, Num, Str] })->type->name
}
since at the time of the first draft of this question I thought it'd be cool (I don't think so anymore, but hey) to do this:
for my $l (@data) {
print Type::Guess->with_roles("+Tiny")->new($l->@*, { types => [qw(Date, Int, Num, Str)] })->type->name
}
(ADDITIONAL EDITS END HERE)
I am trying to get a Type::Tiny object via symbolic reference. It seems to work ok when I do it on built-in types, like this:
use Type::Tiny;
use Types::Standard qw( Int Num Str );
use strict;
$\ = "\n"; $, = "\t";
no strict 'refs';
# ------------------------------------------
# This works ok
# ------------------------------------------
print ref *{"main::Int"}{CODE}->(); # Type::Tiny
print *{"main::Int"}{CODE}->()->name; # Int
however, when I try to create my own type and access it like this:
use Type::Tiny;
use Types::Standard qw( Int Num Str );
use strict;
$\ = "\n"; $, = "\t";
no strict 'refs';
# ------------------------------------------
# ...and now the part that doesn't work
# ------------------------------------------
my $Date = Type::Tiny->new(
name => "Date",
constraint => sub { /^\d{4,4}-\d{2,2}-\d{2,2}$/ },
message => sub { "$_ ain't a date" },
);
print ref $Date; # Type::Tiny, so things look ok
print $Date->name; # Date, so things look ok
# ------------------------------------------
# this doesn't work
# ------------------------------------------
print *{"main::Date"}{SCALAR};
my $date_type_ref = *{"main::Date"}{SCALAR};
print ref $date_type_ref; # SCALAR
print $$date_type_ref; # nothing
# ------------------------------------------
# this doesn't work
# ------------------------------------------
my $date_type_ref = *{"main::Date"}{CODE};
eval { print $date_type_ref->() } || print $@;
# --------------------------------------------------------
# poking around in the dark but these doesn't work either
# --------------------------------------------------------
for my $type (qw/SCALAR HASH ARRAY CODE/) {
my $date_type_ref = *{"main::Date"}{$type};
print ref $date_type_ref;
eval { print $date_type_ref->name }; print $@;
eval {
my $date_type = $$date_type_ref;
print $date_type->name;
};
print $@
}
nothing seems to work.
What should I change? What is a Tiny::Type object under the hood, and how do I get there?
Published by ManitoDeManitos23del23 on Monday 20 January 2025 18:36
When i run :
cpan install Crypt::SSLeay
I get the next output :
make: *** [Makefile:887: test_dynamic] Error 2
OALDERS/LWP-Protocol-https-6.14.tar.gz
2 dependencies missing (IO::Socket::SSL,IO::Socket::SSL::Utils); additionally test harness failed
/usr/bin/make test -- NOT OK
//hint// to see the cpan-testers results for installing this module, try:
reports OALDERS/LWP-Protocol-https-6.14.tar.gz
NANIS/Crypt-SSLeay-0.72.tar.gz
Has already been unwrapped into directory /home/archy/.cpan/build/Crypt-SSLeay-0.72-4
NANIS/Crypt-SSLeay-0.72.tar.gz
Has already been prepared
Running make for N/NA/NANIS/Crypt-SSLeay-0.72.tar.gz
Warning: Prerequisite 'LWP::Protocol::https => 6.02' for 'NANIS/Crypt-SSLeay-0.72.tar.gz' failed when processing 'OALDERS/LWP-Protocol-https-6.14.tar.gz' with 'make_test => NO 2 dependencies missing (IO::Socket::SSL,IO::Socket::SSL::Utils); additionally test harness failed'. Continuing, but chances to succeed are limited.
cp lib/Crypt/SSLeay/MainContext.pm blib/lib/Crypt/SSLeay/MainContext.pm
cp lib/Crypt/SSLeay/X509.pm blib/lib/Crypt/SSLeay/X509.pm
cp lib/Crypt/SSLeay/Err.pm blib/lib/Crypt/SSLeay/Err.pm
cp lib/Crypt/SSLeay/Conn.pm blib/lib/Crypt/SSLeay/Conn.pm
cp lib/Crypt/SSLeay/Version.pm blib/lib/Crypt/SSLeay/Version.pm
cp lib/Net/SSL.pm blib/lib/Net/SSL.pm
cp SSLeay.pm blib/lib/Crypt/SSLeay.pm
cp lib/Crypt/SSLeay/CTX.pm blib/lib/Crypt/SSLeay/CTX.pm
Running Mkbootstrap for SSLeay ()
chmod 644 "SSLeay.bs"
"/usr/bin/perl" -MExtUtils::Command::MM -e 'cp_nonempty' -- SSLeay.bs blib/arch/auto/Crypt/SSLeay/SSLeay.bs 644
"/usr/bin/perl" "/usr/share/perl5/core_perl/ExtUtils/xsubpp" -typemap '/usr/share/perl5/core_perl/ExtUtils/typemap' -typemap '/home/archy/.cpan/build/Crypt-SSLeay-0.72-4/typemap' SSLeay.xs > SSLeay.xsc
mv SSLeay.xsc SSLeay.c
cc -c -D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/include/db5.3 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -march=x86-64 -mtune=generic -O2 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=3 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -g -ffile-prefix-map=/build/perl/src=/usr/src/debug/perl -flto=auto -DVERSION=\"0.72\" -DXS_VERSION=\"0.72\" -fPIC "-I/usr/lib/perl5/5.40/core_perl/CORE" SSLeay.c
SSLeay.xs: In function ‘XS_Crypt__SSLeay__CTX_new’:
SSLeay.xs:152:31: error: implicit declaration of function ‘SSLv3_client_method’; did you mean ‘SSLv23_client_method’? [-Wimplicit-function-declaration]
152 | ctx = SSL_CTX_new(SSLv3_client_method());
| ^~~~~~~~~~~~~~~~~~~
| SSLv23_client_method
SSLeay.xs:152:31: error: passing argument 1 of ‘SSL_CTX_new’ makes pointer from integer without a cast [-Wint-conversion]
152 | ctx = SSL_CTX_new(SSLv3_client_method());
| ^~~~~~~~~~~~~~~~~~~~~
| |
| int
In file included from SSLeay.xs:35:
/usr/include/openssl/ssl.h:1606:47: note: expected ‘const SSL_METHOD *’ {aka ‘const struct ssl_method_st *’} but argument is of type ‘int’
1606 | __owur SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth);
| ~~~~~~~~~~~~~~~~~~^~~~
SSLeay.xs:157:31: error: implicit declaration of function ‘SSLv2_client_method’; did you mean ‘SSLv23_client_method’? [-Wimplicit-function-declaration]
157 | ctx = SSL_CTX_new(SSLv2_client_method());
| ^~~~~~~~~~~~~~~~~~~
| SSLv23_client_method
SSLeay.xs:157:31: error: passing argument 1 of ‘SSL_CTX_new’ makes pointer from integer without a cast [-Wint-conversion]
157 | ctx = SSL_CTX_new(SSLv2_client_method());
| ^~~~~~~~~~~~~~~~~~~~~
| |
| int
/usr/include/openssl/ssl.h:1606:47: note: expected ‘const SSL_METHOD *’ {aka ‘const struct ssl_method_st *’} but argument is of type ‘int’
1606 | __owur SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth);
| ~~~~~~~~~~~~~~~~~~^~~~
make: *** [Makefile:354: SSLeay.o] Error 1
NANIS/Crypt-SSLeay-0.72.tar.gz
/usr/bin/make -- NOT OK
The output to the terminal is too long, so i just put the important part
I need to install this module in order to run nikto with SSL. According to nikto docs i could either install Net::SSL or Net::SSLeay.
When i try to install Net::SSL i get as well the error above, but when doing so with Net::SSLeay everything sees fine in the output.
But runnning r in the cpan shell does not show any module seemed to Net::SSLeay
What can i do ?
Perl: perl 5, version 40, subversion 0 (v5.40.0) built for x86_64-linux-thread-multi OS: Arch Linux x64
Published by steve-m-hay on Monday 20 January 2025 17:24
Update perldelta for ExtUtils::ParseXS
Published by steve-m-hay on Monday 20 January 2025 17:23
Update Module::CoreList for ExtUtils::ParseXS
Published by mehall on Monday 20 January 2025 14:09
I believe it is telling me that the changes2pm.pl is missing. isn't that file suppose to be in the cpan file/module downloaded when cpan starts the install? who/where would i point this out to the developers? more importantly how do i get it to install? couple months back the DBI installed with no issues. should i try to install the last version of DBI or will that open another can of worms so to speak. i really don't like using the --force option for anything and no i don't want to use activestate. any way this is what i'm getting.
>cpan DBI
...
Checking if your kit is complete...
Looks good
I see you're using perl 5.038000 on MSWin32-x64-multi-thread, okay.
Remember to actually *read* the README file!
Use 'make' to build the software (dmake or nmake on Windows).
Then 'make test' to execute self tests.
Then 'make install' to install the DBI and then delete this working
directory before unpacking and building any DBD::* drivers.
Windows users need to use the correct make command.
That may be nmake or dmake depending on which Perl you are using.
If using the Win32 ActiveState build then it is recommended that you
use the ppm utility to fetch and install a prebuilt DBI instead.
Generating a gmake-style Makefile
Writing Makefile for DBI
Writing MYMETA.yml and MYMETA.json
HMBRAND/DBI-1.646.tar.gz
C:\progs\sp5038000001\perl\bin\perl.exe Makefile.PL -- OK
Running make for H/HM/HMBRAND/DBI-1.646.tar.gz
gmake: Circular Changes <- lib\DBI\Changes.pm dependency dropped.
"C:\progs\sp5038000001\perl\bin\perl.exe" -MExtUtils::Command -e mkpath -- blib\lib\DBI
"C:\progs\sp5038000001\perl\bin\perl.exe" -MExtUtils::Command -e rm_f -- lib\DBI\Changes.pm blib\lib\DBI\Changes.pm
perl changes2pm.pl
Can't open perl script "changes2pm.pl": No such file or directory
gmake: *** [makefile:416: lib\DBI\Changes.pm] Error 2
HMBRAND/DBI-1.646.tar.gz
C:\progs\SP5038~1\c\bin\gmake.exe -- NOT OK
Stopping: 'install' failed for 'DBI'.
Any help much appreciated. fyi I'm on windows 10 64 bit os. intel i5 quad. 8 gig of ram.
Since I don't have a github account nor do I want one I have done multiple searches for a work around but I do not want to use a force option and I don't trust activestate to give me a clean Perl build.
Published by khwilliamson on Monday 20 January 2025 13:57
perlintern: Document NEGATE_2IV and kin And clean up the text in their comments
Published by /u/briandfoy on Monday 20 January 2025 12:31
submitted by /u/briandfoy [link] [comments] |
Published by Gabor Szabo on Monday 20 January 2025 06:44
Originally published at Perl Weekly 704
Hi there,
Do you know of any active Perl Podcast?
It reminds me when Lance Wicks reached out to Joshua McAdams to revive Perlcast in 2018. I later joined hands with Lance and we even recorded the first podcast with Neil Bowers. Unfortunately it didn't work out. Fast forward, in the new year 2025, we have another very active member, Philippe Bruhat, who came up with The Underbar, Podcast for Perl. There is already Episode 0 - A New Logo for Perl is published which was recorded on January 10th, 2025. You should listen to it and share your feedbacks to the creator. Also please do spread the word about it.
This year German Perl/Raku Workshop is happening in the beautiful city Munich. Even if German is not your first language, you can still benefit from the presentations. I attended the workshop in the year 2018, my first time. I enjoyed the 3-days event thoroughly. I even gave couple of presentations in English. For me, it was the golden opportunity to meet so many Perl stalwarts. I made many friends for life. Buy your tickets now, it is starting on 12th May 2025 and ends on 14th May 2025.
There is another good news in the year 2025, The Weekly Challenge has secured sponsor for another 12 months, thanks to Lance Wicks for the second year back to back.
Stay safe and enjoy rest of the newsletter.
--
Your editor: Mohammad Sajid Anwar.
Announcement of the new Perl module DateTime::Format::RelativeTime, which is designed to mirror its equivalent Web API Intl.RelativeTimeFormat
Here is the guide how to write safe, fast, convenient, and powerful XS libraries.
Please explore the latest release.
For all music lovers, here is your opportunity to use Perl for fun. Really cool tool, you must try.
Imagine you want to parse free-form address input and match it against a database representing the road network. At Geolytica, Perl is used to manage and enhance vast geo-location datasets and build the application logic of the geocoding engines.
Serious profiling discussed in 2 part series post.
Continued the discussion of profiling Peak DRAM in this second post of the series.
Please checkout the reply for more detailed answer.
Interesting use case and the solution too.
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 "Binary Prefix" and "Alien Dictionary". 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 "Arrange Binary" and "Maximum Average" tasks in Perl and Raku. You will find plenty of solutions to keep you busy.
Cool use of copy parameters where you can modify the contents. Raku Rocks.
Interesting analogy of week 304 with the HTTP status code. Always fun to read the blog post.
For a change, I didn't see any magics used this week. Pretty straight forward approach both in Perl and Raku. Keep it up great work.
Pure regex solution and compact too with plenty of discussion. Highly recommended.
Two simple and easy to follow solutions in Raku. You get enough discussion to get there.
Breaking down task into subtask is always very handy. Have the one-liner and a complete solutions in the end. Great work.
Another demo of regex solution, very clever attempt. You must checkout the solution.
Nice hack to make the task easier. DIY tool lets you play with it too.
Use of goto, interesting. I know it is not commonly used these days. Check it out yourself.
Simple use of loop in Kotlin gave us the solution straight away. New to Kotlin? You don't want to skip it.
Great CPAN modules released last week;
MetaCPAN weekly report.
This Virtual event is going to take place today, January 20, 2025, in Zoom. We will take a look at the GitLab pipelines and will bring a few examples to so who to use it in various situations. Including GitLab pages for building a front-end only web site.
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 type_outcast on Monday 20 January 2025 05:55
I'm converting an existing Dancer2 app to use Log4perl. In my Dancer2 app's config.yml, I have the following:
logger: log4perl
engines:
logger:
log4perl:
config_file: log4perl.conf
And log4perl.conf is as follows:
log4perl.rootLogger = INFO, LOG1
log4perl.appender.LOG1 = Log::Log4perl::Appender::File
log4perl.appender.LOG1.filename = app_name.log
log4perl.appender.LOG1.mode = append
log4perl.appender.LOG1.layout = Log::Log4perl::Layout::PatternLayout
log4perl.appender.LOG1.layout.ConversionPattern = %d %p %m %n
(This is very similar to the example given in Dancer2::Logger::Log4perl's POD.)
When I run the app via plackup
or use
the module in test scripts, it reports loading my configuration (and will throw errors if I have anything invalid in config.yml, but I'm not sure it's using log4perl at all, or ever reading log4perl.conf (no error is reported, even if I move log4perl.conf away entirely). app_name.log is never created, and all of my app's log messages are sent to the console as if I had no logging configuration at all. There are no errors or warnings; the app just loads and ignores my logger config.
I have double checked all relevant modules are installed (though I would hope something would throw a warning otherwise), including Dancer2::Logger::Log4perl, Log::Log4perl and all of the Appender and Layout modules shown above. Running on Perl 5.40.0.
I've also started a new Dancer2 app from scratch with the same logger config, with the same result, so it's not some leftover cruft from elsewhere in my larger application.
What am I doing wrong?
Published by Ron Savage on Monday 20 January 2025 04:34
Published on Monday 20 January 2025 01:35
Published by iabyn on Monday 20 January 2025 00:13
[MERGE] ParseXS: heavily refactor generate_output generate_output() is the function which emits the C which returns the value of a variable. This series of 53 commits simplifies and reorganises that method so that it is much more regular, and with fewer special cases. For example, the code to handle 'OUTLIST var' now shares the same code path as RETVAL, so it can now also take advantage of the various optimisations which used to apply only to RETVAL, such as using TARG. Many more tests have also been added. Also, generate_output() has been renamed to as_output_code() and becomes a method of ExtUtils::ParseXS::Node::Param: part of the gradual shift towards having an AST and methods which act upon it. There is still more work to be done in this area of code generation, but this branch is long enough already, so I will return to it later.
Published by iabyn on Monday 20 January 2025 00:11
ParseXS: fix OUTLIST/OUTPUT override issue I introduced a bug during recent code refactoring. For the specific (and unusual) case of a void XSUB with a single parameter which was both OUTLIST and also had an OUTPUT entry with a code override: foo(IN_OUTLIST int A) OUTPUT: A setA(ST[99], A); the OUTLIST would fail to emit the code to set the new mortal to be stored at ST(0) to the value of A.
Published by Matthias Muth on Sunday 19 January 2025 23:53
Challenge 304 solutions in Perl by Matthias Muth
These are my Challenge 304 Task 1 and 2 solutions in Perl
for The Weekly Challenge.
Thank you, Mohammad Sajid Anwar for two more great challenges this week!
You are given a list of binary digits (0 and 1) and a positive integer, \$n.
Write a script to return true if you can re-arrange the list by replacing at least \$n digits with 1 in the given list so that no two consecutive digits are 1 otherwise return false.Example 1
Input: @digits = (1, 0, 0, 0, 1), $n = 1 Output: true Re-arranged list: (1, 0, 1, 0, 1)
Example 2Input: @digits = (1, 0, 0, 0, 1), $n = 2 Output: false
The task description contains the words 'replacing' and 'digits'.
The first thing that I think of when I hear 'replacing' in a Perl context is 'What's the regular expression that I can use?'. So here we go.
The array of digits is easily turned into a string of zeroes and ones by join
ing together the array elements:
my $string = join "", $digits->@*;
Then, we match the places where we can put in ones instead of zeroes.
The condition for replacing a '0'
by a '1'
is that neither left of it nor right of it there is a '1'
already.
We can translate this directly to a regular expression, using a negative lookbehind and a negative lookahead. Using the x
modifier to make those more easily recognizable:
$string =~ s/ (?<!1) 0 (?!1) /1/x
We cannot simply use the g
modifier to replace all those zeroes in one go, because our substitutions are not taken into account for finding the next match. For sequences like '00000'
we would end up with '11111'
, because every '0'
in the original string fulfils the critera to be replaced by a '1'
.
That means that we need to put the substitution into a while
loop. Within the loop body, we decrement $n
by one, because we were able to do a replacement.
So:
while ( $string =~ s/ (?<!1) 0 (?!1) /1/x ) {
--$n;
}
In production software, or with longer strings, I would probably try to avoid restarting the search for the next match at the beginning of the string again and again. I would probably do so by setting pos $string
to the place behind the recent successful replacement, before re-iterating.
For the challenge examples, this here works well enough already:
use v5.36;
sub arrange_binary( $digits, $n ) {
my $string = join "", $digits->@*;
while ( $string =~ s/ (?<!1) 0 (?!1) /1/x ) {
--$n;
}
return $n <= 0;
}
If you have your own solution, you might try with these additional testcases:
use Test2::V0 qw( -no_srand );
is arrange_binary( [1, 0, 0, 0, 1], 1 ), T,
'Example 1: arrange_binary( [1, 0, 0, 0, 1], 1 ) is true';
is arrange_binary( [1, 0, 0, 0, 1], 2 ), F,
'Example 2: arrange_binary( [1, 0, 0, 0, 1], 2 ) is false';
is arrange_binary( [], 0 ), T,
'Test 1: arrange_binary( [], 0 ) is true';
is arrange_binary( [], 1 ), F,
'Test 2: arrange_binary( [], 1 ) is false';
is arrange_binary( [ 0 ], 1 ), T,
'Test 3: arrange_binary( [ 0 ], 1 ) is true';
is arrange_binary( [ 1 ], 1 ), F,
'Test 4: arrange_binary( [ 1 ], 1 ) is false';
is arrange_binary( [ 0 ], 2 ), F,
'Test 5: arrange_binary( [ 0 ], 2 ) is false';
is arrange_binary( [0, 0], 1 ), T,
'Test 6: arrange_binary( [0, 0], 1 ) is true';
is arrange_binary( [0, 0], 2 ), F,
'Test 7: arrange_binary( [0, 0], 2 ) is false';
is arrange_binary( [0, 1], 1 ), F,
'Test 8: arrange_binary( [0, 1], 1 ) is false';
is arrange_binary( [0, 0, 0], 2 ), T,
'Test 9: arrange_binary( [0, 0, 0], 2 ) is true';
is arrange_binary( [1, 0, 0], 1 ), T,
'Test 10: arrange_binary( [1, 0, 0], 1 ) is true';
is arrange_binary( [0, 1, 0], 1 ), F,
'Test 11: arrange_binary( [0, 1, 0], 1 ) is false';
is arrange_binary( [0, 0, 1], 1 ), T,
'Test 12: arrange_binary( [0, 0, 1], 1 ) is true';
is arrange_binary( [0, 0, 0, 0, 0, 0], 3 ), T,
'Test 13: arrange_binary( [0, 0, 0, 0, 0], 3 ) is true';
is arrange_binary( [0, 0, 0, 0, 0, 0], 4 ), F,
'Test 14: arrange_binary( [0, 0, 0, 0, 0], 4 ) is false';
done_testing;
You are given an array of integers, @ints and an integer, $n which is less than or equal to total elements in the given array.
Write a script to find the contiguous subarray whose length is the given integer, $n, and has the maximum average. It should return the average.Example 1
Input: @ints = (1, 12, -5, -6, 50, 3), \$n = 4 Output: 12.75 Subarray: (12, -5, -6, 50) Average: (12 - 5 - 6 + 50) / 4 = 12.75
Example 2Input: @ints = (5), \$n = 1 Output: 5
So finding 'contiguous subarrays'.
Good that we know the length of each of those subarrays -- it's $n
.
Which makes it easier, because we don't need to find all possible contiguous subarrays of the input arrays, but only those with that given length.
What we really do is a 'sliding window' search, taking the first $n
elements of the array, then the $n
elements starting with the second element, and so on.
In a loop, the first starting index for the sliding window is 0
, of course.
The last index is the one that uses the last $n
elements for the window.
If we have a window size of $n == 1
, the last window starts at $ints->$#*
. Generalizing from there, the last index for any $n
is $ints->$#* - ( $n - 1 )
.
To get the values in the window, we can simply use Perl's arrays slice construct. Starting at index $_
, the window is @int[ $_ .. ( $_ + $n - 1 ) ]
.
I put this together into one statement, using map
to iterate through the windows, sum
to sum up each window, max
to find the largest window sum, and / $n
to reduce that to the largest average.
In my environment, the array data are handed into the subroutine as an arrayref, so instead of @ints
we have $ints
pointing to the data.
I prefer using the 'Postfix Dereferencing Syntax' ($ints->@*[...]
) for accessing the array data (and also $ints->$#*
for the last index into that array), because I find it easier to read and more logical to write most of the times.
So here it is:
use v5.36;
use List::Util qw( sum max );
sub maximum_average( $ints, $n ) {
return max(
map sum( $ints->@[ $_ .. ( $_ + $n - 1 ) ] ),
0 .. ( $ints->$#* - ( $n - 1 ) )
) / $n;
}
Find the complete source code for both tasks, including tests, on Github.
There are several competing philosophies for wrapping external C libraries. One is that the XS module should hide all the details of the library and provide a clean “Perlish interface”. The opposite extreme is that the external C functions should be exposed to Perl using an extremely minimal XS layer, or the Foreign Function Interface (FFI) and all the logic for working with the library should be written in Perl.
I advocate something in the middle. I think that a good interface should expose as much of the low-level as possible (to make the most usage of that library possible by other Perl modules) while “padding the sharp edges” so that it is difficult for Perl-side usage to crash the program. Higher level features can be provided in addition to the low level API via XS, Perl modules, or both.
If you consider that the average C library is an awkward mess of state machines and lightly-enforced state requirements that will segfault if not carefully obeyed, wrapping that nicely for the Perl developer is going to require a lot of data translation and runtime sanity checks. If you skip those runtime sanity checks in your wrapper library, it drags down the efficiency of your subsequent Perl development to the level of C development, which is to say, sitting around scratching your head for hours wondering why the program keeps segfaulting. (or attaching gdb to your debug build of perl) If you write those runtime checks in Perl, like with the FFI approach, your runtime performance can suffer significantly. If you write those runtime checks in XS, you can actually do quite a lot of them before there’s any notable decrease in the performance of the script.
Meanwhile, C code runs an order of magnitude faster than Perl opcodes, so if you’re going to require the end user to use a compiled module already, I feel it makes sense to put as much of the higher-level routines into XS as you have time for. But, the higher level routines shouldn’t be at the expense of the lower-level ones, or else you limit what people can do with the library.
This guide will explain all the tricks I know to write safe, fast, convenient, and powerful XS libraries.
(If you spot anything wrong, or want to contribute suggestions, open an issue at the GitHub repo
One of the first things you’ll need to do for any C library which allocates “objects” is to bind them to a matching Perl object, usually a blessed scalar ref or hash ref. (The C language doesn’t have official objects of course, but a library often allocates a struct or opaque pointer with a lifespan and a destructor function that they expect you to call when you’re done with it, which is the same theme as an object.)
If you read through the common tutorials, you’ll probably see a recipe like
SV*
new(class, some_data)
SV *class;
IV some_data;
INIT:
LibWhaever_obj *obj;
CODE:
obj= LibWhaever_create(some_data);
if (!obj) croak("LibWhaever_create failed");
RETVAL= (SV*) newRV_noinc(newSViv((IV)obj));
sv_bless(RETVAL,
gv_stashpv("YourProject::LibWhatever", GV_ADD));
OUTPUT:
RETVAL
void
DESTROY(self)
SV *self;
INIT:
LibWhaever_obj *obj;
PPCODE:
obj= (LibWhaever_obj*) SvIV(SvRV(self));
LibWhaever_destroy(obj);
XSRETURN(0);
This is about the least effort/overhead you can have for binding a C data structure to a Perl blessed scalar ref, and freeing it when the Perl object goes out of scope. (you can also move some of this code to the typemap, but I’ll come back to that later)
I don’t like this pattern for several reasons:
dclone
, it happily makes a copy of
your scalar ref and then when the first object goes out of scope it runs the
destructor, and the other object is now referring to freed memory and will
probably segfault during its next use.$self->SUPER::DESTROY
, leaking the C object.DESTROY
call.While most of these scenarios shouldn’t happen, if by unfortunate circumstances they do happen, someone loses a bunch of hours debugging it, especially if they aren’t the XS author and don’t know about these pitfalls.
A much more reliable way to link the C structs to the Perl blessed refs is through Perl’s “magic” system. Magic is the name for essentially a pointer within the SV/AV/HV of your object which points to a linked list of C metadata. This metadata describes various things, like operator-overloading or ties or other low-level Perl features. One type of magic is reserved for “extensions” (that’s you!)
There is a fair amount of effort and boilerplate to set up magic on your objects, but consider these benefits:
With that in mind, lets begin suffering through the details.
Magic is described with “struct MGVTBL”:
static int
YourProject_LibWhatever_magic_free(pTHX_ SV* sv, MAGIC* mg) {
LibWhatever_obj *obj= (LibWhatever_obj*) mg->mg_ptr;
LibWhatever_destroy(obj);
}
#ifdef USE_ITHREADS
static int
YourProject_LibWhatever_magic_dup(pTHX_ MAGIC *mg,
CLONE_PARAMS *param)
{
croak("This object cannot be shared between threads");
return 0;
};
#else
#define YourProject_LibWhatever_magic_dup 0
#endif
// magic table for YourProject::LibWhatever
static MGVTBL YourProject_LibWhatever_magic_vtbl= {
0, /* get */
0, /* set */
0, /* length */
0, /* clear */
YourProject_LibWhatever_magic_free, /* free */
#ifdef MGf_COPY
0, /* copy magic to new variable */
#endif
#ifdef MGf_DUP
YourProject_LibWhatever_magic_dup /* dup for new threads */
#endif
#ifdef MGf_LOCAL
,0 /* local */
#endif
};
You only need one static instance for each type of magic your module creates. It’s just metadata telling Perl how to handle your particular type of extension magic. The ifdefs are from past versions of the struct that had fewer fields, though if your module is requiring Perl 5.8 you can assume ‘copy’ and ‘dup’ exist, and from 5.10 ‘local’ always exists as well.
Next, the recipe to attach it to a new Perl object:
SV * my_wrapper(LibWhatever_obj *cstruct) {
SV *obj, *objref;
MAGIC *magic;
obj= newSV(0); // or newHV() or newAV()
objref= newRV_noinc(obj);
sv_bless(objref, gv_stashpv("YourProject::LibWhatever", GV_ADD));
magic= sv_magicext(
obj, // the inner SV/AV/HV, not the ref to it
NULL,
PERL_MAGIC_ext, // "extension magic"
&YourProject_LibWhatever_magic_vtbl, // show perl your functions
(const char*) cstruct, // your custom pointer
0);
#ifdef USE_ITHREADS
magic->mg_flags |= MGf_DUP;
#else
(void)magic; // suppress warning
#endif
return objref;
}
The key there is ‘sv_magicext’. Note that you’re applying it to the thing being
referred to, not the scalar ref that you use for the call to sv_bless
.
The messy ifdef
part is due to the ‘dup’ field of the magic table only being
used when perl was compiled with threading support. The reference to
YourProject_LibWhatever_magic_vtbl
is both an instruction for Perl to know what
functions to call, but also a unique value used to identify your extension
magic from anyone else’s.
To read your pointer back from an SV provided to you, the recipe is:
LibWhatever_obj* YourProject_LibWhatever_from_magic(SV *objref) {
SV *sv;
MAGIC* magic;
if (SvROK(objref)) {
sv= SvRV(objref);
if (SvMAGICAL(sv)) {
// Iterate magic attached to this scalar to find our vtable
for (magic= SvMAGIC(sv); magic; magic = magic->mg_moremagic)
if (magic->mg_type == PERL_MAGIC_ext
&& magic->mg_virtual == &YourProject_LibWhatever_magic_vtbl)
// If found, the mg_ptr points to the fields structure.
return (LibWhatever_obj*) magic->mg_ptr;
}
}
return NULL;
}
This might look a little expensive, but there is likely only one type of magic on your object, so the loop exits on the first iteration, and all you did was “SvROK”, “SvRV”, “SvMAGICAL”, and “SvMAGIC” followed by two comparisons. It’s actually quite a bit faster than verifying the inheritance of the blessed package name.
So there you go - you can now attach your C structs with magic.
In a typical wrapper around a C library, you’re going to be writing a lot of methods
that need to call YourProject_LibWhatever_from_magic
on the first argument. To make
that easier, lets move this decoding step to the typemap.
Without a typemap:
IV
method1(self, param1)
SV *self
IV param1
INIT:
LibWhatever_obj *obj= YouProject_LibWhatever_from_magic(self);
CODE:
if (!obj) croak("Not an instance of LibWhatever");
RETVAL= LibWhatever_method1(obj, param1);
OUTPUT:
RETVAL
With a typemap entry like:
TYPEMAP
LibWhatever_obj* O_LibWhatever_obj
INPUT
O_LibWhatever_obj
$var= YourProject_LibWhatever_from_magic($arg);
if (!$var) croak("Not an instance of LibWhatever");
the XS method becomes
IV
method1(obj, param1)
LibWhatever_obj *obj
IV param1
CODE:
RETVAL= LibWhatever_method1(obj, param1);
OUTPUT:
RETVAL
If you have some functions that take an optional LibWhatever_obj pointer, try this trick:
typedef LibWhatever_obj Maybe_LibWhatever_obj;
...
void
show(obj)
Maybe_LibWhatever_obj *obj
PPCODE:
if (obj) {
printf("...", LibWhatever_get_attr1(obj));
}
else {
printf("NULL");
}
TYPEMAP
LibWhatever_obj* O_LibWhatever_obj
Maybe_LibWhatever_obj* O_Maybe_LibWhatever_obj
INPUT
O_LibWhatever_obj
$var= YourProject_LibWhatever_from_magic($arg);
if (!$var) croak("Not an instance of LibWhatever");
INPUT
O_Maybe_LibWhatever_obj
$var= YourProject_LibWhatever_from_magic($arg);
If you want to save a bit of compiled .so file size, you can move the error message into the ‘from_magic’ function, with a flag:
#define OR_DIE 1
LibWhatever_obj*
YourProject_LibWhatever_from_magic(SV *objref, int flags) {
SV *sv;
MAGIC* magic;
if (SvROK(objref)) {
sv= SvRV(objref);
if (SvMAGICAL(sv)) {
// Iterate magic attached to this scalar to find our vtable
for (magic= SvMAGIC(sv); magic; magic = magic->mg_moremagic)
if (magic->mg_type == PERL_MAGIC_ext
&& magic->mg_virtual == &YourProject_LibWhatever_magic_vtbl)
// If found, the mg_ptr points to the fields structure.
return (LibWhatever_obj*) magic->mg_ptr;
}
}
if (flags & OR_DIE)
croak("Not an instance of LibWhatever");
return NULL;
}
TYPEMAP
LibWhatever_obj* O_LibWhatever_obj
Maybe_LibWhatever_obj* O_Maybe_LibWhatever_obj
INPUT
O_LibWhatever_obj
$var= YourProject_LibWhatever_from_magic($arg, OR_DIE);
INPUT
O_Maybe_LibWhatever_obj
$var= YourProject_LibWhatever_from_magic($arg, 0);
You can play further games with this, like automatically initializing the SV to become
one of your blessed objects if it wasn’t defined, in the style of Perl’s
open my $fh, ...
, or maybe an option to add the magic to an existing object created
by a pure-perl constructor. Do whatever makes sense for your API.
In all the examples so far, I’m storing a single pointer to a type defined in the external C library being wrapped. Chances are, though, you need to store more than just that one pointer.
Imagine a poorly-written C library where you need to call SomeLib_create
to get the
object, then a series of SomeLib_setup
calls before any other function can be used,
then if you want to call SomeLib_go
you have to first call SomeLib_prepare
or else
it segfaults. You could track these states in Perl variables in a hash ref, but it would
just be easier if they were all present in a local C struct of your creation.
So, rather than attaching a pointer to the library struct with magic, you can attach your own allocated struct, and your struct can have a pointer to all the library details. For extra convenience, your struct can also have a pointer to the Perl object which it is attached to, which lets you access that object from other methods you write which won’t have access to the Perl stack.
struct YourProject_objinfo {
SomeLib_obj *obj;
HV *wrapper;
bool started_setup, finished_setup;
bool did_prepare;
};
struct YourProject_objinfo*
YourProject_objinfo_create(HV *wrapper) {
struct YourProject_objinfo *objinfo= NULL;
Newxz(objinfo, 1, struct YourProject_objinfo);
objinfo->wrapper= wrapper;
/* other setup here ... */
return objinfo;
}
void
YourProject_objinfo_free(struct YourProject_objinfo *objinfo) {
if (objinfo->obj) {
SomeLib_obj_destroy(objinfo->obj);
}
/* other cleanup here ... */
Safefree(objinfo);
}
static int YourProject_objinfo_magic_free(pTHX_ SV* sv, MAGIC* mg) {
YourProject_objinfo_free(
(struct YourProject_objinfo *) mg->mg_ptr);
}
One other thing that has changed from the previous scenario is that you can allocate
this struct and attach it to the object whenever you want, instead of waiting for the
user to call the function that creates the instance of SomeLib_obj
. This gives you
more flexible ways to deal with creation of the magic.
Here’s a pattern I like:
#define OR_DIE 1
#define AUTOCREATE 2
struct YourProject_objinfo*
YourProject_objinfo_from_magic(SV *objref, int flags) {
SV *sv;
MAGIC* magic;
if (!sv_isobject(objref))
/* could also check 'sv_derived_from' here, but that's slow */
croak("Not an instance of YourProject");
sv= SvRV(objref);
if (SvMAGICAL(sv)) {
/* Iterate magic attached to this scalar to find our vtable */
for (magic= SvMAGIC(sv); magic; magic = magic->mg_moremagic)
if (magic->mg_type == PERL_MAGIC_ext
&& magic->mg_virtual == &YourProject_objinfo_magic_vtbl)
/* If found, the mg_ptr points to the fields structure. */
return (struct YourProject_objinfo*) magic->mg_ptr;
}
if (flags & AUTOCREATE) {
struct YourProject_objinfo *ret;
if (SvTYPE(sv) != SVt_PVHV)
croak("Expected blessed hashref");
ret= YourProject_objinfo_create((HV*)sv);
magic= sv_magicext(sv, NULL, PERL_MAGIC_ext,
&YourProject_objinfo_magic_vtbl, (const char*) ret, 0);
#ifdef USE_ITHREADS
magic->mg_flags |= MGf_DUP;
#else
(void)magic; // suppress warning
#endif
return ret;
}
if (flags & OR_DIE)
croak("Not an initialized instance of YourProject");
return NULL;
}
typedef struct YourProject_objinfo Maybe_YourProject_objinfo;
typedef struct YourProject_objinfo Auto_YourProject_objinfo;
Then in the typemap:
TYPEMAP
struct YourProject_objinfo* O_YourProject_objinfo
Maybe_YourProject_objinfo* O_Maybe_YourProject_objinfo
Auto_YourProject_objinfo* O_Auto_YourProject_objinfo
INPUT
O_YourProject_objinfo
$var= YourProject_objinfo_from_magic($arg, OR_DIE);
INPUT
O_Maybe_YourProject_objinfo
$var= YourProject_objinfo_from_magic($arg, 0);
INPUT
O_Auto_YourProject_objinfo
$var= YourProject_objinfo_from_magic($arg, AUTOCREATE);
Then use it in your XS methods to conveniently implement your sanity checks for this annoying C library:
# This is called by the pure-perl constructor, after blessing the hashref
void
_init(objinfo, param1, param2)
Auto_YourProject_objinfo* objinfo
IV param1
IV param2
PPCODE:
if (objinfo->obj)
croak("Already initialized");
objinfo->obj= SomeLib_create(param1, param2);
if (!objinfo->obj)
croak("SomeLib_create failed: %s", SomeLib_get_last_error());
XSRETURN(0);
bool
_is_initialized(objinfo)
Maybe_YourProject_objinfo* objinfo
CODE:
RETVAL= objinfo != NULL && objinfo->obj != NULL;
OUTPUT:
RETVAL
void
setup(objinfo, key, val)
struct YourProject_objinfo* objinfo
const char *key
const char *val
PPCODE:
if (objinfo->finished_setup)
croak("Cannot call 'setup' after 'prepare'");
if (!SomeLib_setup(objinfo->obj, key, val))
croak("SomeLib_setup failed: %s", SomeLib_get_last_error());
objinfo->setup_started= true;
XSRETURN(0);
void
prepare(objinfo)
struct YourProject_objinfo* objinfo
PPCODE:
if (!objinfo->started_setup)
croak("Must call setup at least once before 'prepare'");
objinfo->finished_setup= true;
if (!SomeLib_prepare(objinfo->obj))
croak("SomeLib_prepare failed: %s", SomeLib_get_last_error());
objinfo->did_prepare= true;
XSRETURN(0);
void
somelib_go(objinfo)
struct YourProject_objinfo* objinfo
PPCODE:
if (!objinfo->did_prepare)
croak("Must call 'prepare' before 'go'");
if (!SomeLib_go(objinfo->obj))
croak("SomeLib_go failed: %s", SomeLib_get_last_error());
XSRETURN(0);
Like how clean the XS methods got?
When you use the pattern above, your module becomes almost foolproof against misuse. You provide helpful errors for the Perl coder to guide them toward correct usage of the library with easy-to-understand errors (well, depending on how much effort you spend on that) and they don’t have to pull their hair out trying to log all the API calls and compare to the C library documentation to figure out which one happened in the wrong order resulting in a mysterious crash.
The code above is all assuming that the C library is providing objects whose lifespan you are in control of. Many times, the objects from a C library will have some other lifespan that the user can’t directly control with the Perl objects. I’ll cover some techniques for dealing with that in the next article.
Published by chrisarg on Sunday 19 January 2025 18:24
A story in two parts:
Code is released under the MIT license
Published by /u/ReplacementSlight413 on Sunday 19 January 2025 16:42
This is a two part story:
Code is released under the MIT license - feel free to adapt to your use cases (and perhaps someone can provide a Windows version!)
Published by /u/Grinnz on Sunday 19 January 2025 00:28
Published on Sunday 19 January 2025 00:00
In the second part of this we implement the solution that was outlines at the end of Part 1:
The Perl application that implements the first step is straightforward:
ps
command line utility, using the excellent (and safe as it bypasses the shell!)while
loop#!/usr/bin/perl
# Name: monitor_memory.pl
# Purpose: Registers peak DRAM usage of a process
# Usage: perl monitor_memory.pl <PID> <interval_in_seconds> <pidfile>
# Example: perl monitor_memory.pl 12345 1 /tmp/pidfile
# Date: January 19th 2025
# Author: Christos Argyropoulos
# License: MIT https://mit-license.org/
use strict;
use warnings;
use Time::HiRes qw(nanosleep); # high res waiting
use IPC::System::Simple qw(capturex); # safe capture that bypasses the shell
# Process the command line
my ( $pid, $interval_sec, $pidfile ) = @ARGV;
die "Usage: $0 <PID> <interval_in_seconds> <pidfile>\n"
unless defined $pid && defined $interval_sec && defined $pidfile;
# Write our own PID that R will use to kill the Perl application
open( my $fh, '>', $pidfile ) or die "Can't write to $pidfile: $!";
print $fh "$$\n";
close $fh;
# Obtain initial memory usage
my $interval = $interval_sec * 1_000_000_000;
my $initial_mem = capturex('ps', 'o', 'rss=', 'p', $pid);
chomp($initial_mem);
my $max_delta = 0;
# Register the INT and TERM signal handlers to print peak and initial DRAM usage
$SIG{INT} = $SIG{TERM} = sub {
print "$max_delta\t$initial_mem\n";
exit 0;
};
# Obtain the RSS, store the maximum delta up to this point, sleep and re-awaken
while (1) {
my $current = capturex('ps', 'o', 'rss=', 'p', $pid);
chomp($current);
my $delta = $current - $initial_mem;
$max_delta = $delta if $delta > $max_delta;
nanosleep($interval);
}
Having delegated the peak DRAM monitoring to Perl, R is now free to obtain execution timings and memory allocation using Henrik Bengtsson’s excellent profmem
package
The package’s vignette may be found here and explains what id does and how this works (emphasis is mine):
The profmem() function uses the utils::Rprofmem() function for logging memory allocation events to a temporary file. The logged events are parsed and returned as an in-memory R object in a format that is convenient to work with. All memory allocations that are done via the native allocVector3() part of R’s native API are logged, which means that nearly all memory allocations are logged. Any objects allocated this way are automatically deallocated by R’s garbage collector at some point. Garbage collection events are not logged **by profmem(). **Allocations not logged are those done by non-R native libraries or R packages that use native code Calloc() / Free() for internal objects. Such objects are not handled by the R garbage collector.
Based on this description, monitor_memory.pl
and profmem
are complementary: the former will log peak running memory use, while the latter will log in all
allocations, and the combination will provide the best of both worlds.
The tricky part in the implementation is how to ensure that one does end up with an orphan Perl process
when the long calculation throws an error, and thus avoid having to kill the orphan somehow (Unix terminology is so violent). However, the Perl side should not
have to worry about the prospect of becoming an orphan, or detecting it has become one. Killing the Perl process (orphan or not), is a task for R, but Perl has
to help out, by providing R with the PID of the Perl application. Completion of the communication loop is a necessary, but not sufficient condition for culling
the orphan and R has to do its part when implementing the 2nd and 4th step of the top level logic. Here is where R’s tryCatch-finally
facilities come to shine:
# Benchmarking allocations, peak DRAM use and execution timing of an expression in R
bench_time_mem<-function(x) {
gc(reset=FALSE,verbose=FALSE) ## force a gc here
pidfile <- tempfile() # temp file to story Perl's PID
outfile <- tempfile() # stdout redirection for Perl
pid <- Sys.getpid() # get R's PID
ret<-NULL
system2("./monitor_memory.pl",
c(pid,step_of_monitor,pidfile),wait=FALSE,stdout=outfile)
Sys.sleep(0.2) # Wait for PID file to be written
monitor_pid <- readLines(pidfile)[1] # Get Perl's PID
tryCatch (
expr = {
mem<-profmem(time<-system.time(x,gcFirst = FALSE))
rettime<-c(time)
names(rettime)<-names(time)
retval<-c(time,"R_gc_alloc" = sum(mem$bytes,na.rm=T))
}, # execute R expression, get timing and allocations
finally = {
system2("kill",c("-TERM", monitor_pid)) # kill the ? orphan
Sys.sleep(0.2) # Wait for Perl to finish logging
memstats<-read.csv(outfile,sep="\t",
header=FALSE) # get memory statistics
unlink(c(pidfile,outfile)) # cleanup files
retval<-c(retval ,
"delta"= memstats[1,1]*1024,
"initial"= memstats[1,2]*1024
)
}
)
return(retval)
}
The logic of the R partner is fairly straightforward:
monitor_memory.pl
to store its PIDmonitor_memory.pl
and put it in the backgroundtryCatch
bloc and obtain allocations and execution timings with mem<-profmem(time<-system.time(x,gcFirst = FALSE))
finally
block. This code will be executed even if errors are encountered in the user’s expression, and hence no orphan will be allowed to liveLet’s look at a complete example written as a R script with command line parsing to see what we have achieved. For this example, we will use the two sequential and the one large allocation we considered in Part 1 :
# Script Name: profile_time_memory.R
# Purpose: illustrate how to profile time and memory usage in R
# Usage: Rscript profile_time_memory.R --sleep_time 1 --size 1E6 --monitor_step 0.001
# Date: January 19th 2025
# Author: Christos Argyropoulos
# License: MIT https://mit-license.org/
library(profmem)
library(getopt)
# Define command line options
spec <- matrix(c(
'sleep_time', 's', 1, "numeric", "Sleep time in seconds [default 1]",
'size', 'n', 1, "numeric", "Size of the problem N [default 1E6]",
'monitor_step', 'm', 1, "numeric", "Monitoring step in seconds [default 0.001]",
'help', 'h', 0, "logical", "Show this help message and exit"
), byrow = TRUE, ncol = 5)
# Parse command line options
opt <- getopt(spec)
# Assign command line arguments to variables
if(is.null(opt$sleep_time)) opt$sleep_time<-1
if(is.null(opt$size)) opt$size<-1E6
if(is.null(opt$monitor_step)) opt$monitor_step<-0.001
sleep_time <- opt$sleep_time
N <- opt$size
step_of_monitor <- opt$monitor_step
busy_wait <- function(seconds) {
start_time <- Sys.time()
stop_time <- Sys.time()
while (difftime(stop_time, start_time, units = "secs") < seconds) {
stop_time <- Sys.time()
}
}
bench_time_mem<-function(x) {
gc(reset=FALSE,verbose=FALSE) ## force a gc here
pidfile <- tempfile() # temp file to story Perl's PID
outfile <- tempfile() # stdout redirection for Perl
pid <- Sys.getpid() # get R's PID
ret<-NULL
system2("./monitor_memory.pl",
c(pid,step_of_monitor,pidfile),wait=FALSE,stdout=outfile)
Sys.sleep(0.2) # Wait for PID file to be written
monitor_pid <- readLines(pidfile)[1] # Get Perl's PID
tryCatch (
expr = {
mem<-profmem(time<-system.time(x,gcFirst = FALSE))
rettime<-c(time)
names(rettime)<-names(time)
retval<-c(time,"R_gc_alloc" = sum(mem$bytes,na.rm=T))
}, # execute R expression, get timing and allocations
finally = {
system2("kill",c("-TERM", monitor_pid)) # kill the ? orphan
Sys.sleep(0.2) # Wait for Perl to finish logging
memstats<-read.csv(outfile,sep="\t",
header=FALSE) # get memory statistics
unlink(c(pidfile,outfile)) #cleanup files
retval<-c(retval ,
"delta"= memstats[1,1]*1024,
"initial"= memstats[1,2]*1024
)
}
)
return(retval)
}
val<-bench_time_mem(
{
busy_wait(sleep_time);
cat("\nAllocating\n")
q<-rnorm(N);
busy_wait(sleep_time);
rm(q);gc(reset=F)
q2<-rnorm(N);
busy_wait(sleep_time);
rm(q2);gc(reset=F)
}
)
valcp<-bench_time_mem(
{
busy_wait(sleep_time);
cat("\nAllocating\n")
q<-rnorm(N*2);
busy_wait(sleep_time*2);
}
)
cat("\n Allocating a double vector of length N = ",N," in R")
cat("\n with busy waiting period T = ",sleep_time," seconds")
cat("\n and monitoring memory every : ",step_of_monitor," seconds")
cat("\n Will allocate N->gc->N and then 2N at once\n")
cat(paste(rep("=",65),collapse=""))
cat("\n Measured vs alloc'd in R (2N at once): ", valcp["delta"]/valcp["R_gc_alloc"])
cat("\n Measured vs alloc'd in R (N-> gc ->N): ", val["delta"]/val["R_gc_alloc"])
cat("\n")
cat("\n Performance of the two allocations : ")
cat("\n N-> gc ->N : \n")
print(val)
cat("\n 2N at once : \n")
print(valcp)
Running this from the command line we obtain the following output:
Rscript --vanilla profile_time_memory.R -s 1 -n 1000000 -m 0.00001
Allocating
Allocating
Allocating a double vector of length N = 1e+06 in R
with busy waiting period T = 1 seconds
and monitoring memory every : 1e-05 seconds
Will allocate N->gc->N and then 2N at once
=================================================================
Measured vs alloc'd in R (2N at once): 0.995325
Measured vs alloc'd in R (N-> gc ->N): 0.4750328
Performance of the two allocations :
N-> gc ->N :
user.self sys.self elapsed user.child sys.child R_gc_alloc
3.135 0.022 3.163 0.000 0.000 16210416.000
delta initial
7700480.000 76742656.000
2N at once :
user.self sys.self elapsed user.child sys.child R_gc_alloc
3.088 0.017 3.110 0.000 0.000 16000048.000
delta initial
15925248.000 84443136.000
As you can see the Perl monitor identified that the peak memory use of the sequential allocation-deallocation-allocation was half than of a single-step allocation of the entire workspace.
The Perl application’s ability to monitor the process in very fine resolution (theoretically up to nanosec, but reallistically one microsecond is where I’d draw the limit), comes handy when one
has to monitor spikes in peak memory usage. Consider the example, in which some recently allocated memory is used for a fast task and then discarded (this is simulated by providing a small value to the s
argument of the script).
At low resolution, our estimates of the peak memory use are biased, e.g. :
Rscript --vanilla profile_time_memory.R -s .001 -n 1000000 -m 0.1
Allocating
Allocating
Allocating a double vector of length N = 1e+06 in R
with busy waiting period T = 0.001 seconds
and monitoring memory every : 0.1 seconds
Will allocate N->gc->N and then 2N at once
=================================================================
Measured vs alloc'd in R (2N at once): 0.07372778
Measured vs alloc'd in R (N-> gc ->N): 0.3777522
Performance of the two allocations :
N-> gc ->N :
user.self sys.self elapsed user.child sys.child R_gc_alloc
0.154 0.012 0.165 0.000 0.000 16210416.000
delta initial
6123520.000 76742656.000
2N at once :
user.self sys.self elapsed user.child sys.child R_gc_alloc
0.108 0.011 0.119 0.000 0.000 16000048.000
delta initial
1179648.000 84635648.000
However changing the resolution of monitoring re-establishes accuracy:
Rscript --vanilla profile_time_memory.R -s .001 -n 1000000 -m 0.0001
Allocating
Allocating
Allocating a double vector of length N = 1e+06 in R
with busy waiting period T = 0.001 seconds
and monitoring memory every : 1e-04 seconds
Will allocate N->gc->N and then 2N at once
=================================================================
Measured vs alloc'd in R (2N at once): 0.995325
Measured vs alloc'd in R (N-> gc ->N): 0.4750328
Performance of the two allocations :
N-> gc ->N :
user.self sys.self elapsed user.child sys.child R_gc_alloc
0.180 0.007 0.188 0.000 0.000 16210416.000
delta initial
7700480.000 76746752.000
2N at once :
user.self sys.self elapsed user.child sys.child R_gc_alloc
0.119 0.009 0.128 0.000 0.000 16000048.000
delta initial
15925248.000 84447232.000
Having described the solution, let’s provide some limitations and a context of use that acknowledges these limitations and some extensions:
ps
for dataps
command line utility), and Windows, e.g. run R under WSL2
or use tasklist
instead of ps
(another possible extension)I hope you enjoyed this journey with R and Perl so far! Have fun until the next time.
Published by /u/niceperl on Saturday 18 January 2025 20:55
Published by Unknown on Saturday 18 January 2025 21:55
Published by Unknown on Saturday 18 January 2025 21:53
This is the weekly favourites list of CPAN distributions. Votes count: 49
Week's winners (+3): List::MoreUtils
Build date: 2025/01/18 20:44:03 GMT
Clicked for first time:
Increasing its reputation:
Published on Saturday 18 January 2025 00:00
Another year, another opportunity for Perl to excel as a system’s language. Today I decided to take Perl for a (?)wild ride and use it to monitor peak physical DRAM use in a R script. Due to the multi-language nature of the post, there will be a lot of R code in the first part of the series; however, the code is self-explanatory, and should not be difficult to understand (the same applies to the Perl code for those coming from a R background). For those of you who would like to skip the R part (why?), jump straight to the end Part 1 to see the solution and then go to Part 2 for the details.
First, a little bit of background about R’s memory management and the tools that one can use within R to monitor how the process is managing memory. R similar to Perl (?)frees the programmer from having to manage memory manually by providing dynamically allocated containers. R features a garbage collector, which similar to Perl’s uses a reference counting mechanism to return memory back to the operating system. Managing memory in R is as critical as managing memory in Perl, and there are tools available that are built-in the language (the Names and Values in the 2nd edition of the book “Advanced R” is a valuable introduction to memory management, while the Chapter Memory in the first edition of that book is also a useful read). The basic tool used to profile R code is the builtin function Rprof that samples the call stack and writes it out to a log-file that can be subsequently parsed. In the documentation of Rprof, one finds the following disclaimer:
Note that the (timing) interval cannot be too small. With
‘"cpu"’, the time spent in each profiling step is currently added
to the interval. With all profiling events, the computation in
each profiling step causes perturbation to the observed system and
biases the results. What is feasible is machine-dependent. On
Linux, R requires the interval to be at least 10ms, on all other
platforms at least 1ms. Shorter intervals will be rounded up with
a warning.
The relative slow sampling frequencing implies that one must somehow slow the code down to capture peak memory usage. One solution for those of us in Linux systems is to take the hint and release the valgrid Kraken as detailed in Profiling R code for memory use; this will really slow the code down and then we can capture stuff (note that taking the hint, also applies to Perl, i.e. see Test::Valgrind in MetaCPAN). But ultimately, we would like not to slow the code that much, especially if we are also trying to obtain performance information at the same time as we profile the memory.
Assuming that one does not want to use an interactive tool to profile the code (the excellent profvis comes to mind), then one is stuck with the low-level option provided by Rprof logging and summaryRprof parsing. This leads us to the next question: what is the overhead by these tools? Overhead is paid in both hard disk space and execution time. Let’talk about space first: for a high resolution logging (e.g. sampling every 10msec), the log file will grow by ~ 10kb per second of calculation. This may not seem much, but profiling an involved calculation quickly adds up: to profile an expression that takes 10shr to execute will consume 10 x 3600 x 10 = 360000 KB ~ 360MB (incidentally ~10hr is the longest calculation I have ever done in R).To give a sense of measure, the total size of the files in my /var/log is ~1.1GB. And yes, while hard-disks are getting bigger, this is no excuse to fill them with log files! To give an idea of the time overhead, we will need to provide an alternative to hard-disk logging (hint: this will be a Perl program!), but first let’s provide a straightforward R implementation of a logging function.
bench_time_mem<-function(x) { ## x is the R expression to profile
gc(reset=FALSE,verbose=FALSE) ## force a gc here
profing_fname <- tempfile() ## create a temporary file
Rprof(filename=profing_fname, memory.profiling=TRUE,interval=0.01)
time<-system.time(x,gcFirst = FALSE)
Rprof(NULL) ## stop profiling and logging
memprof<-summaryRprof(profing_fname, memory="tseries",lines="hide",diff=TRUE)
mem_alloc<-sum(memprof[-1,"vsize.large"]) ## get memory allocated via malloc
time_prof<-summaryRprof(profing_fname, memory="none",lines="hide",diff=TRUE)
times<-time_prof$by.total[2,-2]
retval<-unlist(c(times[1,1],mem_alloc,file.info(profing_fname)$size))
unlink(profing_fname) ## delete the logfiles
names(retval)<-c("total_time","R_gc_alloc","logfile_size")
retval
}
The code should be self-explanatory even for non R users : it first triggers the garbage collector, and then in sequence creates a temporary file, starts logging to that file, execute the R expression (R code within brackets, similar to how one would provide an anonymous code reference in Perl), then parses the file to get memory usage and timing), and returns the total time, the size of the memory allocated by R in the stack, when running the expression and the size of the log file.
Here is a minimally working example:
## function for busy waiting for a fixed number of seconds
busy_wait <- function(seconds) {
start_time <- Sys.time()
stop_time <- Sys.time()
while (difftime(stop_time, start_time, units = "secs") < seconds) {
stop_time <- Sys.time()
}
}
N<-100000
work_time <- 1 # second(s)
val<-bench_time_mem(
{
busy_wait(work_time); ## work without allocation
cat("\nAllocating\n")
q<-rnorm(N); ## Gaussian random variables
busy_wait(work_time); ## work without allocation
rm(q);gc(reset=F) ## free memory/trigger the gc
q2<-rnorm(N); ## another allocation
busy_wait(work_time); ## busy working!
rm(q2);gc(reset=F) ## free the memory yet again
}
)
In this example, we first provide an implementation of a function busy_wait
that simulates a work load without further allocation.
The actual code to be profiled, alternates periods of busy waiting with allocation and de-allocation. With the values of N
and work_time
in the code snippet, I obtain the following code profile summary:
> val
total_time R_gc_alloc logfile_size
3.4 1600000.0 32446.0
Let’s talk about these figures: first note that parsing the Rprof, does not allow one direct access to the peak memory used during the calculation (only the total amount of memory allocated in the stack. While this information is crucial for performance, i.e. one pays a price for every byte moved around, it is not very informative about whether t he program will even run in the machine, as the datasets scale: R processes data in memory, and when the physical memry is exhausted, the operating system will put the R process out of its misery. In the example above, the peak memory usage while the expression executes is N*8 (since rnorm returns a double precision floating number in C parlance), but since two allocations were done, the profiler cn only report their sum. In fact, the output is virtually indistinguishable from the following code which allocates an array of 2 x N doubles in a single go.
valcp<-bench_time_mem(
{
busy_wait(work_time);
cat("\nAllocating\n")
q<-rnorm(N*2);
rm(q);gc(reset=F)
busy_wait(work_time*2);
}
)
and
valcp
total_time R_gc_alloc logfile_size
3.2 1600000.0 30773.0
Whie both codes allocated the same total amount of memory, the first code would work if one allocated the entirety of the free memory in a given machine, while the second would croak when roughly half the free memory was requested.
The difference of ~0.2sec execution time, is the price one has to pay for triggering R’s garbage collector. The following shows that while allocating an array of 10^5 doubles and filling it with random numbers is very fast (it takes 6msec in my machine), triggering the gaarbage collector is 32x slower.
> system.time(rnorm(N))
user system elapsed
0.006 0.000 0.006
> system.time({q<-rnorm(N);rm(q);gc(reset=FALSE)})
user system elapsed
0.193 0.000 0.194
So how can one get the peak DRAM usage without logging anything to the hard disk? The answer, which will be provided in Part 2, blends together R and Perl and is conceptually very simple, design-wise:
ps
command line utility for resident set size (RSS) i.e. the footprint of the R process in DRAM.(to be continued…)
Published on Friday 17 January 2025 18:58
…let’s just move on shall we ;-)
So you want to be the guy, the one that swoops in to the shop that has been saddled with the legacy Perl application because you’ve been doing Perl since the last century? You know that shop, they have a Perl application and a bunch of developers that only do Python and they’ve suddenly becom allergic to learning something new (to them). From my own experience, here are some of the technologies you’ll encounter and should be familiar with to be the guy.
mod_perl
FastCGI
Moose
HTML::Template
Mason
Template::Toolkit
I checked off the things I’ve encountered in my last three jobs.
Of course, the newer Perl based frameworks are good to know as well:
docker
cpanm
carton
make
bash
…and of these, I think the most common thing you’ll encounter on
sites that run Perl applications is mod_perl
.
Well, maybe not gold, but certainly higher rates and salaries for experienced Perl developers. You’re a unicorn! Strut your stuff. Don’t back down and go cheap. Every day someone leaves the ranks of Perl development only to become one of the herd leaving you to graze alone.
Over the last three years I’ve earned over a half-million dollars in salary and consulting fees. Some of you are probably earning more. Some less. But here’s the bottom line, your skills are becoming scarcer and scarcer. And here’s the kicker…these apps aren’t going away. Companies are loathe to touch some of their cash cows or invest in any kind of “rewrite”. And here’s why…
And here’s what they want you do for a big pile of their cash:
perl
According to the “interweb” the average salary for an experienced Perl developer is around $50/hour or about $100K or so. I’m suspicious of those numbers to be honest. Your mileage may vary but here’s what I’ve been able to get in my last few jobs:
…and I’m not a great negotiator. I do have over 20 years of experience with Perl and over 40 years of experience in IT. I’m not shy about promoting the value of that experience either. I did turn down a job for $155K/year that would have required some technical leadership, a position I think should have been more like $185k/year to lead a team of Perl developers across multiple time zones.
Even if you decide to leave a job or are done with an assignement, don’t burn bridges. Be willing to help them with a transition. Be polite, ask for a recommendation if appropriate. If they’re not planning on rehiring, they may be willing to contract with you for spot assignments.
Imagine you want to parse free-form address input and match it against a database representing the road network.
"751 FAIR OKS AVENUE PASADNA CA"
"751 N Fair Oaks AVE, Pasadena, CA 91103-3069 / 34.158874,-118.151053"
View example"5 Adne Edle Street, London"
"5 THREADNEEDLE STREET, LONDON, United Kingdom EC3V 3NG"
View exampleThe database contains road names, shapes, numbers, zip/postal codes, city names, regions, neighborhood/district names, and more—billions of named location entities worldwide. Add another 100 million points of interest extracted from billions of webpages, and the problem becomes quite difficult.
Two decades of Perl coding, starting in 2005, and this problem is (mostly) solved at Geolytica.
At Geolytica, we harness Perl to manage and enhance vast geo-location datasets and build the application logic of the geocoding engines powering geocoder.ca and geocode.xyz.
We continuously update and enhance our location entities database, because ground truth changes - at the speed of life. One standout example is our work with OpenStreetMap’s POI data. A year ago, we utilized an in-house AI tool, (let’s call it “PerlGPT,”) to refine this dataset, correcting inconsistencies and enhancing data quality. The results were significant enough to share with the community at Free POI Data.
The beauty of Perl lies in its backward compatibility. Despite our codebase spanning over two decades, upgrading Perl across versions has never broken our code. In contrast, with other languages, we’ve observed issues like API changes causing extensive refactoring or even rewrites. Perl’s design allows for seamless integration of old and new code, which is vital for our specific needs.
At Geolytica, tasks like parsing location entities from text involve complex string manipulations. As shown in the examples above, these challenges would be difficult in any programming language, but Perl makes them easier than other options.
The best programming language for any job is the one that makes hard problems easy and impossible ones possible. For Geolytica, that language is Perl.
Ervin Ruci has been immersed in Perl since 1998, initially building student information systems at Mount Allison University, then registry systems for CIRA from 2000 to 2005. In 2005, he became a location-independent entrepreneur, founding Geolytica to tackle the location intelligence problem.
Published by Nicholas Hubbard on Wednesday 15 January 2025 16:13
I recently ran into an interesting bug in my Slackware package manager sbozyp. In this post I will detail the bug and my solution.
Disclaimer: The code snippets in this post do not deal with every edge case and are only meant to convey the ideas relevant to this blog post. All important edge cases are dealt with in the actual code for sbozyp.
Sbozyp is a package manager for SlackBuilds.org, a repository of build scripts for Slackware packages. There is a very important subroutine in sbozyp named build_slackware_pkg()
that (basically) takes the name of a package, builds it into a Slackware package, and returns the path to the built package. To build the package we must execute the packages SlackBuild script. A SlackBuild script always has the general form: { basic setup -> perform build procedure -> conglomerate built parts into a Slackware package }. The last step is always performed by a Slackware-specific tool called makepkg. Unfortunately there is no way (that I know of) to be able to determine the name of the Slackware package before executing the SlackBuild script. For this reason sbozyp must inspect the stdout of the SlackBuild script to look for a line that makepkg always outputs that looks like Slackware package $PATH created
. It is also important of course that the user can see the output of the SlackBuild script as it may contain vital information. This means that both my program and the user needs to examine the output of the SlackBuild script. (See here for more information on SlackBuild scripts)
With the problem description out of the way we can now focus on the solution.
My original solution was an obvious one but had an interesting bug. I simply would use Perl's system command to execute the SlackBuild script with a shell, where I would pipe stdout to tee, writing the stdout to a temporary file. After the SlackBuild would execute I would read the temporary file looking for the Slackware package $PATH created
line. This solved the problem of allowing both the user and sbozyp to read the stdout of the SlackBuild script. For 99% of builds this worked fine ... but then there was qemu. Qemu is a massive project that requires a huge compilation. My system uses a small 4GB tmpfs mounted at /tmp, and the temporary file that was being teed to ended up getting so large from all the qemu compilation output that tee (and thus sbozyp) crashed for "device out of space".
Here is a basic (not actually realistic to sbozyp) outline of that code:
sub build_slackware_pkg {
my ($pkg_name) = @_;
my $slackbuild_script = find_slackbuild_script($pkg_name);
my $tmp_file = make_temp_file(DIR=>'/tmp');
0 == system("set -o pipefail; $slackbuild_script | tee $tmp_file") or die;
open my $fh, '<', $tmp_file or die;
my $slackware_pkg;
while (my $stdout_line = <$tmp_file>) {
$slackware_pkg = $1 if $stdout_line =~ /^Slackware package (.+) created$/;
}
return $slackware_pkg;
}
To solve the problem I create a named pipe, fork, and use the child process to execute the SlackBuild script, teeing stdout to the named pipe. From the parent I read the named pipe looking for the magic Slackware package $PATH created
line. Here is a (also not realistic to sbozyp) outline of this code:
use POSIX qw(mkfifo WNOHANG);
sub build_slackware_pkg {
my ($pkg_name) = @_;
my $slackbuild_script = find_slackbuild_script($pkg_name);
my $tmp_dir = make_temp_dir(DIR=>'/tmp');
mkfifo("$tmp_dir/fifo", 0700);
my $pid = fork();
if ($pid == 0) { # child
0 == system("set -o pipefail; $slackbuild_script | tee $fifo") or die;
exit 0;
} else { # parent
open my $fifo_r_fh, '<', $fifo or die;
my $slackware_pkg;
while (waitpid($pid, WNOHANG) == 0 or my $stdout_line = <$fifo_r_fh>) {
$slackware_pkg = $1 if $stdout_line and $stdout_line =~ /^Slackware package (.+) created$/;
}
die if $? != 0; # $? has exit status of child
return $slackware_pkg;
}
}
This implementation saves disk space as the data is being teed to a named pipe instead of a regular file in /tmp. As data is written to the pipe it is immediately being read, throwing away all the data that sbozyp doesn't need.
Problem solved :)
(Thanks to thrig on the #perl Libera IRC channel for helping lead me to this solution)
I got some great feedback from u/gorkish on reddit with a much simpler way to solve the problem by running the SlackBuild script with open instead of system. Here is the code:
sub build_slackware_pkg {
my ($pkg_name) = @_;
my $slackbuild_script = find_slackbuild_script($pkg_name);
open(my $cmd, '-|', $slackbuild_script) or die $!;
my $slackware_pkg;
while (my $line = <$cmd>) {
$slackware_pkg = $1 if $line =~ /^Slackware package (.+) created$/;
print $line;
}
close $cmd;
die if $? != 0;
return $slackware_pkg;
}
It is older than agriculture and civilization itself. We shall only cover the essential parts needed to make music on the computer. So let’s get right to the point!
How do you make music with code? And what is music in the first place?
Well, for our purposes, music is a combination of rhythm, melody, and harmony.
Okay, what are these musical elements from the perspective of a programming language? And how do you create these elements with code? Enter: Perl.
Here is a basic algorithm that builds an ascending musical phrase two times. It uses named notes with the octave (e.g. C4
):
use MIDI::Util qw(setup_score);
my $score = setup_score();
for (1 .. 2) {
for my $note (qw(C4 D4 E4 F4)) {
$score->n('qn', $note); # Adds a quarter note
$score->r('qn'); # Adds a quarter note rest
}
}
$score->write_score("$0.mid");
In order to actually hear some sound, you can either play the MIDI directly, with a command-line player like timidity
and an sf2
“soundfont” file, as in this list. Also the MIDI file can be used to create an audio formatted file (e.g. WAV, MP3) that can be played. Here is the command for this, that I use on my Mac:
timidity -c ~/timidity.cfg some.mid -Ow -o - | ffmpeg -i - -acodec libmp3lame -ab 64k some.mp3
These command line switches do the following things: -Ow
= Generate RIFF WAVE format output. -o -
= Place output on stdout. -i -
= Specify the infile as stdin. -acodec libmp3lame
= Specify that we are converting to mp3 format. -ab 64k
= Set the bitrate (in bits/s).
But wait! You can also generate and play MIDI in real-time with the MIDI::RtMidi::FFI::Device and MIDI::RtMidi::ScorePlayer modules. :D
So far, we have encountered the “Set up, Play, and Write” algorithm. Next we shall replace the “Play” bit with “Sync” and play the bass and treble parts simultaneously (and again, we use a named note plus octave number in each of the subroutines):
use MIDI::Util qw(setup_score);
my $score = setup_score();
$score->synch(
sub { bass($score) },
sub { treble($score) },
);
$score->write_score("$0.mid");
sub bass {
my ($score) = @_;
for my $note (qw(C3 F3 G3 C4)) {
$score->n('qn', $note);
}
}
sub treble {
my ($score) = @_;
for my $note (qw(C4 D4 E4 F4)) {
$score->n('en', $note);
$score->r('en');
}
}
This code is not especially clever, but illustrates the basics.
If we want to repeat the phrase, just add a for
loop to the synch
:
$score->synch(
sub { bass($score) },
sub { treble($score) },
) for 1 .. 4;
Use the MIDI::Util::set_chan_patch()
function to set the channel and the patch. To set the tempo in “beats per minute”, give a bpm
to the setup_score()
function. To set an individual note volume, add "v$num"
as an argument to the $score->n()
method, where $num
is an integer from 0 to 127. Here is code that sets these parameters:
use MIDI::Util qw(setup_score set_chan_patch);
my $bpm = shift || 120;
my $score = setup_score(bpm => $bpm);
$score->synch(
sub { bass($score) },
sub { treble($score) },
);
$score->write_score("$0.mid");
sub bass {
my ($score) = @_;
set_chan_patch($score, 0, 35);
for my $note (qw(C3 F3 G3 C4)) {
$score->n('qn', $note, 'v127');
}
}
sub treble {
my ($score) = @_;
set_chan_patch($score, 1, 0);
for my $note (qw(C4 D4 E4 F4)) {
$score->n('en', $note, 'v110');
$score->r('en');
}
}
If the synched subroutines do not amount to the same number of beats, the synch
will probably not work as expected, and will stop playing the shorter part and keep playing the longer part until it is finished.
What if we want the program to choose notes at random, to add to the score? Here is a simple example:
sub treble {
my ($score) = @_;
set_chan_patch($score, 1, 0);
my @pitches = (60, 62, 64, 65, 67, 69, 71, 72);
for my $n (1 .. 4) {
my $pitch = $pitches[int rand @pitches];
$score->n('en', $pitch);
$score->r('en');
}
}
For MIDI-Perl, the named note with octave C4
and the MIDI number 60
are identical, as shown in the tables on this page.
Another, more “music-theory way” is to select notes from a named scale (and this time, over two octaves):
use Music::Scales qw(get_scale_MIDI);
# ...
sub treble {
my ($score) = @_;
set_chan_patch($score, 1, 0);
my $octave = 4;
my @pitches = (
get_scale_MIDI('C', $octave, 'major'),
get_scale_MIDI('C', $octave + 1, 'major'),
);
for my $n (1 .. 4) {
my $pitch = $pitches[int rand @pitches];
$score->n('en', $pitch);
$score->r('en');
}
}
We saw above, how to select pitches at random. But this is the least musical or interesting way. Pitches may be selected by interval choice, as with the excellent Music::VoiceGen module. You could also choose by mathematical computation with Music::Voss (code example), or a probability density, or an evolutionary fitness function, etc.
Basslines are single note lines in a lower register. They have their own characteristics, which I will not attempt to summarize. One thing you can do is to make sure your notes are in the octaves 1 to 3. Fortunately, there is a module for this very thing called Music::Bassline::Generator. Woo!
So what is a melody? Good question. I’ll leave out the long-winded music theory discussion, and just say, “Go forth and experiment!”
We can construct chords at random - oof:
use Data::Dumper::Compact qw(ddc);
use MIDI::Util qw(setup_score);
use Music::Scales qw(get_scale_MIDI);
my @pitches = (
get_scale_MIDI('C', 4, 'minor'),
get_scale_MIDI('C', 5, 'minor'),
);
my $score = setup_score();
for my $i (1 .. 8) {
my @chord = map { $pitches[int rand @pitches] } 1 .. 3;
print ddc(\@chord);
$score->n('hn', @chord);
$score->r('hn');
}
$score->write_score("$0.mid");
We can construct chord progressions by name:
use Data::Dumper::Compact qw(ddc);
use MIDI::Util qw(setup_score midi_format);
use Music::Chord::Note;
my $score = setup_score();
my $mcn = Music::Chord::Note->new;
for my $c (qw(Cm7 F7 BbM7 EbM7 Adim7 D7 Gm)) {
my @chord = $mcn->chord_with_octave($c, 4);
@chord = midi_format(@chord); # convert to MIDI-Perl notation
print ddc(\@chord);
$score->n('wn', @chord);
}
$score->write_score("$0.mid");
Chord progressions may be constructed algorithmically. Here is an example of a randomized state machine that selects chords from the major scale using the default settings of the Music::Chord::Progression module:
use Data::Dumper::Compact qw(ddc);
use MIDI::Util qw(setup_score);
use Music::Chord::Progression;
my $score = setup_score();
my $prog = Music::Chord::Progression->new;
my $chords = $prog->generate;
print ddc($chords);
$score->n('wn', @$_) for @$chords;
$score->write_score("$0.mid");
Advanced Neo-Riemannian operations can be used with the Music::Chord::Progression::Transform module.
To get chord inversions, use the Music::Chord::Positions module. For instance, say we have a chord like C major (C4-E4-G4
), and we want the first or second inversion. Sure, we could just rewrite it to be E4-G4-C5
or G4-C5-E5
- but that’s not programming! The inversion of a chosen chord can be programmatically altered if deemed necessary.
This bit requires creativity! But fortunately, there is also the Music::Duration::Partition module. With it, rhythms can be generated and then applied to single-note, chord, or drum parts. This time, let’s choose the pitches more musically with Music::VoiceGen:
use MIDI::Util qw(setup_score);
use Music::Duration::Partition ();
use Music::Scales qw(get_scale_MIDI);
use Music::VoiceGen ();
my $score = setup_score();
# get rhythmic phrases
my $mdp = Music::Duration::Partition->new(
size => 4, # 1 measure in 4/4
pool => [qw(hn dqn qn en)], # half, dotted-quarter, quarter, eighth notes
);
my @motifs = $mdp->motifs(4);
# assign voices to the rhythmic motifs
my @pitches = (
get_scale_MIDI('C', 4, 'minor'),
get_scale_MIDI('C', 5, 'minor'),
);
my $voice = Music::VoiceGen->new(
pitches => \@pitches,
intervals => [qw(-3 -2 -1 1 2 3)], # allowed interval jumps
);
my @voices;
for my $motif (@motifs) {
my @notes = map { $voice->rand } @$motif;
push @voices, \@notes;
}
for (1 .. 4) { # repeat the group of phrases 4 times
for my $n (0 .. $#motifs) {
# add each motif with corresponding voices, to the score
$mdp->add_to_score($score, $motifs[$n], $voices[$n]);
}
}
$score->write_score("$0.mid");
Sidebar: Modulo arithmetic
If we want to stay within a range, say the chromatic scale of all notes, use the %
operator:
use Data::Dumper::Compact qw(ddc);
use Music::Scales qw(get_scale_notes);
my @notes = get_scale_notes('C', 'chromatic');
my %tritones = map { $notes[$_] => $notes[ ($_ + 6) % @notes ] } 0 .. $#notes;
print ddc(\%tritones);
(The “tritone” is a musical interval, once considered to be the “Devil’s interval.” 🎶 Purple haze all in my brain 🎶)
Sidebar: Alternation math
If we want to change every other iteration, we can also use the %
operator:
if ($i % 2 == 0) {
$score->n('qn', $pitch);
$score->r('qn');
}
else {
$score->n('hn', $pitch);
}
A steady pulse:
use MIDI::Drummer::Tiny;
my $d = MIDI::Drummer::Tiny->new;
$d->count_in(4); # Closed hi-hat for 4 measures
$d->write;
A simple “backbeat” rhythm:
use MIDI::Drummer::Tiny;
my $d = MIDI::Drummer::Tiny->new(file => "$0.mid");
$d->note(
$d->quarter,
$d->closed_hh,
$_ % 2 ? $d->kick : $d->snare
) for 1 .. $d->beats * $d->bars;
$d->write;
With this module, you can craft unique grooves like John Bonham’s “Fool in the Rain” (code example):
With combinatorial sequences from Music::CreatingRhythms, you can make algorithmic drums (code example):
And how about random grooves (code example)?
Please see the tutorials in the MIDI-Drummer-Tiny distribution for details on how to implement beats in your program.
This is an involved subject. Ideally, the different parts of a composition are distinct. If the piece starts slow (i.e. with a low note density per measure - not tempo change), then the next section should be more dense, then less again. Start quiet and soft? Follow with loud. If a part is staccato and edgy, it may be followed by a smooth legato section. Low register first, then higher register next, etc, etc. If a piece never changes, it is monotonous!
However that being said, consider “Thursday Afternoon” by Brian Eno. Brilliant.
You too can make music with Perl! This can be comprised of single-note lines (melody and bass, for instance), chord progressions, and of course drums. * Creativity not included. Haha!
• The example code for this article
• Music::Chord::Progression::Transform
• My personal music is available to stream on all platforms.
I have the pleasure to announce the release of the new Perl module DateTime::Format::RelativeTime
, which is designed to mirror its equivalent Web API Intl.RelativeTimeFormat
It requires only Perl v5.10.1
to run, and uses an exception class to return error or to die (if the option fatal
is provided and set to a true value).
You can use it the same way as the Web API:
use DateTime::Format::RelativeTime;
my $fmt = DateTime::Format::RelativeTime->new(
# You can use en-GB (Unicode / web-style) or en_GB (system-style), it does not matter.
'en_GB', {
localeMatcher => 'best fit',
# see getNumberingSystems() in Locale::Intl for the supported number systems
numberingSystem => 'latn',
# Possible values are: long, short or narrow
style => 'short',
# Possible values are: always or auto
numeric => 'always',
},
) || die( DateTime::Format::RelativeTime->error );
# Format relative time using negative value (-1).
$fmt->format( -1, 'day' ); # "1 day ago"
# Format relative time using positive value (1).
$fmt->format( 1, 'day' ); # "in 1 day"
This will work with 222 possible locales
as supported by the Unicode CLDR (Common Locale Data Repository). The CLDR data (currently the Unicode version 46.1
) is made accessible via another module I created a few months ago: Locale::Unicode::Data
However, beyond the standard options, and parameters you can pass to the methods format
and formatToParts
(or format_to_parts
if you prefer), you can also provide 1 or 2 DateTime
objects, and DateTime::Format::RelativeTime
will figure out for you the greatest difference between the 2 objects.
If you provide only 1 DateTime
object, DateTime::Format::RelativeTime
will instantiate a second one with DateTime->now
and using the first DateTime
object time_zone
value.
For example:
my $dt = DateTime->new(
year => 2024,
month => 8,
day => 15,
);
$fmt->format( $dt );
# Assuming today is 2024-12-31, this would return: "1 qtr. ago"
or, with 2 DateTime
objects:
my $dt = DateTime->new(
year => 2024,
month => 8,
day => 15,
);
my $dt2 = DateTime->new(
year => 2022,
month => 2,
day => 22,
);
$fmt->format( $dt => $dt2 ); # "2 yr. ago"
When using the method formatToParts
(or format_to_parts
) you will receive an array reference of hash references, making it easy to customise and handle as you wish. For example:
use DateTime::Format::RelativeTime;
use Data::Pretty qw( dump );
my $fmt = DateTime::Format::RelativeTime->new( 'en', { numeric => 'auto' });
my $parts = $fmt->formatToParts( 10, 'seconds' );
say dump( $parts );
would yield:
[
{ type => "literal", value => "in " },
{ type => "integer", unit => "second", value => 10 },
{ type => "literal", value => " seconds" },
]
You can use a negative number to indicate the past, and you can also use decimals, such as:
my $parts = $fmt->formatToParts( -12.5, 'hours' );
say dump( $parts );
would yield:
[
{ type => "integer", unit => "hour", value => 12 },
{ type => "decimal", unit => "hour", value => "." },
{ type => "fraction", unit => "hour", value => 5 },
{ type => "literal", value => " hours ago" },
]
The possible units
are: year
, quarter
, month
, week
, day
, hour
, minute
, and second
, and those can be provided in singular or plural form.
Of course, you can choose a different numbering system than the default latn
, i.e. numbers from 0
to 9
, as long as the numbering system you want to use is of numeric
type. There are 77 of those out of 96 in the CLDR data. See the method number_system
in Locale::Unicode::Data for more information.
So, for example:
use DateTime::Format::RelativeTime;
use Data::Pretty qw( dump );
my $fmt = DateTime::Format::RelativeTime->new( 'ar', { numeric => 'auto' });
my $parts = $fmt->formatToParts( -3, 'minutes' );
say dump( $parts );
would yield:
[
{ type => "literal", value => "قبل " },
{ type => "integer", value => '٣', unit => "minute" },
{ type => "literal", value => " دقائق" },
]
or, here we are explicitly setting the numbering system to deva
, which is not a system default:
use DateTime::Format::RelativeTime;
use Data::Pretty qw( dump );
my $fmt = DateTime::Format::RelativeTime->new( 'hi-IN', { numeric => 'auto', numberingSystem => 'deva' });
my $parts = $fmt->formatToParts( -3.5, 'minutes' );
say dump( $parts );
would yield:
[
{ type => "integer", value => '३', unit => "minute" },
{ type => "decimal", value => ".", unit => "minute" },
{ type => "fraction", value => '५', unit => "minute" },
{ type => "literal", value => " मिनट पहले" },
]
The option numeric
can be set to auto
or always
. If it is on auto
, the API will check if it can find a time relative term, such as today
or yesterday
instead of returning in 0 day
or 1 day ago
. If it is set to always
, then the API will always return a format involving a number like the ones I just mentioned.
Just like its Web API counterpart, you can use the method resolvedOptions
after having instantiated an object to check how DateTime::Format::RelativeTime
has resolved the options you provided, or possibly come up with default ones:
use DateTime::Format::RelativeTime;
use Data::Pretty qw( dump );
# locale is "Marathi"
my $fmt = DateTime::Format::RelativeTime->new( 'mr', { numeric => 'auto' });
my $options = $fmt->resolvedOptions;
say dump( $options );
would yield:
{
locale => "mr",
numberingSystem => "deva"
numeric => "auto",
style => "long",
}
deva
is the numbering system in the CLDR for “Devanagari Digits” used by the following 12 locales: bgc
(Haryanvi), bho
(Bhojpuri), brx
(Bodo), doi
(Dogri), hi
(Hindi), kok
(Konkani), mai
(Maithili), mr
(Marathi), ne
(Nepali), raj
(Rajasthani), sa
(Sanskrit) and xnr
(Kangri).
I hope you will enjoy this module, and that it will be useful to you. I have spent quite a bit of time putting it together, and it has been rigorously tested. If you see any bugs, or opportunities for improvement, kindly submit an issue on Gitlab
Published by Multisoft Systems on Monday 13 January 2025 07:12
Master the art of automation with PERL Scripting Training Online Course by Multisoft Systems.
Published by Gabor Szabo on Monday 13 January 2025 06:33
Originally published at Perl Weekly 703
Hi there!
After the Advent calendars and the vacation days it seems people are still tired and there is not much content to share. So let me repeat and rephrase my call for speaker for the online live events.
At these online meetings I'd like to see guests who would teach me, and the audience, interesting stuff. Eg. 'How to write Object oriented code in Perl?', 'How to write a web application using Mojolicious?', 'How to process huge amounts of data with Perl?', just to give you a few ideas.
There could be presentations on how a particular module works, and how to contribute to that module.
These could be also about Python, Rust, or some other interesting language, but I guess the readers of this newsletter will more likely know and want to share Perl.
These could be well-prepared presentations, or they can be more ad-hoc, pair-programming sessions. Just as you'd explain things to a new co-worker.
After each presentation we stay around and have a discussion. It is way better than just watching the video, but of course you have to join us at the live event to enjoy that. I hope to see you at one of the events either as listener or as guest speaker!
Anyway, if you are interested, check out the planned live events and send me an email with a topic you'd like to talk about.
Enjoy your week!
--
Your editor: Gabor Szabo.
Structured data on your web pages are said to help the search engines (aka. Google) to index your site, rank it higher and then display informative snippets to the people searching.
... and by brian d foy.
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 "Arrange Binary" and "Maximum Average". 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 "3-digits Even" and "Delete and Earn" tasks in Perl and Raku. You will find plenty of solutions to keep you busy.
Combination of sort, uniq, map, join and grep. This is not for weak hearts. Great work.
Find the difference between combinations and permutations. Highly recommended.
Nice catch of edge case and solution for the same. Keep it up great work.
Nice introduction to List::Gather, thanks. Cool use case, must checkout.
Nice promotion of CPAN modules and end up with a compact solutions. Well done.
Breakdown of a task into smaller subtask is very handy and easy to follow. Nice to see the use of new Perl camel.
Recursive call in play this week. Compact and powerful solutions, keep sharing knowledge with us.
Use of Bitmask is a clever move, brilliant. Great work, keep it up.
This is for Python fans. Anybody can follow the code, it is so simple. Thanks for sharing.
Great CPAN modules released last week.
Virtual event
January 20, 2025, Virtual event in Zoom
Virtual event
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.
If you have a website, then it’s very likely that you would like as many people as possible to see it. One of the best tools for achieving that is to ensure that your site is returned close to the top of as many search results pages as possible.
In order to do that, you really have two targets:
The second item on the list is mostly about getting other websites on the same topic to link to you – and it is outside the scope of this post. In this post, I want to talk about a good way to ensure search engines know what your site is about.
Of course, the search engines have invested a lot of money in working that out for themselves. They scan the text on your site and processes it to extract the meaning. But there are various ways you can make it easier for them. And they like sites that make their lives easier.
One of the most powerful ways to achieve this is to add structured data to your site. That means adding extra mark-up to your web pages which explains what the page is about. On the Schema.org website, you can find dozens of “things” the you can describe in structured data – for example, here is the definition of the Person entity. Each entity has a number of (largely optional) properties which can be included in structured data about an object of that type. Each property can be a string or another structured data entity. Additionally, entities are arranged in a hierarchy, so one entity can be based on another, more generic, entity. A Person, for example, inherits all of the properties of a Thing (which is the most generic type of entity). This is a lot like inheritance in Object-Oriented Programming.
Perhaps most usefully, the definition of each entity type ends with some examples of how structured data about an entity of that type could be added to an HTML document. The examples cover three formats:
Because it is completely separate to the existing mark-up, I find JSON-LD to be easier to work with. And for that reason, I wrote MooX::Role::JSON_LD which makes it easy to generate JSON-LD for classes that are based on Moo or Moose. Let’s look at a simple example of using it to add Person JSON-LD to a web page of a person. We’ll assume we already have a Person class that we use to provide the data on a web page about a person. It has attributes first_name, last_name and birth_date.
We start with some configuration. We load the role and define two subroutines which tell us which entity type we’re working with and which attributes we want to include in the JSON-LD. The code might look like this:
with 'MooX::Role::JSON_LD'; sub json_ld_type { 'Person' }; sub json_ld_fields { [ qw[ first_name last_name birth_date ] ] };
We can now use our Person class like this:
use Person; my $bowie = Person->new({ first_name => 'David', last_name => 'Bowie', birth_date => '1947-01-08', }); say $bowie->json_ld;
This produces the following output:
{ "@context" : "http://schema.org/", "@type" : "Person", "first_name" : "David", "last_name" : "Bowie", "birth_date" : "1947-01-08" }
This looks pretty good. But, sadly, it’s not valid JSON-LD. In the Schema.org Person entity, the relevant properties are called “givenName”, “familyName” and “birthDate”. Obviously, if we were designing our class from scratch, we could create attributes with those names. But often we’re adding features to existing systems and we don’t have that luxury. So the role allows us to change the names of attributes before they appear in the JSON-LD. We need to look more closely at the json_ld_fields() subroutine. It defines the names of the attributes that will appear in the JSON-LD. It returns an array reference and each element of the array contains a string which is the name of an attribute. But one of these elements can also contain a hash reference. In that case, the key of the hash is the name of the property we want to appear in the JSON-LD and the value is the name of the matching attribute in our class. So we can redefine our subroutine to look like this:
sub json_ld_fields { [ { givenName => 'first_name' }, { familyName => 'last_name' }, { birthDate => 'birth_date' }, ] }
And now we get the following JSON-LD:
{ "@context" : "http://schema.org/", "@type" : "Person", "givenName" : "David", "familyName" : "Bowie", "birthDate" : "1947-01-08" }
Which is now valid.
There’s one other trick we can use. We’ve seen the Schema.org Person entity has a “firstName” and “lastName” properties which map directly onto our “first_name” and “last_name” attributes. But the Person entity inherits from the Thing entity and that has a property called “name” which might be more useful for us. So perhaps we want to combine the “first_name” and “last_name” attributes into the single JSON-LD property. We can do that by changing our json_ld_fields() subroutine again:
sub json_ld_fields { [ { birthDate => 'birth_date'}, { name => sub { $_[0]->first_name . ' ' . $_[0]->last_name} }, ] }
In this version, we’ve added the “name” as the key of a hashref and the value is an anonymous subroutine that is passed the object and returns the name by concatenating the first and last names separated by a space. We now get this JSON-LD:
{ "@context" : "http://schema.org/", "@type" : "Person", "birthDate" : "1947-01-08" "name" : "David Bowie", }
Using this approach, allows us to build arbitrary JSON-LD properties from a combination of attributes from our object’s attributes.
Let’s look at a real-world example (and the reason why I was reminded of this module’s existence earlier this week.
I have a website called ReadABooker. It’s about the books that compete for the Booker Prize. Each year, a shortlist of six novels is announced and, later in the year, a winner is chosen. The winning author gets £50,000 and all of the shortlisted novels get massively increased sales. It’s a big deal in British literary circles. I created the website a few years ago. It lists all of the events (the competition goes back to 1969) and for each year, it lists all of the shortlisted novels. You can also see all of the authors who have been shortlisted and which of their shortlisted novels have won the prize. Each novel has a “Buy on Amazon” button and that link includes my associate ID – so, yes, it’s basically an attempt to make money out of people who want to buy Booker shortlisted novels.
But it’s not working. It’s not working because not enough people know about the site. So last week I decided to do a bit of SEO work on the site. And the obvious improvement was to add JSON-LD for the book and author pages.
The site itself is fully static. It gets updated twice a year – once when the shortlist is announced and then again when the winner is announced (the second update is literally setting a flag on a database row). The data about the novels is stored in an SQLite database. And there are DBIx::Class classes that allow me to access that data. So the obvious place to add the JSON-LD code is in Booker::Schema::Result::Book and Booker::Schema::Result::Person (a person can exist in the database if they have been an author, a judge or both).
The changes for the Person class were trivial. I don’t actually hold much information about the people in the database.
with 'MooX::Role::JSON_LD'; sub json_ld_type { 'Person' } sub json_ld_fields { [ qw/name/, ]; }
The changes in the Book class have one interesting piece of code:
with 'MooX::Role::JSON_LD'; sub json_ld_type { 'Book' } sub json_ld_fields { [ { name => 'title' }, { author => sub { $_[0]->author->json_ld_data } }, { isbn => 'asin' }, ]; }
The link between a book and its author is obviously important. But in the database, that link is simply represented by a foreign key in the book table. Having something like “author : 23” in the JSON-LD would be really unhelpful, so we take advantage of the link between the book and the author that DBIx::Class has given us and call the json_ld_data() method on the book’s author object. This method (which is added to any class that uses the role) returns the raw data structure which is later passed to a JSON encoder to produce the JSON-LD. So by calling that method inside the anonymous subroutine that creates the “author” attribute we can reuse that data in our book data.
The Person class creates JSON-LD like this:
{ "@context" : "http://schema.org/", "@type" : "Person", "name" : "Theresa Mary Anne Smith" }
And the Book class creates JSON-LD like this:
{ "@context" : "http://schema.org/", "@type" : "Book", "author" : { "@context" : "http://schema.org/", "@type" : "Person", "name" : "Theresa Mary Anne Smith" }, "isbn" : "B086PB2X8F", "name" : "Office Novice" }
There were two more changes needed. We needed to get the JSON-LD actually onto the HTML pages. The site is created using the Template Toolkit and the specific templates are author.html.tt and title.html.tt. Adding the JSON-LD to these pages was as simple as adding one line to each template:
[% author.json_ld_wrapped -%]
And
[% book.json_ld_wrapped -%]
We haven’t mentioned the json_ld_wrapped() method yet. Let me explain the hierarchy of the three main methods that the role adds to a class.
And that’s how I added JSON-LD to my website pretty easily. I now need to wait and see just how effective these changes will be. Hopefully thousands of people will be buying books through my site in the coming weeks and I can sit back and stop having to write code for a living.
It’s the dream!
How about you? Which of your websites would benefit from the addition of a few carefully-crafted pieces of JSON-LD?
The post Adding structured data with Perl appeared first on Perl Hacks.
Published by Unknown on Saturday 11 January 2025 21:48
Published by Perl Steering Council on Saturday 11 January 2025 07:35
Three of us again. Aristotle had limited time, Philippe and Graham stayed longer.
use VERSION
when given a non-existent version from the gap between the last v5.41.x and 42. We think it should be an error, to continue helping with typos (in line with the current helpful error message when requesting e.g. 5.10 instead of 5.010).Published by perlancar on Monday 06 January 2025 01:30
dist | author | abstract | date |
---|---|---|---|
AI-Chat | BOD | Interact with AI Chat APIs | 2024-03-02T22:12:10 |
AI-Image | BOD | Generate images using OpenAI's DALL-E | 2024-03-06T23:01:10 |
AI-Ollama-Client | CORION | Client for AI::Ollama | 2024-04-05T09:15:33 |
Ac_me-Local | CONTRA | The great new Ac_me::Local! | 2024-12-06T13:35:37 |
Acme-App-Broken | CONTRA | The great new Acme::App::Broken! | 2024-09-05T12:41:54 |
Acme-Both-MakefilePL-And-BuildPL | CONTRA | The great new Acme::Both::MakefilePL::And::BuildPL! | 2024-09-23T17:37:52 |
Acme-CPANModules-ArrayData | PERLANCAR | List of modules related to ArrayData | 2024-02-05T00:05:46 |
Acme-CPANModules-BPOM-FoodRegistration | PERLANCAR | List of modules and utilities related to Food Registration at BPOM | 2024-04-27T00:06:16 |
Acme-CPANModules-FormattingDate | PERLANCAR | List of various methods to format dates | 2024-02-18T14:01:27 |
Acme-CPANModules-GroupingElementsOfArray | PERLANCAR | List of modules to group elements of array into several buckets | 2024-01-14T00:11:01 |
Acme-CPANModules-HashData | PERLANCAR | List of modules related to HashData | 2024-02-06T00:05:32 |
Acme-CPANModules-Import-CPANRatings-User-davidgaramond | PERLANCAR | List of modules mentioned by CPANRatings user davidgaramond | 2024-02-28T00:05:24 |
Acme-CPANModules-InfoFromCPANTesters | PERLANCAR | List of distributions that gather information from CPANTesters | 2024-02-18T14:03:11 |
Acme-CPANModules-InterestingTies | PERLANCAR | List of interesting uses of the tie() interface | 2024-02-29T00:05:11 |
Acme-CPANModules-JSONVariants | PERLANCAR | List of JSON variants/extensions | 2024-04-29T00:05:46 |
Acme-CPANModules-LoadingModules | PERLANCAR | List of modules to load other Perl modules | 2024-03-01T00:06:07 |
Acme-CPANModules-LoremIpsum | PERLANCAR | List of modules related to "Lorem Ipsum", or lipsum, placeholder Latin text | 2024-03-02T00:05:10 |
Acme-CPANModules-MatchingString | PERLANCAR | List of modules related to matching string | 2024-01-10T01:49:01 |
Acme-CPANModules-ModifiedHashes | PERLANCAR | List of modules that provide hashes with modified behaviors | 2024-07-13T02:14:33 |
Acme-CPANModules-MultipleDispatch | PERLANCAR | List of modules to do smart matching | 2024-08-18T00:05:41 |
Acme-CPANModules-OpeningFileInApp | PERLANCAR | List of modules to open a file with appropriate application | 2024-03-04T00:05:56 |
Acme-CPANModules-QuickGraph | PERLANCAR | List of modules/tools to quickly create graph/chart | 2024-01-21T00:05:07 |
Acme-CPANModules-RandomText | PERLANCAR | List of modules for generating random (placeholder) text | 2024-03-05T00:05:27 |
Acme-CPANModules-RemovingElementsFromArray | PERLANCAR | List of modules to help remove elements from array | 2024-01-07T00:05:24 |
Acme-CPANModules-Soundex | PERLANCAR | List of modules that implement the soundex algorithm | 2024-11-24T00:05:57 |
Acme-CPANModules-UnixCommandImplementations | PERLANCAR | List of various CLIs that try to reimplement traditional Unix commands | 2024-09-08T00:05:29 |
Acme-CPANModules-UnixCommandVariants | PERLANCAR | List of various CLIs that are some variants of traditional Unix commands | 2024-08-26T09:48:48 |
Acme-CPANModules-UnixCommandWrappers | PERLANCAR | List of various CLIs that wrap existing Unix commands | 2024-09-01T00:05:58 |
Acme-CaSe | CONTRA | The great new Acme::CaSe! | 2024-11-19T08:13:26 |
Acme-Case | CONTRA | The great new Acme::Case! | 2024-11-19T08:13:37 |
Acme-Free-API-ChuckNorris | OODLER | Perl API client for the Chuck Norris Quote API service, https://api.chucknorris.io. | 2024-08-29T18:39:41 |
Acme-Free-API-Geodata-GeoIP | CAVAC | Lookup GeoIP data for an IP address | 2024-09-01T10:12:01 |
Acme-Free-API-Stonks | OODLER | Perl API client for the, top 50 stocks discussed on le'Reddit subeddit – r/Wallstreetbets, https://tradestie.com/apps/reddit/api/. | 2024-08-30T16:21:46 |
Acme-Free-API-Ye | OODLER | Perl API client for the Kanye Rest Quote API service, https://kanye.rest/. | 2024-08-28T16:39:49 |
Acme-Free-Advice | SANKO | Wise words. Dumb code. | 2024-09-03T17:56:37 |
Acme-Free-Advice-Slip | SANKO | Seek Advice from the Advice Slip API | 2024-09-03T16:56:16 |
Acme-Free-Advice-Unsolicited | SANKO | Solicit Unsolicited Advice from the Unsolicited Advice API | 2024-09-03T16:59:12 |
Acme-Free-Dog-API | OODLER | Perl API client for the Dog API service, https://dog.ceo/dog-api. | 2024-09-04T05:43:45 |
Acme-Free-Public-APIs | OODLER | Perl API client for … | 2024-09-07T01:03:15 |
Acme-Ghost | ABALAMA | An yet another view to daemon processes | 2024-01-06T19:41:31 |
Acme-Insult | SANKO | Code That Wasn't Raised Right | 2024-09-03T16:02:55 |
Acme-Insult-Evil | SANKO | Programmatically Generate Evil Insults | 2024-09-03T15:20:59 |
Acme-Insult-Glax | SANKO | Programmatically Generate Insults | 2024-09-03T15:19:30 |
Acme-Insult-Pirate | SANKO | Programmatically Generate Pirate Themed Insults | 2024-09-03T15:24:06 |
Acme-TaintTest | SIDNEY | module for checking taint peculiarities on some CPAN testers | 2024-03-25T09:22:24 |
Acme-Version-Same | CONTRA | Module for testing CPAN Pause indexing | 2024-12-19T08:38:15 |
Acme | STIGTSP | foo() returns "Foo" | 2024-12-05T16:35:21 |
Acrux | ABALAMA | Southern crucis constellation for your applications | 2024-01-28T14:27:27 |
Acrux-DBI | ABALAMA | Database independent interface for Acrux applications | 2024-02-05T18:44:46 |
Akamai-PropertyFetcher | SHINGO | Akamaiプãƒãƒ‘ティã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ–ãƒãƒ¼ã‚¸ãƒ§ãƒ³æƒ…å ±ã‚’å–å¾—ã™ã‚‹ãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ« | 2024-11-04T08:09:49 |
Algorithm-Gutter | JMATES | cellular automata to simulate rain in a gutter | 2024-11-25T15:59:02 |
Alien-Cowl | ZMUGHAL | Find or build Cowl | 2024-02-15T03:24:53 |
Alien-DuckDB | PERIGRIN | Find or build DuckDB | 2024-11-26T03:41:37 |
Alien-NLOpt | DJERIUS | Build and Install the NLOpt library | 2024-04-28T00:59:11 |
Alien-NLopt | DJERIUS | Build and Install the NLopt library | 2024-05-01T05:00:12 |
Alien-Pipx | CHRISARG | Provides the pipx Python Package Manager | 2024-03-09T13:19:11 |
Alien-Qhull | DJERIUS | Build and Install the Qhull library | 2024-02-09T17:38:52 |
Alien-RtAudio | JBARRETT | Install RtAudio | 2024-06-23T15:44:22 |
Alien-SeqAlignment-MMseqs2 | CHRISARG | find, build and install the mmseqs2 tools | 2024-03-24T03:33:13 |
Alien-SeqAlignment-bowtie2 | CHRISARG | find, build and install the bowtie2 tools | 2024-03-19T12:31:34 |
Alien-SeqAlignment-cutadapt | CHRISARG | Provide the cutadapt utility for eliminating polyA tails through pipx | 2024-03-09T04:49:02 |
Alien-SeqAlignment-hmmer3 | CHRISARG | find, build and install the hmmer3 tools | 2024-03-22T03:29:12 |
Alien-SeqAlignment-last | CHRISARG | find, build and install the last tools | 2024-03-16T21:16:36 |
Alien-SeqAlignment-minimap2 | CHRISARG | A Perl wrapper for the minimap2 binary executables | 2024-03-23T00:58:56 |
Alien-SunVox | JBARRETT | Install The SunVox Library – Alexander Zolotov's SunVox modular synthesizer and sequencer | 2024-06-20T09:22:51 |
Alien-cargo | PLICEASE | Find or download the cargo command (build system and package manager for Rust) | 2024-11-24T00:06:19 |
Alien-cargo-capi | PLICEASE | Find or build the cargo capi command | 2024-11-24T23:56:49 |
Alien-cargo-clone | PLICEASE | Find or build the cargo clone command | 2024-11-24T02:33:51 |
Alien-cue | PLICEASE | Find or download the cue configuration language tool | 2024-05-07T11:34:32 |
Alien-fpm | NHUBBARD | Alien package for the fpm package builder | 2024-10-20T14:38:39 |
Alien-libextism | EXTISM | find or build and install libextism with development dependencies | 2024-06-06T03:28:08 |
Alien-libsecp256k1 | BRTASTIC | Interface to libsecp256k1 | 2024-09-12T15:25:26 |
Alien-libversion | GDT | Alien wrapper for libversion | 2024-05-01T20:53:07 |
Alien-onnxruntime | EGOR | Discover or download and install onnxruntime (ONNX Runtime is a cross-platform inference and training machine-learning accelerator.) | 2024-04-17T22:03:45 |
Alien-pipx | CHRISARG | Provides the pipx Python Package Manager | 2024-03-08T20:39:35 |
Alien-poetry | OLIVER | Download and install poetry | 2024-05-11T11:33:08 |
Alien-raylib5 | PERIGRIN | Alien distribution for raylib video game engine, version 5 and above | 2024-11-23T06:04:11 |
Alt-Crypt-OpenSSL-PKCS12-Broadbean | DAKKAR | Perl extension to OpenSSL's PKCS12 API. | 2024-09-30T09:02:44 |
Alt-Digest-MD5-OpenSSL | SKIM | Perl interface to the MD5 Algorithm | 2024-01-25T10:58:17 |
Amazon-SQS-Client | BIGFOOT | Perl classes for interacting with Amazon SQS | 2024-11-27T23:07:47 |
Amazon-Sites | DAVECROSS | A class to represent Amazon sites | 2024-03-20T16:18:39 |
Amon2-Plugin-Web-Flash | YOSHIMASA | Ruby on Rails flash for Amon2 | 2024-05-26T03:25:23 |
AnyEvent-I3X-Workspace-OnDemand | WATERKIP | An I3 workspace loader | 2024-04-12T18:33:21 |
AnyEvent-Sway | JOHNMERTZ | communicate with the Sway window manager | 2024-02-09T00:42:07 |
Aozora2Epub | YOSHIMASA | Convert Aozora Bunko XHTML to EPUB | 2024-06-14T10:10:23 |
Apache-Session-MariaDB | BPS | An implementation of Apache::Session using MariaDB | 2024-01-19T14:46:59 |
App-ArticleWrap | SCHROEDER | word wrap news articles or mail files | 2024-06-28T11:16:03 |
App-BPOMUtils-NutritionLabelRef | PERLANCAR | Get one or more values from BPOM nutrition label reference (ALG, acuan label gizi) | 2024-06-05T00:06:03 |
App-BPOMUtils-RPO-Ingredients | PERLANCAR | Group ingredients suitable for food label | 2024-03-10T00:05:30 |
App-BookmarkFeed | SCHROEDER | Create a RSS feed from Markdown files | 2024-08-05T16:33:07 |
App-CSVUtils-csv_mix_formulas | PERLANCAR | Mix several formulas/recipes (lists of ingredients and their weights/volumes) into one, and output the combined formula | 2024-03-03T00:06:02 |
App-Changelog | OLOOEEZ | Simple command-line CHANGELOG.md generator written in Perl | 2024-12-01T14:35:19 |
App-Codit | HANJE | IDE for and in Perl | 2024-05-20T08:51:39 |
App-CommonPrefixUtils | PERLANCAR | Utilities related to common prefix | 2024-06-01T00:05:57 |
App-CommonSuffixUtils | PERLANCAR | Utilities related to common suffix | 2024-06-02T00:06:21 |
App-ComparerUtils | PERLANCAR | CLIs related to Comparer | 2024-03-06T00:05:48 |
App-CryptPasswordUtilUtils | PERLANCAR | Utilities related to Crypt::Password::Util | 2024-01-06T07:55:01 |
App-DWG-Sort | SKIM | Tool to sort DWG files by version. | 2024-03-06T10:03:07 |
App-DesktopNotifyUtils | PERLANCAR | Utilities related to Desktop::Notify | 2024-09-16T00:05:20 |
App-GeometryUtils | PERLANCAR | Utilities related to geometry | 2024-07-07T00:05:12 |
App-GnuCash-MembershipUtils | PDURDEN | A group of perl modules and scripts to help in using GnuCash for membership. | 2024-02-27T02:41:31 |
App-Greple-stripe | UTASHIRO | Greple zebra stripe module | 2024-10-07T08:06:15 |
App-Greple-under | UTASHIRO | greple under-line module | 2024-10-14T09:17:05 |
App-HeightUtils | PERLANCAR | Utilities related to body height | 2024-09-15T00:06:02 |
App-ISBN-Check | SKIM | Tool for ISBN checking. | 2024-02-25T18:59:34 |
App-KemenkesUtils-RDA | PERLANCAR | Get one or more values from Indonesian Ministry of Health's RDA (AKG, angka kecukupan gizi, from Kemenkes) | 2024-06-12T00:06:32 |
App-LastStats | DAVECROSS | A module to fetch and display Last.fm statistics | 2024-07-28T17:34:18 |
App-LevenshteinUtils | PERLANCAR | CLI utilities related to Levenshtein algorithm | 2024-01-30T00:06:14 |
App-LinkSite | DAVECROSS | 2024-10-29T11:43:59 | |
App-MARC-Record-Stats | SKIM | Tool to work with MARC::Record::Stats on MARC dataset. | 2024-02-21T11:20:06 |
App-MediaPi | MATHIAS | Media Player for Raspberry Pi or other devices with very small screen. | 2024-09-04T21:26:50 |
App-MergeCal | DAVECROSS | 2024-02-02T17:37:26 | |
App-NKC2MARC | SKIM | Tool to fetch record from National library of the Czech Republic to MARC file. | 2024-06-26T07:14:52 |
App-NutrientUtils | PERLANCAR | Utilities related to nutrients | 2024-05-26T00:06:02 |
App-PasswordManager | OLOOEEZ | Simple password manager for adding, listing, editing, deleting, and copying passwords to the clipboard | 2024-11-26T12:46:39 |
App-PerlGzipScript | SKAJI | Gzip perl scripts to reduce their file size | 2024-07-20T12:49:29 |
App-Prove-Plugin-TestArgs | SVW | A prove plugin to configure test aliases and arguments | 2024-02-07T12:49:02 |
App-SortExampleUtils | PERLANCAR | CLIs related to SortExample | 2024-03-07T00:05:25 |
App-SortKeyUtils | PERLANCAR | CLIs related to SortKey | 2024-03-08T00:05:47 |
App-SortSpecUtils | PERLANCAR | CLIs related to SortSpec | 2024-03-09T00:05:07 |
App-SorterUtils | PERLANCAR | CLIs related to Sorter | 2024-03-11T00:05:26 |
App-SpreadsheetOpenUtils | PERLANCAR | Utilities related to Spreadsheet::Open | 2024-03-12T00:05:23 |
App-Standup-Diary | SMONFF | Manage a simple Markdown journal for your daily standups | 2024-12-01T17:05:39 |
App-Stouch | SAMYOUNG | Simple template file creator | 2024-11-22T02:59:41 |
App-Tarotplane | SAMYOUNG | Curses flashcard program | 2024-10-31T18:18:37 |
App-TextSimilarityUtils | PERLANCAR | CLI utilities related to text similarity | 2024-01-31T00:06:20 |
App-Tickit-Hello | SKIM | Tickit application with hello world. | 2024-12-18T19:17:34 |
App-TimeTracker-Gtk3StatusIcon | DOMM | Show TimeTracker status as a GTK3 StatusIcon in the system tray | 2024-09-11T19:36:29 |
App-Timestamper-Log-Process | SHLOMIF | various filters and queries for App::Timestamper logs. | 2024-06-09T04:42:04 |
App-TodoList | OLOOEEZ | Simple command-line to-do list manager written in Perl | 2024-11-25T23:32:50 |
App-YtDlpUtils | PERLANCAR | Utilities (mostly wrappers) related to yt-dlp | 2024-07-10T02:59:34 |
App-bsky | SANKO | A Command-line Bluesky Client | 2024-01-26T03:46:19 |
App-cat-v | UTASHIRO | cat-v command implementation | 2024-03-31T10:58:40 |
App-cdbookmark | PERLANCAR | Change directory to one from the list | 2024-02-03T00:05:45 |
App-chartimes | TULAMILI | 2024-03-15T01:18:20 | |
App-colcount | TULAMILI | 各行について、カラムの数を数えたり、条件を満たすカラムの数を数えたりする。 | 2024-03-15T10:32:37 |
App-coldigits | TULAMILI | TSVファイルの各列が何桁のものが何件あったかを、行列状に示す。 | 2024-11-18T14:35:34 |
App-cpx | CONTRA | Install and execute CPAN package binaries | 2024-09-05T17:09:44 |
App-ctransition | TULAMILI | 入力の全ての文字に対して、次の文字は何であるかの回数の集計を、行列状に表示する。 | 2024-03-15T12:58:49 |
App-datasection | PLICEASE | Work with __DATA__ section files from the command line | 2024-12-22T14:02:22 |
App-findsort | PERLANCAR | Unix find wrapper to add sorting | 2024-10-27T00:05:59 |
App-gapstat | TULAMILI | 改行区切りの数値列に、差分が1を超えるギャップ(間隙)がないかを、チェックする。 | 2024-11-20T15:35:38 |
App-genusername | PERLANCAR | Generate random username | 2024-01-26T00:05:37 |
App-grep-similar-text | PERLANCAR | Print lines similar to the specified text | 2024-02-01T00:06:12 |
App-grep-sounds-like | PERLANCAR | Print lines with words that sound like to the specified word | 2024-11-20T05:47:15 |
App-hashdata | PERLANCAR | Show content of HashData modules (plus a few other things) | 2024-01-25T00:06:11 |
App-htidx | GBROWN | generate static HTML directory listings. | 2024-05-29T11:03:56 |
App-indent | PERLANCAR | Indent text | 2024-01-25T04:48:04 |
App-lcpan-CmdBundle-namespace | PERLANCAR | lcpan subcommands related to namespaces | 2024-01-09T04:56:59 |
App-mqtt2job | CHRISC | Subscribe to an MQTT topic and trigger job execution | 2024-10-24T22:22:49 |
App-optex-glob | UTASHIRO | optex filter to glob filenames | 2024-09-10T12:36:16 |
App-optex-mask | UTASHIRO | optex data masking module | 2024-08-16T10:14:16 |
App-optex-scroll | UTASHIRO | optex scroll region module | 2024-09-11T12:40:16 |
App-papersway | SPWHITTON | PaperWM-like window management for Sway/i3wm | 2024-04-12T08:18:00 |
App-pdfresize | PERLANCAR | Resize each page of PDF file to a new dimension | 2024-09-29T00:06:10 |
App-pdfsize | PERLANCAR | Show dimensions of PDF files | 2024-09-22T00:05:31 |
App-perlwhich | MMCCLENN | locate a Perl module | 2024-01-09T21:42:01 |
App-prefixcat | PERLANCAR | Like Unix `cat` but by default prefix each line with filename | 2024-08-09T12:33:04 |
App-rdapper | GBROWN | a simple console-based RDAP client. | 2024-05-29T23:00:49 |
App-repeat | PERLANCAR | Repeat a command a number of times | 2024-12-06T06:58:51 |
App-runscript | SVW | Module that implements the runscript utility | 2024-06-25T08:11:16 |
App-samelines | TULAMILI | 2024-03-14T07:49:49 | |
App-sbozyp | NHUBBARD | a package manager for Slackware's SlackBuilds.org | 2024-10-20T20:41:27 |
App-sort_by_comparer | PERLANCAR | Sort lines of text by a Comparer module | 2024-04-16T00:06:00 |
App-sort_by_example | PERLANCAR | Sort lines of text by example | 2024-04-20T00:05:10 |
App-sort_by_sorter | PERLANCAR | Sort lines of text by a Sorter module | 2024-04-17T00:05:42 |
App-sort_by_sortkey | PERLANCAR | Sort lines of text by a SortKey module | 2024-04-24T00:06:38 |
App-zen | LITCHIE | Zen is a markdown based literate programming tool | 2024-02-05T05:48:06 |
Archive-Libarchive-Compress | PLICEASE | Recursively archive a directory (using libarchive) | 2024-11-15T19:49:10 |
Archive-SCS | NAUTOFON | SCS archive controller | 2024-05-21T18:27:52 |
Arcus-Client | JAMTWOIN | Perl client for arcus cache cluster | 2024-09-04T02:56:17 |
Arithmetic-PaperAndPencil | JFORGET | simulating paper and pencil techniques for basic arithmetic operations | 2024-04-22T19:57:44 |
Ascii-Text | LNATION | module for generating ASCII text in various fonts and styles | 2024-08-29T10:49:18 |
Ascii-Text-Image | LNATION | module for generating images using ASCII text. | 2024-09-04T15:30:30 |
Astro-MoonPhase-Simple | BLIAKO | Calculate the phase of the Moon on a given time without too much blah blah | 2024-07-14T14:14:36 |
Audio-Cuefile-Libcue | GREGK | Perl interface to the libcue cuesheet reading library | 2024-07-19T19:43:23 |
Audio-SunVox-FFI | JBARRETT | Bindings for the SunVox library – a modular synthesizer and sequencer | 2024-06-22T11:40:41 |
Authorization-AccessControl | TYRRMINAL | Hybrid RBAC/ABAC access control | 2024-05-16T03:53:26 |
Autoconf-Template | BIGFOOT | autoconfiscation help scripts | 2024-08-06T09:26:35 |
Bencher-Scenario-ExceptionHandling | PERLANCAR | Benchmark various ways to do exception handling in Perl | 2024-04-13T00:05:36 |
Bencher-Scenario-ListFlattenModules | PERLANCAR | Benchmark various List::Flatten implementaitons | 2024-03-13T00:05:28 |
Bencher-ScenarioBundle-Accessors | PERLANCAR | Scenarios to benchmark class accessors | 2024-05-13T00:05:21 |
Bencher-ScenarioBundle-Algorithm-Diff | PERLANCAR | Scenarios to benchmark Algorithm::Diff | 2024-05-11T00:06:17 |
Bencher-ScenarioBundle-Graphics-ColorNames | PERLANCAR | Scenarios to benchmark Graphics::ColorNames and related modules | 2024-05-12T00:05:13 |
Bencher-ScenarioBundle-Log-Any | PERLANCAR | Scenarios for benchmarking Log::Any | 2024-05-20T00:06:19 |
Bencher-ScenarioBundle-Log-ger | PERLANCAR | Scenarios for benchmarking Log::ger | 2024-05-21T00:05:46 |
Bencher-ScenarioBundle-Ref-Util | PERLANCAR | Benchmark Ref::Util | 2024-12-04T03:38:56 |
Bencher-ScenarioBundle-SmartMatch | PERLANCAR | Scenarios to benchmark switch & smartmatch in Perl | 2024-07-03T09:45:27 |
Bencher-Scenarios-Log-Any | PERLANCAR | Scenarios for benchmarking Log::Any | 2024-01-28T00:05:19 |
Bencher-Scenarios-Log-Dispatch | PERLANCAR | Bencher scenarios related to Log::Dispatch modules | 2024-02-04T00:05:28 |
Bencher-Scenarios-Log-Dispatch-FileRotate | PERLANCAR | Scenarios to benchmark Log::Dispatch::FileRotate | 2024-02-11T00:05:41 |
Bencher-Scenarios-Text-Table-Sprintf | PERLANCAR | Scenarios for benchmarking Text::Table::Sprintf | 2024-03-14T00:05:21 |
Bio-EnsEMBL | ABECKER | Bio::EnsEMBL – Ensembl Core API | 2024-09-03T10:23:40 |
Bio-SeqAlignment | CHRISARG | Aligning (and pseudo aligning) biological sequences | 2024-03-24T01:05:16 |
Bio-SeqAlignment-Applications-SequencingSimulators-RNASeq-Polyester | CHRISARG | Skeleton package that does nothing but reserve the namespace. | 2024-06-09T13:20:02 |
Bio-SeqAlignment-Components-Libraries-edlib | CHRISARG | basic edlib library | 2024-06-13T03:23:35 |
Bio-SeqAlignment-Components-SeqMapping | CHRISARG | Imports all modules relevant to sequence mapping | 2024-06-10T04:15:38 |
Bio-SeqAlignment-Components-Sundry | CHRISARG | Miscellaneous components for building awesome sequencing apps. | 2024-06-09T16:30:30 |
Bio-SeqAlignment-Examples-EnhancingEdlib | CHRISARG | Parallelizing Edlib with MCE, OpenMP using Inline and FFI::Platypus | 2024-06-13T01:02:44 |
Bio-SeqAlignment-Examples-TailingPolyester | CHRISARG | "Beefing" up the RNA sequencing simulator polyester with polyA tails | 2024-06-12T12:26:38 |
Bitcoin-Secp256k1 | BRTASTIC | Perl interface to libsecp256k1 | 2024-09-17T22:11:45 |
Bluesky | SANKO | The Bluesky Social Network | 2024-12-03T01:56:20 |
Bot-Telegram | VASYAN | A micro^W nano framework for creating telegram bots based on WWW::Telegram::BotAPI | 2024-06-17T11:13:27 |
Bundle-WATERKIP | WATERKIP | A mono repo for perl scripts and modules which WATERKIP likes | 2024-02-27T23:00:33 |
Business-CAMT | MARKOV | ISO20022 Cash Management (CAMT) messages | 2024-11-25T08:57:16 |
Business-ID-VehiclePlate | PERLANCAR | Parse Indonesian vehicle plate number | 2024-08-07T00:05:46 |
Business-PAYONE | ARTHAS | Perl library for PAYONE online payment system | 2024-10-17T09:59:35 |
Business-Tax-US-Form_1040-Worksheets | JKEENAN | IRS Form 1040 worksheets calculations | 2024-03-20T19:16:50 |
CLI-Meta-less | PERLANCAR | Metadata for 'cp' Unix commnd | 2024-09-27T08:41:45 |
CPAN-Namespace-Check-Visibility | CONTRA | Check if a namespace exists on public CPAN | 2024-12-18T12:39:35 |
CPAN-Requirements-Dynamic | LEONT | Dynamic prerequisites in meta files | 2024-04-27T15:17:57 |
CPANSA-DB | BDFOY | the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit | 2024-11-17T20:32:36 |
CSAF | GDT | Common Security Advisory Framework | 2024-04-23T21:49:42 |
CVSS | GDT | CVSS (Common Vulnerability Scoring System) command line interface | 2024-07-30T22:11:37 |
CXC-DB-DDL | DJERIUS | DDL for table creation, based on SQL::Translator::Schema | 2024-04-04T16:24:13 |
CXC-DB-DDL-Field-Pg | DJERIUS | DBD::Pg specific Field class | 2024-06-27T14:28:18 |
CXC-Data-Visitor | DJERIUS | Invoke a callback on every element at every level of a data structure. | 2024-03-23T17:04:24 |
CXC-PDL-Bin1D | DJERIUS | one dimensional binning functions | 2024-01-20T20:50:24 |
Cache-Memcached-PDeque | HAIJENP | Implements a priority deque using memcached as storage | 2024-10-25T23:24:58 |
Cache-MemcachedBinary | XMOLEX | Perl extension for Memcached server with binary protocol. | 2024-01-22T15:13:42 |
Captcha-Stateless-Text | HIGHTOWE | stateless, text-based CAPTCHAs | 2024-04-17T21:19:21 |
Carp-Object | DAMI | a replacement for Carp or Carp::Clan, object-oriented | 2024-04-28T17:58:22 |
Carp-Patch-ExcludePackage | PERLANCAR | Exclude some packages from stack trace | 2024-03-15T00:06:01 |
Carp-Patch-OutputToBrowser | PERLANCAR | Output stacktrace to browser as HTML instead of returning it | 2024-04-25T00:05:19 |
Catalyst-Plugin-Flash | ARISTOTLE | put values on the stash of the next request | 2024-04-09T05:06:19 |
Catalyst-Plugin-Profile-DBI-Log | BIGPRESH | Capture queries executed during a Catalyst route with DBI::Log | 2024-08-29T23:41:08 |
Catalyst-View-EmbeddedPerl | JJNAPIORK | Catalyst View wrapper for Template::EmbeddedPerl | 2024-09-25T22:17:09 |
Catalyst-View-EmbeddedPerl-PerRequest | JJNAPIORK | Catalyst View wrapper for Template::EmbeddedPerl | 2024-09-25T23:20:52 |
Catalyst-View-EmbeddedPerl-PerRequest-ValiantRole | JJNAPIORK | Role to add Valiant HTML Formbuilder support | 2024-10-03T21:32:43 |
Catmandu-Store-OpenSearch | NJFRANCK | A searchable store backed by Opensearch | 2024-07-03T07:23:38 |
Chart-ECharts | GDT | Apache ECharts wrapper for Perl | 2024-11-21T22:24:32 |
CheerLights-API | NOTHANS | A Perl module for accessing the CheerLights API | 2024-11-06T02:28:43 |
Chess-ELO-FIDE | NICEPERL | Download and store FIDE ratings | 2024-02-24T19:06:41 |
Circle-Block | CHENGYU | the block module for Circle::Chain SDK | 2024-08-29T06:33:17 |
Circle-Chain | CHENGYU | The great new Circle::Chain! | 2024-08-29T02:33:58 |
Circle-Common | CHENGYU | the common module for Circle::Chain SDK | 2024-08-29T06:31:49 |
Circle-Miner | CHENGYU | The miner module for circle chain sdk. | 2024-12-04T13:38:23 |
Circle-Node | CHENGYU | The great new Circle::Node! | 2024-09-09T13:37:01 |
Circle-User | CHENGYU | the user module for Circle::Chain SDK | 2024-08-29T06:36:47 |
Circle-Wallet | CHENGYU | the circle chain SDK in PERL | 2024-08-29T06:36:58 |
Common-Log-Parser | RRWO | Parse the common log format lines used by Apache | 2024-02-09T15:12:27 |
CommonsLang | YUPEN | Commonly used functions for Perl language | 2024-12-25T02:31:45 |
Comparer | PERLANCAR | Reusable comparer subroutines | 2024-02-08T00:11:13 |
Comparer-by_similarity | PERLANCAR | Compare similarity to a reference string | 2024-02-23T02:06:32 |
Comparer-date_in_text | PERLANCAR | Compare date found in text (or text asciibetically, if no date is found) | 2024-04-18T00:05:43 |
Comparer-file_mtime | PERLANCAR | Compare file's mtime (modification time) | 2024-10-06T00:05:31 |
Comparer-file_num_links | PERLANCAR | Compare file's number of (hard) links | 2024-11-24T00:06:08 |
Comparer-file_size | PERLANCAR | Compare file's size | 2024-12-01T00:05:53 |
Comparer-from_sortkey | PERLANCAR | Compare keys generated by a SortKey:: module | 2024-03-16T00:05:16 |
Comparer-similarity | PERLANCAR | Compare similarity to a reference string | 2024-02-24T00:06:09 |
Complete-Nutrient | PERLANCAR | Completion routines related to nutrients | 2024-05-31T00:05:25 |
Compression-Util | TRIZEN | Implementation of various techniques used in data compression. | 2024-03-21T01:02:57 |
Config-IniFiles-Check-Health | HORSHACK | 2024-09-09T13:06:48 | |
Config-Proxy | SGRAY | Loader class for HTTP proxy configuration parsers. | 2024-12-10T12:47:30 |
Consumer-NonBlock | EXODIST | Send data between processes without blocking. | 2024-07-02T19:53:48 |
Couch-DB | MARKOV | thick CouchDB interface | 2024-05-29T16:37:08 |
Crate | CARELINE | Everything that PDK modules needs | 2024-10-05T12:14:57 |
Crypt-Bear | LEONT | BearSSL for Perl | 2024-11-12T20:07:54 |
Crypt-Credentials | LEONT | Manage credential files | 2024-01-31T01:30:24 |
Crypt-PQClean-Sign | GUL | Post-Quantum Cryptography with keypair | 2024-12-23T12:37:49 |
Crypt-Passphrase-Bcrypt-AES | LEONT | A peppered AES-encrypted Bcrypt encoder for Crypt::Passphrase | 2024-01-28T18:15:23 |
Crypt-Passphrase-Bcrypt-Compat | LEONT | A bcrypt encoder for Crypt::Passphrase | 2024-04-08T14:24:10 |
Crypt-URandom-Password | STIGTSP | Generate passwords from cryptographically secure pseudorandom bytes | 2024-12-27T22:59:54 |
Crypt-URandom-Token | STIGTSP | Generate secure strings for passwords, secrets and similar | 2024-12-28T20:03:58 |
DBD-Mock-Session-GenerateFixtures | UXYZAB | When a real DBI database handle ($dbh) is provided, the module generates DBD::Mock::Session data. Otherwise, it returns a DBD::Mock::Session object populated with generated data. This not a part form DBD::Mock::Session distribution just a wrapper around it. | 2024-04-29T18:25:02 |
DBIx-Class-FilterColumn-Encrypt | LEONT | Transparently encrypt columns in DBIx::Class | 2024-02-03T15:22:04 |
DBIx-Class-Helper-ColumnNames | RRWO | Retrieve column names from a resultset | 2024-09-23T22:20:45 |
DBIx-Class-ResultSet-PrettyPrint | PTC | Pretty print DBIx::Class result sets. | 2024-10-14T12:10:32 |
DBIx-HoldMyPlace | LANX | Automagic SQL Placeholders from interpolated variables | 2024-10-24T17:09:34 |
DBIx-QuickORM | EXODIST | Actively maintained Object Relational Mapping that makes getting started Quick and has a rich feature set. | 2024-10-26T07:48:12 |
DBIx-Squirrel | CPANIC | A module for working with databases | 2024-08-08T20:01:38 |
DWIM-Block | DCONWAY | Use AI::Chat without having to write the infrastructure code | 2024-06-26T22:36:44 |
Daje-Generate | JANESKIL | lib::Generate | 2024-09-22T15:46:05 |
Daje-Plugin-GeneratePerl | JANESKIL | It's new $module | 2024-12-13T14:31:42 |
Daje-Plugin-GenerateSQL | JANESKIL | It's new $module | 2024-12-12T07:35:48 |
Daje-Plugin-GenerateSchema | JANESKIL | It's new $module | 2024-12-12T16:54:45 |
Daje-Tools-DataSections | JANESKIL | It's new $module | 2024-12-05T16:13:18 |
Daje-Tools-Datasections | JANESKIL | Load and store data sections in memory from a named *.pm | 2024-12-07T14:41:02 |
Daje-Tools-Filechanged | JANESKIL | It's new $module | 2024-12-08T10:11:41 |
Daje-Workflow-Database | JANESKIL | It's new $module | 2024-12-24T15:22:30 |
Daje-Workflow-Database-Model | JANESKIL | It's new $module | 2024-12-29T07:48:59 |
Daje-Workflow-Loader | JANESKIL | It's new $module | 2024-12-22T13:51:40 |
Dancer2-Controllers | RAWLEYFOW | A tool to allow OO style route declaration in Dancer2 | 2024-02-14T22:00:57 |
Dancer2-Plugin-NYTProf | GEEKRUTH | NYTProf, in your Dancer2 application! | 2024-07-02T13:03:01 |
Dancer2-Session-DBI | EPISODEIV | DBI based session engine for Dancer | 2024-08-29T13:55:42 |
Data-Annotation | POLETTIX | [Put something meaningful here!] | 2024-11-13T22:16:57 |
Data-Checks | PEVANS | XS functions to assist in value constraint checking | 2024-06-19T13:26:59 |
Data-Dump-HTML-Collapsible | PERLANCAR | Dump Perl data structures as HTML document with collapsible sections | 2024-03-08T08:22:33 |
Data-Dump-HTML-PopUp | PERLANCAR | Dump Perl data structures as HTML document with nested pop ups | 2024-03-18T13:24:01 |
Data-Dump-IfSmall | PERLANCAR | Like Data::Dump but reference with dump larger than a certain size will be dumped as something like 'LARGE:ARRAY(0x5636145ea5e8)' | 2024-03-18T00:06:06 |
Data-Dump-SkipObjects | PERLANCAR | Like Data::Dump but objects of some patterns are dumped tersely | 2024-03-19T00:05:05 |
Data-Dumper-UnDumper | BIGPRESH | load Data::Dumper output, including self-references | 2024-04-25T21:42:30 |
Data-FastPack-JPacker | DRCLAW | backend class for packing FastPack data files into web loadable JPack | 2024-11-11T00:47:35 |
Data-FastPack | DRCLAW | FastPack Record format, parsing and serialising module | 2024-11-08T04:39:52 |
Data-HTML-Element | SKIM | Data objects for HTML elements. | 2024-01-06T17:04:11 |
Data-HTML-Footer | SKIM | Data object for HTML footer. | 2024-05-31T09:32:54 |
Data-ISO8583 | CADE | 2024-06-08T22:52:33 | |
Data-Identifier | LION | format independent identifier object | 2024-09-18T14:36:10 |
Data-InfoBox | SKIM | Data objects for info box. | 2024-09-15T11:37:07 |
Data-JPack | DRCLAW | Offline/Online Web application and data system | 2024-11-08T04:40:03 |
Data-LnArray-XS | LNATION | Arrays | 2024-07-12T09:58:12 |
Data-Login | SKIM | Data objects for login. | 2024-02-05T22:13:16 |
Data-Message-Board | SKIM | Data objects for message board. | 2024-05-27T18:30:24 |
Data-MiniDumpX | PERLANCAR | A simplistic data structure dumper (demo for Plugin::System) | 2024-04-14T00:06:13 |
Data-Navigation-Item | SKIM | Data object for navigation item. | 2024-03-04T11:53:10 |
Data-Person | SKIM | Data objects for person. | 2024-05-27T08:46:48 |
Data-Random-Person | SKIM | Generate random person. | 2024-07-05T00:10:57 |
Data-Record-Serialize-Encode-html | DJERIUS | encode a record as html | 2024-01-10T17:34:01 |
Data-Sah-FilterBundle-Array | PERLANCAR | More array-related Sah filters | 2024-01-29T00:06:31 |
Data-Sah-FilterBundle-Filename-Safe | PERLANCAR | Sah filters related to removing problematic characters from filename | 2024-02-10T00:06:19 |
Data-Section-Pluggable | PLICEASE | Read structured data from __DATA__ | 2024-12-05T05:27:18 |
Data-Section-Pluggable-Plugin-Yaml | PLICEASE | Data::Section::Pluggable Plugin for YAML | 2024-12-05T12:02:28 |
Data-Section-Writer | PLICEASE | Write __DATA__ section files for Data::Section, Data::Section::Simple or Mojo::Loader::data_section | 2024-12-04T00:15:50 |
Data-Structure-Deserialize-Auto | TYRRMINAL | Deserializes data structures from perl, JSON, YAML, or TOML data, from strings or files | 2024-02-22T18:13:45 |
Data-TagDB | LION | Work with Tag databases | 2024-09-10T15:25:24 |
Data-Text-Simple | SKIM | Data objects for text in language. | 2024-02-29T17:29:50 |
Data-Tranco | GBROWN | An interface to the Tranco domain list. | 2024-06-03T13:35:31 |
Data-Transfigure | TYRRMINAL | performs rule-based data transfigurations of arbitrary structures | 2024-02-19T20:39:03 |
Database-Abstraction | NHORNE | database abstraction layer | 2024-01-29T19:03:18 |
Date-Business | BPSCHUCK | fast calendar and business date calculations | 2024-10-09T01:15:25 |
Date-Holidays-Adapter-USA | GENE | Adapter for USA holidays | 2024-03-19T20:38:52 |
Date-Holidays-Adapter-USExtended | GENE | Adapter for the USExtended module holidays | 2024-09-23T22:23:33 |
Date-Holidays-CW | WATERKIP | Curacoa's official holidays | 2024-02-08T07:49:12 |
Date-Holidays-USA | GENE | Provides United States of America holidays | 2024-03-19T20:09:45 |
Date-Holidays-USExtended | GENE | Provides an extended set of United States holidays | 2024-09-23T22:23:44 |
Date-Parse-Modern | BAKERSCOT | Provide string to unixtime conversions | 2024-01-14T00:42:01 |
DateTime-Format-Intl | JDEGUEST | A Web Intl.DateTimeFormat Class Implementation | 2024-10-09T02:07:15 |
DateTime-Format-PDF | SKIM | PDF DateTime Parser and Formatter. | 2024-04-01T09:23:07 |
DateTime-Format-Unicode | JDEGUEST | Unicode CLDR Formatter for DateTime | 2024-09-10T10:06:43 |
DateTime-Locale-FromCLDR | JDEGUEST | DateTime Localised Data from Unicode CLDR | 2024-08-01T22:59:44 |
DateTime-Schedule | TYRRMINAL | Determine scheduled days in range based on inclusions/exclusions | 2024-06-11T20:45:23 |
Debug-Helper-Flag | AAHAZRED | Define and import boolean constant DEBUG_FLAG helping to optimize code. | 2024-10-10T07:48:38 |
Devel-Confess-Patch-UseDataDumpHTMLCollapsible | PERLANCAR | Use Data::Dump::HTML::Collapsible to stringify reference | 2024-04-26T00:05:16 |
Devel-Confess-Patch-UseDataDumpHTMLPopUp | PERLANCAR | Use Data::Dump::HTML::PopUp to stringify reference | 2024-04-28T00:06:05 |
Devel-Confess-Patch-UseDataDumpIfSmall | PERLANCAR | Use Data::Dump::IfSmall format refs | 2024-03-20T00:05:59 |
Devel-Confess-Patch-UseDataDumpSkipObjects | PERLANCAR | Use Data::Dump::SkipObjects to stringify some objects | 2024-03-21T00:06:11 |
Devel-Confess-Source-Patch-ExcludePackage | PERLANCAR | Exclude some packages from source trace | 2024-02-25T00:05:42 |
Devel-Cover-Report-Codecov-Service-GithubActions | TOBYINK | gather env vars from Github Actions for Codecov report | 2024-09-06T07:41:09 |
Devel-StatProfiler | MBARBON | low-overhead sampling code profiler | 2024-06-24T21:51:40 |
Device-Chip-From-Sensirion | PEVANS | a collection of chip drivers for Sensirion sensors | 2024-10-23T11:08:42 |
Device-Chip-SCD4x | PEVANS | chip driver for SCD40 and SCD41 | 2024-01-17T16:23:46 |
Dist-Build | LEONT | A modern module builder, author tools not included! | 2024-04-26T10:50:10 |
Dist-Build-XS-Alien | LEONT | Dist::Build extension to use Alien modules. | 2024-08-31T19:19:30 |
Dist-Build-XS-PkgConfig | LEONT | Dist::Build extension to use pkg-config. | 2024-09-13T20:11:49 |
Dist-Zilla-App-Command-DiffMint | HAARG | Compare files to what a minting profile produces | 2024-10-15T08:56:46 |
Dist-Zilla-Plugin-DistBuild | LEONT | Build a Build.PL that uses Dist::Build | 2024-04-26T10:55:35 |
Dist-Zilla-Plugin-DynamicPrereqs-Meta | LEONT | Add dynamic prereqs to to the metadata in our Dist::Zilla build | 2024-04-27T15:50:03 |
Dist-Zilla-Plugin-GitHub-Offline | LEONT | Add a GitHub repo's info to META.{yml,json} | 2024-02-06T19:19:02 |
Dist-Zilla-Plugin-Sah-SchemaBundle | PERLANCAR | Plugin to use when building Sah-SchemaBundle-* distribution | 2024-03-22T00:05:48 |
Dist-Zilla-Plugin-SimpleBootstrap | HAARG | Bootstrap a Dist::Zilla library | 2024-10-04T23:31:37 |
Dist-Zilla-Plugin-Sorter | PERLANCAR | Plugin to use when building Sorter::* distribution | 2024-05-07T00:05:19 |
Dist-Zilla-Plugin-Test-Pod-Coverage-TrustMe | HAARG | An author test for Pod Coverage | 2024-10-09T04:35:48 |
Dist-Zilla-Role-GetDistFileURL | PERLANCAR | Get URL to a file inside a Perl distribution | 2024-03-23T00:05:56 |
Dist-Zilla-Stash-OnePasswordLogin | RJBS | get login credentials from 1Password | 2024-05-25T16:30:09 |
Drought-PET-Thornthwaite | AALLGOOD | Calculate potential evapotranspiration (PET) using the Thornthwaite method | 2024-12-12T21:22:38 |
Environment-Is | PLICEASE | Detect environments like Docker or WSL | 2024-11-23T20:54:33 |
Eugener | EUGENER | A test package. DOnt install. | 2024-11-26T14:55:51 |
Eugener-test | EUGENER | A test package. DOnt install. | 2024-11-20T13:32:00 |
Eugener_test | EUGENER | A test package. DOnt install. | 2024-11-20T13:34:56 |
Eugenertest | EUGENER | A test package. DOnt install. | 2024-11-20T14:04:58 |
Exercises-API | NOBUNAGA | API Ninja's Exercises API | 2024-07-02T19:49:44 |
ExtUtils-Builder | LEONT | An overview of the foundations of the ExtUtils::Builder Plan framework | 2024-04-25T12:14:45 |
ExtUtils-Builder-Compiler | LEONT | Portable compilation | 2024-04-25T13:18:11 |
ExtUtils-Typemaps-Misc | LEONT | A collection of miscelaneous typemap templates | 2024-06-20T19:43:50 |
ExtUtils-Typemaps-Signal | LEONT | asdasd | 2024-01-29T20:58:52 |
Extender | DOMERO | Dynamically enhance Perl objects with additional methods from other modules or custom subroutines | 2024-07-17T10:06:50 |
Extism | EXTISM | Extism Perl SDK | 2024-06-06T04:07:08 |
FFI-Platypus-Lang-V | PLICEASE | Documentation and tools for using Platypus with the V programming language | 2024-12-19T01:16:18 |
Feed-Data-AlJazeera | LNATION | The great new Feed::Data::AlJazeera! | 2024-05-09T05:50:01 |
Feed-Data-BBC | LNATION | Waiting for comedians to present the news | 2024-05-02T08:38:34 |
Feed-Data-CNN | LNATION | The rest of the world will follow. | 2024-05-02T10:11:29 |
File-FindUniq | PERLANCAR | Find unique or duplicate file {contents,names} | 2024-12-05T07:59:52 |
File-Information | LION | generic module for extrating information from filesystems | 2024-10-11T03:10:43 |
File-ShareDir-Tiny | LEONT | Locate per-dist and per-module shared files | 2024-08-25T13:07:29 |
File-SharedVar | CDRAKE | Pure-Perl extension to share variables between Perl processes using files and file locking for their transport | 2024-10-17T12:03:33 |
File-Strfile | SAMYOUNG | OO strfile interface | 2024-10-30T16:27:36 |
File-Util-Rename | PERLANCAR | Utilities related to renaming files | 2024-02-12T00:06:03 |
File-ValueFile | LION | module for reading and writing ValueFile files | 2024-10-03T00:30:39 |
Filter-Syntactic | DCONWAY | Source filters based on syntax, instead of luck | 2024-06-26T22:39:31 |
Frame | CRABAPP | Bare-bones, real-time web framework (WIP) | 2024-10-05T23:42:01 |
Fred-Fish-DBUG | CLEACH | Perl implementation of the Fred Fish C/C++ macros for Perl. | 2024-09-25T18:58:19 |
FreeDesktop-Icons | HANJE | Use icon libraries quick & easy | 2024-05-31T17:26:09 |
Full | TEAM | common pragmata for Perl scripts and modules | 2024-09-14T01:53:27 |
Future-IO-Impl-AnyEvent | MATHIAS | Future::IO implementation using AnyEvent | 2024-11-27T21:09:50 |
GCC-Builtins | BLIAKO | access GCC compiler builtin functions via XS | 2024-03-19T13:31:21 |
Game-Cribbage | LNATION | The great new Game::Cribbage! | 2024-05-15T00:15:17 |
Game-EnergyLoop | JMATES | a simple energy system | 2024-11-05T19:13:51 |
Game-Kezboard | JMATES | a SDL game where cards are used to move around a board | 2024-01-12T00:56:02 |
Game-Snake | LNATION | A clone of the classic snake game using raylib | 2024-12-29T20:33:35 |
Games-Sudoku-Html | SHE | Visualize and play collections of standard 9×9 Sudoku in your browser. | 2024-02-24T20:23:44 |
Games-Sudoku-PatternSolver | SHE | Solve, generate and play Sudoku 9×9 grids. | 2024-01-09T21:26:03 |
Games-Sudoku-Pdf | SHE | Produce pdf files from your digital Sudoku sources or collections. | 2024-02-24T19:57:08 |
Genealogy-FindaGrave | NHORNE | Find URLs on FindaGrave for a person | 2024-11-11T13:01:21 |
Generate | JANESKIL | lib::GenerateSQL | 2024-09-22T09:42:48 |
Geo-Coder-GeoApify | NHORNE | Provides a Geo-Coding functionality using https://www.geoapify.com/maps-api/ | 2024-10-23T17:12:59 |
Geo-Leaflet | MRDVT | Generates Leaflet web page | 2024-11-09T02:48:31 |
Geography-States-Borders | GENE | Return the borders of states and provinces | 2024-01-24T19:03:21 |
Getopt-Lazier | JOJESSF | Lazy Getopt-like BS | 2024-09-02T15:31:38 |
Github-ReleaseFetcher | TEODESIAN | Fetch either the latest or a particular named version of a file in a release from github | 2024-08-13T22:09:19 |
Google-Protobuf-Loader | MATHIAS | Automatically load .proto file using the standard "use" syntax. | 2024-09-22T20:40:43 |
Graphics-ColorNamesCMYK | PERLANCAR | Define CMYK values for common color names | 2024-05-10T00:05:12 |
Graphics-ColorNamesCMYK-All | PERLANCAR | CMYK colors from all Graphics::ColorNamesCMYK::* | 2024-06-03T00:06:12 |
Graphics-ColorNamesCMYK-BannersCom | PERLANCAR | Basic CMYK colors from banners.com | 2024-05-17T00:05:09 |
Graphics-ColorNamesCMYK-JohnDecemberCom | PERLANCAR | CMYK color names from johndecember.com | 2024-05-19T00:05:21 |
Graphics-ColorNamesCMYK-Pantone | PERLANCAR | Pantone colors | 2024-05-14T00:05:54 |
Graphics-ColorNamesCMYK-ToutesLesCouleursCom | PERLANCAR | CMYK colors from http://toutes-les-couleurs.com/ (red) | 2024-05-16T00:06:24 |
Graphics-ColorNamesLite | PERLANCAR | Define RGB values for common color names (lite version) | 2024-05-09T00:05:39 |
HATX | HOEKIT | A fluent interface for Hash and Array Transformations | 2024-06-28T13:43:31 |
Hades-Realm-Rope | LNATION | Hades realm for Moose | 2024-05-20T13:39:42 |
Hash-Iter | PERLANCAR | Generate a coderef iterator for a hash | 2024-11-04T08:09:16 |
HashData-Color-CMYK-JohnDecemberCom | PERLANCAR | CMYK color names (from johndecember.com) | 2024-05-18T00:05:57 |
HashData-Color-CMYK-ToutesLesCouleursCom | PERLANCAR | CMYK color names (from ToutesLesCouleursCom) | 2024-05-15T00:06:01 |
HashData-Color-PantoneToCMYK | PERLANCAR | Mapping of Pantone color names to CMYK values | 2024-05-08T00:05:41 |
HashData-ColorCode-CMYK-JohnDecemberCom | PERLANCAR | CMYK color names (from johndecember.com) | 2024-05-22T00:06:06 |
HashData-ColorCode-CMYK-Pantone | PERLANCAR | Mapping of Pantone color names to CMYK values | 2024-05-23T00:05:32 |
HashData-ColorCode-CMYK-ToutesLesCouleursCom | PERLANCAR | CMYK color names (from ToutesLesCouleursCom) | 2024-05-24T00:06:00 |
HashDataBundle-Display-Resolution | PERLANCAR | HashData::* modules related to Display::Resolution | 2024-11-10T00:05:43 |
IO-Async-Loop-Epoll-FD | LEONT | Use IO::Async with Epoll and special filehandles | 2024-01-27T16:45:26 |
IO-SocketAlarm | NERDVANA | Perform asynchronous actions when a socket changes status | 2024-08-27T06:08:13 |
IPC-MicroSocket | PEVANS | minimal request/response or pub/sub mechanism | 2024-08-05T13:49:53 |
IPCamera-Reolink | SFOBERSKI | Reolink API provides access to the System, Security, Network, Video input, Enc, Record, PTZ, and Alarm functions of a Reolink IP Camera or NVR via HTTP(S)/REST | 2024-01-04T22:06:26 |
ImgurAPI | DILLANBH | Imgur API client | 2024-03-20T03:11:21 |
ImgurAPI-Client | DILLANBH | 2024-03-20T03:31:39 | |
Intellexer-API | HAX | Perl API client for the Intellexer, a webservice that, "enables developers to embed Intellexer semantics products using XML or JSON." | 2024-03-04T02:34:33 |
JIRA-REST-Lite | SHINGO | Lightweight wrapper around Jira's REST API | 2024-08-16T01:20:46 |
JSON-LD | PLICEASE | Load and dump JSON files | 2024-12-07T15:40:25 |
JSON-Ordered-Conditional | LNATION | A conditional language within an ordered JSON struct | 2024-04-06T06:47:37 |
JSON-ToHTML | ARISTOTLE | render JSON-based Perl datastructures as HTML tables | 2024-04-09T04:28:11 |
Kanboard-API | BARBARITO | A Perl interface to the Kanboard API | 2024-07-28T23:46:12 |
Kelp-Module-Beam-Wire | BRTASTIC | Beam::Wire dependency injection container for Kelp | 2024-06-02T14:32:03 |
Kelp-Module-Storage-Abstract | BRTASTIC | Abstract file storage for Kelp | 2024-10-16T20:04:19 |
Kelp-Module-YAML | BRTASTIC | YAML encoder / decoder for Kelp | 2024-06-24T19:18:49 |
KelpX-Controller | BRTASTIC | Base custom controller for Kelp | 2024-07-08T14:43:24 |
Knowledge | RSPIER | a great new dist | 2024-04-27T11:13:53 |
LaTeX-Easy-Templates | BLIAKO | Easily format content into PDF/PS/DVI with LaTeX templates. | 2024-03-15T21:43:58 |
Langertha | GETTY | The clan of fierce vikings with axe and shield to AId your rAId | 2024-08-03T20:56:05 |
Linux-Landlock | MBALLARIN | An interface to the Landlock sandboxing facility of Linux | 2024-05-09T20:12:52 |
List-Stream | RAWLEYFOW | Simple, fast, functional processing of list data | 2024-10-07T15:49:41 |
Local-Acme | CONTRA | The great new Local::Acme! | 2024-11-19T07:43:34 |
Locale-CLDR-Locales-Aa | JGNI | Locale::CLDR – Data Package ( Perl localization data for Afar ) | 2024-02-25T13:32:59 |
Locale-CLDR-Locales-Ab | JGNI | Locale::CLDR – Data Package ( Perl localization data for Abkhazian ) | 2024-02-25T13:34:25 |
Locale-CLDR-Locales-An | JGNI | Locale::CLDR – Data Package ( Perl localization data for Aragonese ) | 2024-02-25T13:39:26 |
Locale-CLDR-Locales-Ann | JGNI | Locale::CLDR – Data Package ( Perl localization data for Obolo ) | 2024-02-25T13:40:49 |
Locale-CLDR-Locales-Apc | JGNI | Locale::CLDR – Data Package ( ) | 2024-02-25T13:42:12 |
Locale-CLDR-Locales-Arn | JGNI | Locale::CLDR – Data Package ( Perl localization data for Mapuche ) | 2024-02-25T13:43:47 |
Locale-CLDR-Locales-Ba | JGNI | Locale::CLDR – Data Package ( Perl localization data for Bashkir ) | 2024-02-25T13:49:23 |
Locale-CLDR-Locales-Bal | JGNI | Locale::CLDR – Data Package ( Perl localization data for Baluchi ) | 2024-02-25T13:49:34 |
Locale-CLDR-Locales-Bew | JGNI | Locale::CLDR – Data Package ( Perl localization data for Betawi ) | 2024-02-25T13:54:02 |
Locale-CLDR-Locales-Bgc | JGNI | Locale::CLDR – Data Package ( Perl localization data for Haryanvi ) | 2024-02-25T13:56:59 |
Locale-CLDR-Locales-Bgn | JGNI | Locale::CLDR – Data Package ( Perl localization data for Western Balochi ) | 2024-02-25T13:57:19 |
Locale-CLDR-Locales-Bho | JGNI | Locale::CLDR – Data Package ( Perl localization data for Bhojpuri ) | 2024-02-25T13:58:45 |
Locale-CLDR-Locales-Blo | JGNI | Locale::CLDR – Data Package ( Perl localization data for Anii ) | 2024-02-25T14:00:25 |
Locale-CLDR-Locales-Blt | JGNI | Locale::CLDR – Data Package ( Perl localization data for Tai Dam ) | 2024-02-25T14:00:36 |
Locale-CLDR-Locales-Bss | JGNI | Locale::CLDR – Data Package ( Perl localization data for Akoose ) | 2024-02-25T14:08:19 |
Locale-CLDR-Locales-Byn | JGNI | Locale::CLDR – Data Package ( Perl localization data for Blin ) | 2024-02-25T14:08:31 |
Locale-CLDR-Locales-Cad | JGNI | Locale::CLDR – Data Package ( Perl localization data for Caddo ) | 2024-02-25T14:11:41 |
Locale-CLDR-Locales-Cch | JGNI | Locale::CLDR – Data Package ( Perl localization data for Atsam ) | 2024-02-25T14:11:52 |
Locale-CLDR-Locales-Ceb | JGNI | Locale::CLDR – Data Package ( Perl localization data for Cebuano ) | 2024-01-06T19:23:24 |
Locale-CLDR-Locales-Cho | JGNI | Locale::CLDR – Data Package ( Perl localization data for Choctaw ) | 2024-02-25T14:16:44 |
Locale-CLDR-Locales-Cic | JGNI | Locale::CLDR – Data Package ( Perl localization data for Chickasaw ) | 2024-02-25T14:19:31 |
Locale-CLDR-Locales-Co | JGNI | Locale::CLDR – Data Package ( Perl localization data for Corsican ) | 2024-02-25T14:21:02 |
Locale-CLDR-Locales-Csw | JGNI | Locale::CLDR – Data Package ( Perl localization data for Swampy Cree ) | 2024-02-25T14:22:49 |
Locale-CLDR-Locales-Cv | JGNI | Locale::CLDR – Data Package ( Perl localization data for Chuvash ) | 2024-02-25T14:25:39 |
Locale-CLDR-Locales-Doi | JGNI | Locale::CLDR – Data Package ( Perl localization data for Dogri ) | 2024-01-06T19:33:59 |
Locale-CLDR-Locales-Dv | JGNI | Locale::CLDR – Data Package ( Perl localization data for Divehi ) | 2024-02-25T14:35:09 |
Locale-CLDR-Locales-Frr | JGNI | Locale::CLDR – Data Package ( Perl localization data for Northern Frisian ) | 2024-02-25T14:51:31 |
Locale-CLDR-Locales-Gaa | JGNI | Locale::CLDR – Data Package ( Perl localization data for Ga ) | 2024-02-25T14:54:34 |
Locale-CLDR-Locales-Gez | JGNI | Locale::CLDR – Data Package ( Perl localization data for Geez ) | 2024-02-25T14:57:36 |
Locale-CLDR-Locales-Gn | JGNI | Locale::CLDR – Data Package ( Perl localization data for Guarani ) | 2024-02-25T14:59:14 |
Locale-CLDR-Locales-Hnj | JGNI | Locale::CLDR – Data Package ( Perl localization data for Hmong Njua ) | 2024-02-25T15:09:00 |
Locale-CLDR-Locales-Ie | JGNI | Locale::CLDR – Data Package ( Perl localization data for Interlingue ) | 2024-02-25T15:20:31 |
Locale-CLDR-Locales-Io | JGNI | Locale::CLDR – Data Package ( Perl localization data for Ido ) | 2024-02-25T15:23:42 |
Locale-CLDR-Locales-Iu | JGNI | Locale::CLDR – Data Package ( Perl localization data for Inuktitut ) | 2024-02-25T15:26:52 |
Locale-CLDR-Locales-Jbo | JGNI | Locale::CLDR – Data Package ( Perl localization data for Lojban ) | 2024-02-25T15:28:43 |
Locale-CLDR-Locales-Kaj | JGNI | Locale::CLDR – Data Package ( Perl localization data for Jju ) | 2024-02-25T15:34:54 |
Locale-CLDR-Locales-Kcg | JGNI | Locale::CLDR – Data Package ( Perl localization data for Tyap ) | 2024-02-25T15:36:25 |
Locale-CLDR-Locales-Ken | JGNI | Locale::CLDR – Data Package ( Perl localization data for Kenyang ) | 2024-02-25T15:39:38 |
Locale-CLDR-Locales-Kgp | JGNI | Locale::CLDR – Data Package ( Perl localization data for Kaingang ) | 2024-01-06T20:28:00 |
Locale-CLDR-Locales-Kpe | JGNI | Locale::CLDR – Data Package ( Perl localization data for Kpelle ) | 2024-02-25T15:51:57 |
Locale-CLDR-Locales-Kxv | JGNI | Locale::CLDR – Data Package ( Perl localization data for Kuvi ) | 2024-02-25T15:59:25 |
Locale-CLDR-Locales-La | JGNI | Locale::CLDR – Data Package ( Perl localization data for Latin ) | 2024-02-25T16:01:00 |
Locale-CLDR-Locales-Lij | JGNI | Locale::CLDR – Data Package ( Perl localization data for Ligurian ) | 2024-02-25T16:05:34 |
Locale-CLDR-Locales-Lmo | JGNI | Locale::CLDR – Data Package ( Perl localization data for Lombard ) | 2024-02-25T16:07:06 |
Locale-CLDR-Locales-Mai | JGNI | Locale::CLDR – Data Package ( Perl localization data for Maithili ) | 2024-01-06T20:57:37 |
Locale-CLDR-Locales-Mdf | JGNI | Locale::CLDR – Data Package ( Perl localization data for Moksha ) | 2024-02-25T16:18:24 |
Locale-CLDR-Locales-Mic | JGNI | Locale::CLDR – Data Package ( Perl localization data for Mi'kmaw ) | 2024-02-25T16:25:46 |
Locale-CLDR-Locales-Mni | JGNI | Locale::CLDR – Data Package ( Perl localization data for Manipuri ) | 2024-01-06T21:09:13 |
Locale-CLDR-Locales-Moh | JGNI | Locale::CLDR – Data Package ( Perl localization data for Mohawk ) | 2024-02-25T16:30:37 |
Locale-CLDR-Locales-Mus | JGNI | Locale::CLDR – Data Package ( Perl localization data for Muscogee ) | 2024-02-25T16:35:33 |
Locale-CLDR-Locales-Myv | JGNI | Locale::CLDR – Data Package ( Perl localization data for Erzya ) | 2024-02-25T16:38:20 |
Locale-CLDR-Locales-No | JGNI | Locale::CLDR – Data Package ( Perl localization data for Norwegian ) | 2024-01-06T21:31:12 |
Locale-CLDR-Locales-Nqo | JGNI | Locale::CLDR – Data Package ( Perl localization data for N’Ko ) | 2024-02-25T16:49:31 |
Locale-CLDR-Locales-Nr | JGNI | Locale::CLDR – Data Package ( Perl localization data for South Ndebele ) | 2024-02-25T16:49:40 |
Locale-CLDR-Locales-Nso | JGNI | Locale::CLDR – Data Package ( Perl localization data for Northern Sotho ) | 2024-02-25T16:51:06 |
Locale-CLDR-Locales-Nv | JGNI | Locale::CLDR – Data Package ( Perl localization data for Navajo ) | 2024-02-25T16:52:52 |
Locale-CLDR-Locales-Ny | JGNI | Locale::CLDR – Data Package ( Perl localization data for Nyanja ) | 2024-02-25T16:54:29 |
Locale-CLDR-Locales-Oc | JGNI | Locale::CLDR – Data Package ( Perl localization data for Occitan ) | 2024-02-25T16:56:13 |
Locale-CLDR-Locales-Osa | JGNI | Locale::CLDR – Data Package ( Perl localization data for Osage ) | 2024-02-25T16:59:38 |
Locale-CLDR-Locales-Pap | JGNI | Locale::CLDR – Data Package ( Perl localization data for Papiamento ) | 2024-02-25T17:02:20 |
Locale-CLDR-Locales-Pcm | JGNI | Locale::CLDR – Data Package ( Perl localization data for Nigerian Pidgin ) | 2024-01-06T21:37:52 |
Locale-CLDR-Locales-Pis | JGNI | Locale::CLDR – Data Package ( Perl localization data for Pijin ) | 2024-02-25T17:04:08 |
Locale-CLDR-Locales-Quc | JGNI | Locale::CLDR – Data Package ( Perl localization data for Kʼicheʼ ) | 2024-02-25T17:10:41 |
Locale-CLDR-Locales-Raj | JGNI | Locale::CLDR – Data Package ( Perl localization data for Rajasthani ) | 2024-02-25T17:10:54 |
Locale-CLDR-Locales-Rhg | JGNI | Locale::CLDR – Data Package ( Perl localization data for Rohingya ) | 2024-02-25T17:12:17 |
Locale-CLDR-Locales-Rif | JGNI | Locale::CLDR – Data Package ( Perl localization data for Riffian ) | 2024-02-25T17:13:41 |
Locale-CLDR-Locales-Sa | JGNI | Locale::CLDR – Data Package ( Perl localization data for Sanskrit ) | 2024-01-06T21:50:35 |
Locale-CLDR-Locales-Sat | JGNI | Locale::CLDR – Data Package ( Perl localization data for Santali ) | 2024-01-06T21:53:44 |
Locale-CLDR-Locales-Sc | JGNI | Locale::CLDR – Data Package ( Perl localization data for Sardinian ) | 2024-01-06T21:55:19 |
Locale-CLDR-Locales-Scn | JGNI | Locale::CLDR – Data Package ( Perl localization data for Sicilian ) | 2024-02-25T17:27:35 |
Locale-CLDR-Locales-Sdh | JGNI | Locale::CLDR – Data Package ( Perl localization data for Southern Kurdish ) | 2024-02-25T17:29:16 |
Locale-CLDR-Locales-Shn | JGNI | Locale::CLDR – Data Package ( Perl localization data for Shan ) | 2024-02-25T17:36:55 |
Locale-CLDR-Locales-Sid | JGNI | Locale::CLDR – Data Package ( Perl localization data for Sidamo ) | 2024-02-25T17:37:33 |
Locale-CLDR-Locales-Skr | JGNI | Locale::CLDR – Data Package ( ) | 2024-02-25T17:40:13 |
Locale-CLDR-Locales-Sma | JGNI | Locale::CLDR – Data Package ( Perl localization data for Southern Sami ) | 2024-02-25T17:41:45 |
Locale-CLDR-Locales-Smj | JGNI | Locale::CLDR – Data Package ( Perl localization data for Lule Sami ) | 2024-02-25T17:43:11 |
Locale-CLDR-Locales-Sms | JGNI | Locale::CLDR – Data Package ( Perl localization data for Skolt Sami ) | 2024-02-25T17:45:00 |
Locale-CLDR-Locales-Ss | JGNI | Locale::CLDR – Data Package ( Perl localization data for Swati ) | 2024-02-25T17:49:56 |
Locale-CLDR-Locales-Ssy | JGNI | Locale::CLDR – Data Package ( Perl localization data for Saho ) | 2024-02-25T17:51:22 |
Locale-CLDR-Locales-St | JGNI | Locale::CLDR – Data Package ( Perl localization data for Southern Sotho ) | 2024-02-25T17:51:52 |
Locale-CLDR-Locales-Su | JGNI | Locale::CLDR – Data Package ( Perl localization data for Sundanese ) | 2024-01-06T22:11:13 |
Locale-CLDR-Locales-Syr | JGNI | Locale::CLDR – Data Package ( Perl localization data for Syriac ) | 2024-02-25T17:56:10 |
Locale-CLDR-Locales-Szl | JGNI | Locale::CLDR – Data Package ( Perl localization data for Silesian ) | 2024-02-25T17:57:55 |
Locale-CLDR-Locales-Tig | JGNI | Locale::CLDR – Data Package ( Perl localization data for Tigre ) | 2024-02-25T18:04:08 |
Locale-CLDR-Locales-Tn | JGNI | Locale::CLDR – Data Package ( Perl localization data for Tswana ) | 2024-02-25T18:07:12 |
Locale-CLDR-Locales-Tok | JGNI | Locale::CLDR – Data Package ( Perl localization data for Toki Pona ) | 2024-02-25T18:08:49 |
Locale-CLDR-Locales-Tpi | JGNI | Locale::CLDR – Data Package ( Perl localization data for Tok Pisin ) | 2024-02-25T18:09:03 |
Locale-CLDR-Locales-Trv | JGNI | Locale::CLDR – Data Package ( Perl localization data for Taroko ) | 2024-02-25T18:13:13 |
Locale-CLDR-Locales-Trw | JGNI | Locale::CLDR – Data Package ( Perl localization data for Torwali ) | 2024-02-25T18:13:24 |
Locale-CLDR-Locales-Ts | JGNI | Locale::CLDR – Data Package ( Perl localization data for Tsonga ) | 2024-02-25T18:15:02 |
Locale-CLDR-Locales-Tyv | JGNI | Locale::CLDR – Data Package ( Perl localization data for Tuvinian ) | 2024-02-25T18:18:06 |
Locale-CLDR-Locales-Ve | JGNI | Locale::CLDR – Data Package ( Perl localization data for Venda ) | 2024-02-25T18:24:31 |
Locale-CLDR-Locales-Vec | JGNI | Locale::CLDR – Data Package ( Perl localization data for Venetian ) | 2024-02-25T18:25:52 |
Locale-CLDR-Locales-Vmw | JGNI | Locale::CLDR – Data Package ( Perl localization data for Makhuwa ) | 2024-02-25T18:28:42 |
Locale-CLDR-Locales-Wa | JGNI | Locale::CLDR – Data Package ( Perl localization data for Walloon ) | 2024-02-25T18:30:55 |
Locale-CLDR-Locales-Wal | JGNI | Locale::CLDR – Data Package ( Perl localization data for Wolaytta ) | 2024-02-25T18:33:42 |
Locale-CLDR-Locales-Wbp | JGNI | Locale::CLDR – Data Package ( Perl localization data for Warlpiri ) | 2024-02-25T18:33:50 |
Locale-CLDR-Locales-Xnr | JGNI | Locale::CLDR – Data Package ( Perl localization data for Kangri ) | 2024-02-25T18:37:03 |
Locale-CLDR-Locales-Yrl | JGNI | Locale::CLDR – Data Package ( Perl localization data for Nheengatu ) | 2024-01-06T22:42:43 |
Locale-CLDR-Locales-Za | JGNI | Locale::CLDR – Data Package ( Perl localization data for Zhuang ) | 2024-02-25T18:44:50 |
Locale-Intl | JDEGUEST | A Web Intl.Locale Class Implementation | 2024-10-07T05:24:54 |
Locale-Unicode-Data | JDEGUEST | Unicode CLDR SQL Data | 2024-08-01T22:43:58 |
Locale-Unicode | JDEGUEST | Unicode Locale Identifier compliant with BCP47 and CLDR | 2024-05-17T08:05:23 |
Log-Any-Simple | MATHIAS | A very thin wrapper around Log::Any, using a functional interface that dies automatically when you log above a given level. | 2024-04-24T19:51:03 |
Log-Log4perl-Config-YamlConfigurator | SVW | Reads Log4perl YAML configurations | 2024-05-29T07:51:19 |
MFab-Plugins-Datadog | DDROWN | Mojolicious plugin for Datadog APM integration | 2024-11-06T16:24:14 |
MIDI-RtMidi-ScorePlayer | GENE | Play a MIDI score in real-time | 2024-07-10T23:51:51 |
MPGA | NNZ | MPGA – a module that makes it easy to write PERL programs | 2024-10-05T14:26:55 |
Mail-Colander | POLETTIX | Categorize and manage email messages | 2024-11-28T06:49:09 |
Mail-Sieve | POLETTIX | Categorize and manage email messages | 2024-11-16T10:11:54 |
Map-Metro-Plugin-Map-London | ETJ | Map::Metro map for London | 2024-09-02T10:23:10 |
Map-Tube-Hamburg | GWS | Interface to the Hamburg U- and S-Bahn maps including AKN | 2024-12-09T01:59:08 |
Map-Tube-Rome | GDT | Interface to the Rome tube map | 2024-08-27T16:01:05 |
Markdown-Perl | MATHIAS | Very configurable Markdown processor written in pure Perl, supporting the CommonMark spec and many extensions | 2024-03-31T21:17:51 |
Math-GSL-Alien | HAKONH | Easy installation of the GSL shared library | 2024-07-17T09:42:10 |
Math-JS | SISYPHUS | Emulate JavaScript arithmetic in perl | 2024-01-11T01:26:01 |
Math-LiveStats | CDRAKE | Pure perl module to make mean, standard deviation, and p-values available for given window sizes in streaming data | 2024-10-11T04:34:55 |
Math-NLopt | DJERIUS | Math::NLopt – Perl interface to the NLopt optimization library | 2024-05-01T07:53:48 |
Math-NumberBase-XS | ZARABOZO | Lighting fast number converter from one base to another base | 2024-11-11T05:30:35 |
Math-Recaman | SIMONW | Calculate numbers in Recamán's sequence | 2024-06-25T17:25:03 |
Math-Symbolic-Custom-Collect | MJOHNSON | Collect up Math::Symbolic expressions | 2024-09-27T09:17:04 |
Math-Symbolic-Custom-CollectSimplify | MJOHNSON | Simplify Math::Symbolic expressions using Math::Symbolic::Custom::Collect | 2024-12-13T10:30:30 |
Math-Symbolic-Custom-Matrix | MJOHNSON | Matrix routines for Math::Symbolic | 2024-12-08T10:54:28 |
Math-Symbolic-Custom-Polynomial | MJOHNSON | Polynomial routines for Math::Symbolic | 2024-11-27T17:41:18 |
Math-Symbolic-Custom-ToShorterString | MJOHNSON | Shorter string representations of Math::Symbolic trees | 2024-09-16T19:09:06 |
MetaCPAN-Pod-HTML | HAARG | Format Pod as HTML for MetaCPAN | 2024-08-26T14:55:52 |
Microsoft-Teams-WebHook | TYRRMINAL | Microsoft Teams WebHook with AdaptiveCards for formatting notifications | 2024-02-20T19:13:14 |
Minima | TESSARIN | Efficient web framework built with modern core classes | 2024-10-04T21:57:09 |
Mo-utils-CSS | SKIM | Mo CSS utilities. | 2024-01-06T14:56:51 |
Mo-utils-Country | SKIM | Mo country utilities. | 2024-04-11T13:41:33 |
Mo-utils-Email | SKIM | Mo utilities for email. | 2024-01-07T23:51:14 |
Mo-utils-IRI | SKIM | Mo utilities for IRI. | 2024-02-29T21:11:02 |
Mo-utils-Time | SKIM | Mo time utilities. | 2024-04-12T14:28:06 |
Mo-utils-TimeZone | SKIM | Mo timezone utilities. | 2024-04-03T16:34:52 |
Mo-utils-URI | SKIM | Mo utilities for URI. | 2024-02-11T16:17:45 |
Module-Features-PluginSystem | PERLANCAR | Features of modules that generate text tables | 2024-03-25T00:05:33 |
Module-Pluggable-_ModuleFeatures | PERLANCAR | Features declaration for Module::Pluggable | 2024-03-26T00:05:20 |
Mojo-UserAgent-Role-Retry | SSMN | Retry requests on failure | 2024-12-29T07:39:35 |
Mojo-UserAgent-Role-TotalTimeout | KARJALA | Role for Mojo::UserAgent that enables setting total timeout including redirects | 2024-11-06T09:41:19 |
MojoUserAgentRoleRetry | SSMN | Retry requests on failure | 2024-12-29T00:59:46 |
Mojolicious-Plugin-Authentication-OIDC | TYRRMINAL | OpenID Connect implementation integrated into Mojolicious | 2024-04-25T19:27:09 |
Mojolicious-Plugin-Authorization-AccessControl | TYRRMINAL | Integrate Authorization::AccessControl into Mojolicious | 2024-05-16T23:23:55 |
Mojolicious-Plugin-BModel | BCDE | Catalyst-like models in Mojolicious | 2024-09-16T17:46:31 |
Mojolicious-Plugin-Badge | GDT | Badge Plugin for Mojolicious | 2024-09-19T21:29:36 |
Mojolicious-Plugin-Config-Structured-Bootstrap | TYRRMINAL | Autoconfigure Mojolicious application and plugins | 2024-05-20T13:39:53 |
Mojolicious-Plugin-Credentials | LEONT | A credentials store in mojo | 2024-01-31T01:34:25 |
Mojolicious-Plugin-Cron-Scheduler | TYRRMINAL | Mojolicious Plugin that wraps Mojolicious::Plugin::Cron for job configurability | 2024-04-16T11:48:54 |
Mojolicious-Plugin-Data-Transfigure | TYRRMINAL | Mojolicious adapter for Data::Transfigure | 2024-05-18T03:42:37 |
Mojolicious-Plugin-INIConfig-Extended | HESCO | Mojolicious Plugin to overload a Configuration | 2024-02-13T02:33:46 |
Mojolicious-Plugin-Migration-Sqitch | TYRRMINAL | Run Sqitch database migrations from a Mojo app | 2024-04-30T15:37:52 |
Mojolicious-Plugin-Module-Loader | TYRRMINAL | Automatically load mojolicious namespaces | 2024-04-19T14:09:36 |
Mojolicious-Plugin-ORM-DBIx | TYRRMINAL | Easily load and access DBIx::Class functionality in Mojolicious apps | 2024-04-03T13:32:06 |
Mojolicious-Plugin-SendEmail | TYRRMINAL | Easily send emails from Mojolicious applications | 2024-04-01T20:40:24 |
Mojolicious-Plugin-Sessionless | TYRRMINAL | Installs noop handlers to disable Mojolicious sessions | 2024-04-16T12:45:37 |
Mojolicious-Plugin-WebComponent | RES | An effort to make creating and using custom web components easier | 2024-02-29T16:06:39 |
MooX-Pack | LNATION | The great new MooX::Pack! | 2024-04-20T01:52:17 |
MooseX-JSONSchema | GETTY | Adding JSON Schema capabilities to your Moose class | 2024-08-03T20:36:33 |
MooseX-Types-StrictScalarTypes | DCANTRELL | strict Moose type constraints for integers, numbers, and Booleans | 2024-09-01T10:27:38 |
Mslm | MSLM | The official Perl Library for Mslm APIs. | 2024-01-16T04:34:02 |
Multi-Dispatch | DCONWAY | Multiple dispatch for Perl subs and methods | 2024-06-26T22:42:28 |
Music-Dice | GENE | Define and roll musical dice | 2024-11-30T22:57:09 |
Neo4j-Driver-Plugin-LWP | AJNN | Neo4j::Driver plug-in for libwww-perl | 2024-12-11T07:09:57 |
Net-Async-OpenExchRates | VNEALV | Interaction with OpenExchangeRates API | 2024-04-20T11:46:28 |
Net-EANSearch | JANW | Perl module for EAN and ISBN lookup and validation using the API on https://www.ean-search.org | 2024-02-13T15:30:27 |
Net-EPP-MITMProxy | GBROWN | A generic EPP proxy server framework. | 2024-05-02T11:56:41 |
Net-EPP-Server | GBROWN | A simple EPP server implementation. | 2024-04-08T09:38:21 |
Net-LineNotify | SHINGO | A simple wrapper for LINE Notify API | 2024-09-29T10:48:48 |
Net-MailChimp | ARTHAS | Perl library with MINIMAL interface to use MailChimp API. | 2024-03-14T13:52:35 |
Net-OpenSSH-More | TEODESIAN | Net::OpenSSH submodule with many useful features | 2024-08-09T00:03:26 |
Net-OpenVPN-Manager | ATOY | Start OpenVPN Manager and return PSGI handler | 2024-03-08T18:07:11 |
Net-PaccoFacile | ARTHAS | Perl library with MINIMAL interface to use PaccoFacile API. | 2024-03-01T16:16:12 |
Net-RDAP-Server | GBROWN | an RDAP server framework. | 2024-10-22T15:56:38 |
Net-SNMP-Mixin-PoE | GAISSMAI | mixin class for power over ethernet related infos from | 2024-09-10T21:05:54 |
Number-Iterator | LNATION | The great new Number::Iterator! | 2024-04-18T19:45:31 |
Number-Iterator-XS | LNATION | iterate numbers faster | 2024-07-26T03:56:33 |
OSLV-Monitor | VVELOX | OS level virtualization monitoring extend for LibreNMS. | 2024-09-10T21:09:59 |
Object-Pad-LexicalMethods | PEVANS | operator for lexical method call syntax | 2024-09-20T19:02:48 |
Object-Pad-Operator-Of | PEVANS | access fields of other instances | 2024-08-22T11:30:12 |
Ogma | LNATION | Command Line Applications via Rope | 2024-05-07T08:27:11 |
OpenAPI-PerlGenerator | CORION | create Perl client SDKs from OpenAPI specs | 2024-03-24T11:02:37 |
OpenFeature-SDK | CATOUC | OpenFeature SDK for Perl | 2024-08-17T19:40:04 |
OpenMP | OODLER | Metapackage for using OpenMP in Perl | 2024-07-19T21:12:29 |
OpenSearch | LHRST | It's new $module | 2024-05-15T15:22:31 |
PDK-Concern | CARELINE | PDK::Concern::H3c::Netdisco – Explore and manage LLDP topology for H3C devices | 2024-10-17T13:32:50 |
PDK-Content | CARELINE | Content parsing and management for PDK | 2024-10-06T13:51:02 |
PDK-Crate | CARELINE | Everything that PDK modules needs | 2024-10-05T12:19:02 |
PDK-DBI | CARELINE | PDK::DBI- A Moose-based wrapper for MySQL|Postgresql database operations using DBIx::Custom | 2024-10-10T03:28:18 |
PDK-Utils | CARELINE | Utility functions for PDK | 2024-10-07T06:42:24 |
PDL-Complex | ETJ | handle complex numbers (DEPRECATED – use native complex) | 2024-11-26T02:13:32 |
PDL-Fit | ETJ | Levenberg-Marquardt fitting routine for PDL | 2024-12-09T12:50:50 |
PDL-GSL | ETJ | parameter estimations and probability density functions for distributions. | 2024-12-09T06:08:00 |
PDL-Graphics-IIS | ETJ | Display PDL images on IIS devices (saoimage/ximtool) | 2024-09-30T06:20:14 |
PDL-Graphics-Limits | ETJ | derive limits for display purposes | 2024-11-25T23:17:09 |
PDL-IO-Browser | ETJ | 2D data browser for PDL | 2024-11-26T00:27:38 |
PDL-IO-Dicom | ETJ | a module for reading DICOM images. | 2024-12-04T02:35:06 |
PDL-IO-ENVI | ETJ | read ENVI data files into PDL | 2024-12-04T04:14:07 |
PDL-IO-GD | ETJ | Interface to the GD image library. | 2024-12-03T05:35:52 |
PDL-IO-HDF | ETJ | A PDL interface to the HDF4 library. | 2024-12-03T04:08:00 |
PDL-IO-IDL | ETJ | I/O of IDL Save Files | 2024-12-04T02:48:09 |
PDL-Minuit | ETJ | Minuit 1 optimization routines for PDL | 2024-09-30T12:57:29 |
PDL-Opt-GLPK | SOMMREY | PDL interface to the GNU Linear Programming Kit | 2024-02-21T20:32:39 |
PDL-Opt-Simplex | ETJ | Simplex optimization routines | 2024-12-09T13:46:51 |
PDL-Perldl2 | ETJ | Simple shell (version 2) for PDL | 2024-11-26T02:48:28 |
PDL-Transform-Proj4 | ETJ | PDL::Transform interface to the Proj4 projection library | 2024-12-03T08:19:30 |
POE-Wheel-Run-DaemonHelper | VVELOX | Helper for the POE::Wheel::Run for easy controlling logging of stdout/err as well as restarting with backoff. | 2024-01-16T18:57:00 |
Parallel-TaskExecutor | MATHIAS | Cross-platform executor for parallel tasks executed in forked processes | 2024-04-13T20:02:27 |
Parse-SNI | OLEG | parse Server Name Indication from TLS handshake | 2024-01-23T07:57:10 |
Password-OnePassword-OPCLI | RJBS | get items out of 1Password with the "op" CLI | 2024-05-25T15:24:06 |
Perl-Critic-Policy-Plicease-ProhibitArrayAssignAref | PLICEASE | Don't assign an anonymous arrayref to an array | 2024-12-28T21:26:23 |
Perl-PrereqScanner-Scanner-DistBuild | LEONT | scan for Dist::Build dependencies | 2024-07-12T12:03:35 |
Perl-Version-Bumper | BOOK | Update use VERSION on any Perl code | 2024-11-20T08:48:15 |
PerlIO-win32console | TONYC | Win32 console output layer | 2024-05-26T13:03:53 |
Perlgram-Bot | AMIRCANDY | 2024-02-26T10:23:19 | |
Plack-App-CPAN-Changes | SKIM | Plack application for CPAN::Changes object. | 2024-03-14T18:23:48 |
Plack-App-Catmandu-OAI | NJFRANCK | drop in replacement for Dancer::Plugin::Catmandu::OAI | 2024-08-23T07:58:26 |
Plack-App-Catmandu-SRU | NJFRANCK | drop in replacement for Dancer::Plugin::Catmandu::SRU | 2024-07-30T09:27:07 |
Plack-App-ChangePassword | SKIM | Plack change password application. | 2024-02-08T21:15:14 |
Plack-App-Login-Request | SKIM | Plack application for request of login information. | 2024-04-29T14:23:02 |
Plack-App-Storage-Abstract | BRTASTIC | Serve files with Storage::Abstract | 2024-10-13T20:10:13 |
Plack-Middleware-Static-Precompressed | ARISTOTLE | serve a tree of static pre-compressed files | 2024-03-14T11:30:12 |
Plack-Middleware-Validate_Google_IAP_JWT | HKOBA | Validate JWT from Google IAP | 2024-12-18T05:31:51 |
Plack-Middleware-Zstandard | PLICEASE | Compress response body with Zstandard | 2024-05-10T18:08:23 |
Plugin-System-_ModuleFeatures | PERLANCAR | Features declaration for Plugin::System | 2024-03-27T00:05:42 |
Pod-Weaver-Plugin-Sah-SchemaBundle | PERLANCAR | Plugin to use when building Sah::SchemaBundle::* distribution | 2024-02-26T00:06:02 |
Pongo | HAPPYBEAR | A Perl MongoDB interface using XS and the MongoDB C driver. | 2024-11-29T05:14:14 |
Poz | YTURTLE | A simple, composable, and extensible data validation library for Perl. | 2024-12-08T07:09:15 |
Protocol-Sys-Virt-Devel | EHUELS | Development helper for Protocol::Sys::Virt and its dependants | 2024-08-31T20:48:14 |
Protocol-Sys-Virt | EHUELS | Transport independent implementation of the remote LibVirt protocol | 2024-08-31T21:36:13 |
QRCode-Any | PERLANCAR | Common interface to QRCode functions | 2024-05-06T00:06:19 |
Qhull | DJERIUS | a really awesome library | 2024-03-05T20:37:59 |
Quaint | LNATION | The great new Quaint! | 2024-10-27T12:51:58 |
RDF-Cowl | ZMUGHAL | A lightweight API for working with OWL 2 ontologies | 2024-02-15T03:37:41 |
RT-Extension-Import-CSV | BPS | RT-Extension-Import-CSV Extension | 2024-05-15T18:16:51 |
RT-Extension-SMSWebhook-Twilio | BPS | RT-Extension-SMSWebhook-Twilio Extension | 2024-06-13T22:40:55 |
Random-Simple | BAKERSCOT | Simple, usable, real world random numbers | 2024-11-08T23:59:53 |
Raylib-FFI | PERIGRIN | Perl FFI bindings for raylib | 2024-06-27T05:58:30 |
RecentInfo-Manager | CORION | 2024-11-03T07:32:19 | |
Regex-Common | ARFREITAS | Provide commonly requested regular expressions | 2024-08-12T22:48:17 |
Regexp-IntInequality | HAUKEX | generate regular expressions to match integers greater than / less than / etc. a value | 2024-03-08T17:59:24 |
Result-Simple | KFLY | A dead simple perl-ish Result like F#, Rust, Go, etc. | 2024-11-23T09:23:32 |
Rex-Commands-PerlSync | BRTASTIC | Sync directories, better | 2024-10-01T18:26:54 |
Rope-Cmd | LNATION | Command Line Applications via Rope | 2024-08-30T20:00:03 |
RxPerl-Extras | KARJALA | extra operators for RxPerl | 2024-08-06T10:18:15 |
SMS-Send-CZ-Smsmanager | RADIUSCZ | SMS::Send driver for SMS Manager – Czech Republic | 2024-06-10T20:58:38 |
SPVM-HTTP-Tiny | KIMOTO | HTTP Client | 2024-02-23T08:53:30 |
SPVM-IO-Socket-SSL | KIMOTO | Sockets for SSL. | 2024-10-28T02:23:49 |
SPVM-Net-DNS-Native | KIMOTO | Short Description | 2024-10-21T07:04:37 |
SPVM-R | KIMOTO | Porting R language Features | 2024-06-26T05:10:09 |
SPVM-Resource-Eigen | KIMOTO | Resource for C++ Eigen library | 2024-07-16T23:56:40 |
SQL-Formatter | PLICEASE | Format SQL using the rust sqlformat library | 2024-10-05T00:09:29 |
STIX | GDT | Structured Threat Information Expression (STIX) | 2024-11-18T11:00:07 |
Sah-PSchemaBundle | PERLANCAR | Convention for Sah-PSchemaBundle-* distribution | 2024-06-06T00:06:05 |
Sah-PSchemaBundle-Array | PERLANCAR | Parameterized schemas related to array type | 2024-06-07T00:06:17 |
Sah-PSchemaBundle-Perl | PERLANCAR | Parameterized schemas related to Perl | 2024-06-08T00:06:02 |
Sah-PSchemaBundle-Re | PERLANCAR | Various regular-expression (parameterized) schemas | 2024-06-09T00:05:56 |
Sah-SchemaBundle | PERLANCAR | Convention for Sah-SchemaBundle-* distribution | 2024-03-28T00:05:49 |
Sah-SchemaBundle-Array | PERLANCAR | Sah schemas related to array type | 2024-03-29T00:05:36 |
Sah-SchemaBundle-ArrayData | PERLANCAR | Sah schemas related to ArrayData | 2024-03-30T00:05:33 |
Sah-SchemaBundle-Binary | PERLANCAR | Sah schemas related to binary data | 2024-03-17T00:05:16 |
Sah-SchemaBundle-Bool | PERLANCAR | Sah schemas related to bool data type | 2024-03-24T00:05:52 |
Sah-SchemaBundle-BorderStyle | PERLANCAR | Sah schemas related to BorderStyle | 2024-03-31T00:05:05 |
Sah-SchemaBundle-Business-ID-BCA | PERLANCAR | Sah schemas related to BCA (Bank Central Asia) bank | 2024-04-23T00:05:53 |
Sah-SchemaBundle-Business-ID-Mandiri | PERLANCAR | Sah schemas related to Mandiri bank | 2024-04-30T00:05:43 |
Sah-SchemaBundle-Business-ID-NIK | PERLANCAR | Sah schemas related to Indonesian citizenship registration numbers (NIK) | 2024-05-01T00:05:13 |
Sah-SchemaBundle-Business-ID-NKK | PERLANCAR | Sah schemas related to Indonesian family card number (NKK) | 2024-05-02T00:05:52 |
Sah-SchemaBundle-Business-ID-NOPPBB | PERLANCAR | Sah schemas related to Indonesian property tax numbers (NOP PBB) | 2024-05-03T00:05:32 |
Sah-SchemaBundle-Business-ID-NPWP | PERLANCAR | Sah schemas related to Indonesian taxpayer registration number (NPWP) | 2024-05-04T00:05:59 |
Sah-SchemaBundle-Business-ID-SIM | PERLANCAR | Sah schemas related to Indonesian driving license number (nomor SIM) | 2024-05-05T00:06:14 |
Sah-SchemaBundle-CPAN | PERLANCAR | Sah schemas related to CPAN | 2024-06-15T00:07:21 |
Sah-SchemaBundle-Chrome | PERLANCAR | Various Sah schemas related to Google Chrome | 2024-06-12T00:06:44 |
Sah-SchemaBundle-Code | PERLANCAR | Various schemas related to 'code' type and coderefs | 2024-06-10T04:51:45 |
Sah-SchemaBundle-Collection | PERLANCAR | Various Sah collection (array/hash) schemas | 2024-06-16T00:05:16 |
Sah-SchemaBundle-Color | PERLANCAR | Sah schemas related to color codes/names | 2024-06-12T00:06:55 |
Sah-SchemaBundle-ColorScheme | PERLANCAR | Sah schemas related to color schemes | 2024-06-23T15:45:50 |
Sah-SchemaBundle-ColorTheme | PERLANCAR | Sah schemas related to ColorTheme | 2024-06-30T00:05:31 |
Sah-SchemaBundle-Comparer | PERLANCAR | Sah schemas related to Comparer | 2024-04-21T00:05:30 |
Sah-SchemaBundle-Country | PERLANCAR | Various Sah schemas related to country codes/names | 2024-07-14T00:05:31 |
Sah-SchemaBundle-Currency | PERLANCAR | Various Sah currency schemas | 2024-07-21T00:06:11 |
Sah-SchemaBundle-DBI | PERLANCAR | Schemas related to DBI | 2024-07-28T00:06:12 |
Sah-SchemaBundle-DNS | PERLANCAR | Schemas related to DNS | 2024-08-04T00:05:19 |
Sah-SchemaBundle-Data-Sah | PERLANCAR | Sah schemas related to Data::Sah | 2024-08-11T00:06:03 |
Sah-SchemaBundle-DataSizeSpeed | PERLANCAR | Sah schemas related to data sizes & speeds (filesize, transfer speed, etc) | 2024-08-02T21:50:19 |
Sah-SchemaBundle-Nutrient | PERLANCAR | Sah schemas related to nutrients | 2024-06-04T02:10:55 |
Sah-SchemaBundle-Path | PERLANCAR | Schemas related to filesystem path | 2024-04-01T00:06:15 |
Sah-SchemaBundle-Perl | PERLANCAR | Sah schemas related to Perl | 2024-04-02T00:05:40 |
Sah-SchemaBundle-SortKey | PERLANCAR | Sah schemas related to SortKey | 2024-04-22T00:06:02 |
Sah-SchemaBundle-Sorter | PERLANCAR | Sah schemas related to Sorter | 2024-04-03T00:14:57 |
Sah-Schemas-Business-ID-BCA | PERLANCAR | Sah schemas related to BCA (Bank Central Asia) bank | 2024-01-19T00:06:10 |
Sah-Schemas-Business-ID-Mandiri | PERLANCAR | Sah schemas related to Mandiri bank | 2024-01-20T00:05:58 |
Sah-Schemas-Business-ID-NIK | PERLANCAR | Sah schemas related to Indonesian NIK numbers | 2024-01-09T07:04:01 |
Sah-Schemas-Business-ID-NKK | PERLANCAR | Sah schemas related to Indonesian family card number (NKK) | 2024-01-09T09:11:12 |
Sah-Schemas-Business-ID-NOPPBB | PERLANCAR | Sah schemas related to Indonesian property tax numbers (NOP PBB) | 2024-01-12T00:11:12 |
Sah-Schemas-Business-ID-NPWP | PERLANCAR | Sah schemas related to Indonesian taxpayer registration number (NPWP) | 2024-01-11T00:11:24 |
Sah-Schemas-Business-ID-SIM | PERLANCAR | Sah schemas related to Indonesian driving license number (nomor SIM) | 2024-01-15T00:19:01 |
Sah-Schemas-Comparer | PERLANCAR | Sah schemas related to Comparer | 2024-02-16T00:06:37 |
Sah-Schemas-HashData | PERLANCAR | Sah schemas related to HashData | 2024-01-24T00:05:49 |
Sah-Schemas-SortKey | PERLANCAR | Sah schemas related to SortKey | 2024-02-18T14:01:38 |
Sah-Schemas-Sorter | PERLANCAR | Sah schemas related to Sorter | 2024-04-03T00:05:43 |
Salus | LNATION | The great new Salus! | 2024-05-09T20:06:13 |
Seven | LNATION | The great new Seven! | 2024-04-13T03:30:11 |
Shannon-Entropy-XS | LNATION | Calculate the Shannon entropy H of a given input string faster. | 2024-07-18T19:43:55 |
Slack-BlockKit | RJBS | a toolkit for building BlockKit blocks for Slack | 2024-07-04T01:51:26 |
Slackware-SBoKeeper | SAMYOUNG | SlackBuild package manager helper | 2024-10-31T22:09:33 |
SlapbirdAPM-Agent-Dancer2 | RAWLEYFOW | Agent software for the Perl application performance monitor, Slapbird. slapbirdapm.com | 2024-08-25T20:49:02 |
SlapbirdAPM-Agent-Mojo | RAWLEYFOW | Agent software for the Perl application performance monitor, Slapbird. slapbirdapm.com | 2024-08-09T21:00:04 |
SlapbirdAPM-Agent-Plack | RAWLEYFOW | A Plack agent software for the Perl application performance monitor, Slapbird. slapbirdapm.com | 2024-08-18T02:37:49 |
SmsAero | SMSAERO | Send SMS via smsaero.ru | 2024-12-09T11:55:19 |
Socket-More-Interface | DRCLAW | Query network interfaces of your system | 2024-01-15T22:26:01 |
Socket-More-Lookup | DRCLAW | System DNS Lookup Routines | 2024-01-17T00:58:57 |
Socket-More-Resolver | DRCLAW | Loop Agnostic Asynchronous DNS Resolving | 2024-01-17T22:58:34 |
Software-Security-Policy | TIMLEGGE | Create a Security Policy for your distribution | 2024-12-16T03:23:46 |
Sort-BySimilarity | PERLANCAR | Sort by most similar to a reference string | 2024-02-02T00:05:29 |
Sort-Key-SortKey | PERLANCAR | Thin wrapper for Sort::Key to easily use SortKey::* | 2024-04-04T00:05:05 |
Sort-SubBundle-BySimilarity | PERLANCAR | Sort::Sub subroutines that sort items based on how similar it is to the target string | 2024-01-18T00:05:32 |
Sort-SubBundle-DefHash | PERLANCAR | Sort::Sub subroutines related to DefHash | 2024-01-13T00:11:24 |
Sort-SubBundle-Rinci | PERLANCAR | Sort::Sub subroutines related to Rinci | 2024-01-16T00:11:13 |
Sort-SubBundle-Sah | PERLANCAR | Sort::Sub subroutines related to Sah | 2024-01-17T00:05:53 |
SortExample | PERLANCAR | Sort examples | 2024-02-27T00:05:08 |
SortExample-Color-Rainbow-EN | PERLANCAR | Ordered list of names of colors in the rainbow, in English | 2024-04-05T00:06:12 |
SortKey | PERLANCAR | Reusable sort key generators | 2024-02-14T00:05:41 |
SortKey-Num-by_length | PERLANCAR | String length as sort key | 2024-02-15T00:05:40 |
SortKey-Num-file_mtime | PERLANCAR | File modification time as sort key | 2024-10-13T00:05:46 |
SortKey-Num-file_num_links | PERLANCAR | File number of (hard) links as sort key | 2024-12-08T00:05:11 |
SortKey-Num-file_size | PERLANCAR | File size as sort key | 2024-12-15T00:05:21 |
SortKey-Num-length | PERLANCAR | String length as sort key | 2024-02-15T00:05:51 |
SortKey-Num-pattern_count | PERLANCAR | Number of occurrences of string/regexp pattern as sort key | 2024-04-06T00:05:41 |
SortKey-Num-similarity | PERLANCAR | Similarity to a reference string as sort key | 2024-04-08T00:05:21 |
SortKey-Num-similarity_jaccard | PERLANCAR | Jaccard coefficient of a string to a reference string, as sort key | 2024-05-30T00:05:29 |
SortKey-date_in_text | PERLANCAR | Date found in text as sort key | 2024-04-19T00:05:23 |
SortSpec | PERLANCAR | Specification of sort specification | 2024-04-09T00:05:37 |
SortSpec-Perl-CPAN-ChangesGroup-PERLANCAR | PERLANCAR | Specification to sort changes group heading PERLANCAR-style | 2024-04-10T00:05:24 |
Sorter | PERLANCAR | Sorter | 2024-02-07T00:11:23 |
Sorter-by_similarity | PERLANCAR | Sort by most similar to a reference string | 2024-02-13T00:06:16 |
Sorter-file_by_mtime | PERLANCAR | Sort files by mtime (modification time) | 2024-10-20T00:06:08 |
Sorter-file_by_num_links | PERLANCAR | Sort files by number of (hard) links | 2024-12-22T00:06:07 |
Sorter-file_by_size | PERLANCAR | Sort files by size | 2024-12-29T00:05:07 |
Sorter-from_comparer | PERLANCAR | Sort by comparer generated by a Comparer:: module | 2024-04-11T00:05:17 |
Sorter-from_sortkey | PERLANCAR | Sort by keys generated by a SortKey:: module | 2024-04-12T00:05:58 |
SpeL-Wizard | WDAEMS | engine to build audio files from the spel files generated by SpeL and maintain their up-to-dateness | 2024-06-10T08:18:20 |
SpeL-Wizard-20240610-TRIAL | WDAEMS | engine to build audio files from the spel files generated by SpeL and maintain their up-to-dateness | 2024-06-10T08:02:44 |
Sq | DAVIDRAAB | The Sq Language | 2024-12-25T05:30:48 |
Sqids | MYSOCIETY | generate short unique identifiers from numbers | 2024-04-06T10:43:27 |
Standup-Diary | SMONFF | Manage a simple journal in Markdown files | 2024-11-23T14:08:21 |
Storage-Abstract | BRTASTIC | Abstraction for file storage | 2024-10-13T14:20:10 |
Str-Iter | PERLANCAR | Generate a coderef iterator to iterate a string one (or more) character(s) at a time | 2024-11-17T00:06:14 |
String-Mask-XS | LNATION | mask sensitive data faster | 2024-07-03T21:44:29 |
String-Random-Regexp-regxstring | BLIAKO | Generate random strings from a regular expression | 2024-06-28T23:27:57 |
Sumu-Perl-Modules | CEEJAY | Perl Modules | 2024-09-28T16:29:04 |
Sumu-Windows-Upm4win | CEEJAY | User Pass Manager For Windows | 2024-12-13T16:44:11 |
Super-Powers | LNATION | The hiddden truth | 2024-05-02T04:21:41 |
Switch-Back | DCONWAY | given/when for a post-given/when Perl | 2024-06-26T22:42:39 |
Switch-Right | DCONWAY | Switch and smartmatch done right this time | 2024-06-26T22:45:36 |
Syntax-Keyword-Assert | PEVANS | debugging checks that throw exceptions | 2024-10-01T14:24:23 |
Syntax-Keyword-PhaserExpression | PEVANS | phasers as arbitrary expressions rather than blocks | 2024-10-28T21:33:23 |
Syntax-Operator-Is | PEVANS | match operator using Data::Checks constraints | 2024-07-08T14:59:03 |
Sys-Async-Virt | EHUELS | LibVirt protocol implementation for clients | 2024-09-15T20:23:53 |
Sys-Ebpf | TAKEMIO | Pure-Perl interface for eBPF (extended Berkeley Packet Filter) | 2024-10-04T10:02:24 |
Sys-GetRandom-FFI | RRWO | get random bytes from the system | 2024-12-27T00:55:22 |
Sys-GetRandom-PP | MAUKE | pure Perl interface to getrandom(2) | 2024-06-15T05:21:14 |
TAP-Formatter-JUnit-PrintTxtStdout | DERIV | … | 2024-01-31T10:09:30 |
TLV-EMV-Parser | GHE | A module for parsing EMV TLV strings | 2024-01-15T21:42:01 |
TLV-EMV-Tags | GHE | A module that holds all EMV tags | 2024-01-15T21:42:12 |
TLV-Parser | GHE | A module for parsing TLV strings | 2024-01-15T21:26:13 |
TableData-Business-ID-BPOM-FoodAdditive | PERLANCAR | Food additives in BPOM | 2024-04-10T11:10:00 |
TableData-Business-ID-BPOM-NutritionLabelRef | PERLANCAR | Nutrients | 2024-05-27T00:05:19 |
TableData-Business-ID-Kemenkes-RDA | PERLANCAR | Indonesian RDA (AKG, Angka Kecukupan Gizi) | 2024-07-02T03:05:09 |
TableData-Health-Nutrient | PERLANCAR | Nutrients | 2024-05-25T00:05:57 |
TableDataRole-Source-DBI | PERLANCAR | Role to access table data from DBI | 2024-05-28T00:05:38 |
TableDataRole-Source-SQLite | PERLANCAR | Role to access table data from SQLite database table/query | 2024-05-29T00:05:31 |
Tags-HTML-CPAN-Changes | SKIM | Tags helper for CPAN::Changes object. | 2024-03-14T09:55:49 |
Tags-HTML-ChangePassword | SKIM | Tags helper for change password. | 2024-02-07T20:04:02 |
Tags-HTML-DefinitionList | SKIM | Tags helper for definition list. | 2024-05-17T16:54:28 |
Tags-HTML-Element | SKIM | Tags helper for HTML elements. | 2024-01-06T18:37:31 |
Tags-HTML-Footer | SKIM | Tags helper for HTML footer. | 2024-06-03T15:29:04 |
Tags-HTML-Image | SKIM | Tags helper class for image presentation. | 2024-04-20T13:32:39 |
Tags-HTML-Login-Request | SKIM | Tags helper for login request. | 2024-04-29T11:23:37 |
Tags-HTML-Message-Board | SKIM | Tags helper for message board. | 2024-06-03T16:01:31 |
Tags-HTML-Navigation-Grid | SKIM | Tags helper for navigation grid. | 2024-05-10T18:53:42 |
Tags-HTML-Tree | SKIM | Tags helper for Tree. | 2024-05-01T16:50:02 |
Task-Map-Tube | MANWAR | Bundles Map::Tube::* map packages. | 2024-12-12T11:04:59 |
Task-Map-Tube-Bundle | MANWAR | Bundles Map::Tube::* map packages. | 2024-12-12T11:33:02 |
Task-MemManager | CHRISARG | A memory allocator for low level code in Perl. | 2024-08-25T23:42:18 |
Task-MemManager-CMalloc | CHRISARG | Allocates buffers using C's malloc | 2024-12-02T03:33:50 |
Tcl-Tk-Tkwidget-Tix | VKON | 2024-01-01T18:54:55 | |
Tcl-Tk-Tkwidget-treectrl | VKON | 2024-01-01T08:54:34 | |
Temperature-Calculate-DegreeDays | AALLGOOD | Perl package to compute cooling, heating, and growing degree days | 2024-12-03T20:23:00 |
Template-EmbeddedPerl | JJNAPIORK | Validation Library and more | 2024-09-13T22:16:50 |
Template-Plexsite | DRCLAW | Class for interlinked templating | 2024-11-12T03:59:54 |
Template-Plugin-Package | PETDANCE | allow calling of class methods on arbitrary classes that do not accept the class name as their first argument. | 2024-03-12T04:32:11 |
Term-ANSI-Sprintf | LNATION | sprintf with ANSI colors | 2024-08-25T09:46:37 |
Test-SpellCheck | PLICEASE | Check spelling of POD and other documents | 2024-12-29T15:22:23 |
Test-SpellCheck-Plugin-Lang-EN-US | PLICEASE | US English language dictionary for Test::SpellCheck | 2024-12-29T15:25:31 |
Test2-Plugin-DBBreak | JOSERIJO | Automatic breakpoint on failing tests for the perl debugger | 2024-10-23T12:08:14 |
Test2-Tools-ComboObject | PLICEASE | Combine checks and diagnostics into a single test as an object | 2024-11-22T07:22:27 |
Test2-Tools-MIDI | JMATES | test MIDI file contents | 2024-04-09T23:42:34 |
Text-Schmutz | RRWO | You̇r screen is quiṭe dirty, please cleȧn it. | 2024-07-11T22:41:24 |
Text-Table-Boxed | JIMAVERA | Automate separators and rules for Text::Table | 2024-12-12T01:46:12 |
Text-Template-Tiny | JV | Variable substituting template processor | 2024-07-05T11:10:37 |
Tie-Array-ArrayData | PERLANCAR | Access ArrayData object as a tied array | 2024-01-23T00:05:50 |
Tie-Array-TableData | PERLANCAR | Access TableData object as a tied array | 2024-01-22T00:05:54 |
Tie-Hash-DataSection | PLICEASE | Access __DATA__ section via tied hash | 2024-12-24T05:56:04 |
Tie-Hash-HashData | PERLANCAR | Access HashData object as a tied hash | 2024-02-09T00:05:25 |
Time-RTM | CADE | Run-time metrics stats | 2024-12-01T23:49:06 |
Tiny-Prof | TIMKA | Perl profiling made simple to use. | 2024-04-26T07:19:38 |
Tk-AppWindow | HANJE | an application framework based on Tk | 2024-02-28T15:29:57 |
Tk-DynaMouseWheelBind | HANJE | Wheel scroll panes filled with widgets | 2024-05-20T18:53:11 |
Tk-FileBrowser | HANJE | Multi column file system explorer | 2024-03-29T21:22:08 |
Tk-PodViewer | HANJE | Simple ROText based pod viewer. | 2024-08-20T20:06:36 |
Tk-Terminal | HANJE | Running system commands in a Tk::Text widget. | 2024-07-03T09:24:21 |
Tradestie-WSBetsAPI | NOBUNAGA | Tradestie's Wallstreet Bets API | 2024-07-01T19:57:16 |
URI-Shorten | TEODESIAN | Shorten URIs so that you don't have to rely on external services | 2024-05-07T08:50:48 |
URI-Shortener | TEODESIAN | Shorten URIs so that you don't have to rely on external services | 2024-05-07T15:53:48 |
UserAgent-Any | MATHIAS | Wrapper above any UserAgent library, supporting sync and async calls. | 2024-11-15T21:01:30 |
UserAgent-Any-JSON | MATHIAS | Specialization of UserAgent::Any for JSON APIs. | 2024-11-16T20:16:37 |
UserPassManagerForWindows | CEEJAY | User Pass Manager For Windows | 2024-12-13T15:39:41 |
Version-libversion-XS | GDT | Perl binding for libversion | 2024-05-01T20:09:03 |
WWW-Gemini | ANTONOV | 2024-03-11T02:51:49 | |
WWW-Suffit-Plugin-ConfigAuth | ABALAMA | The Suffit plugin for authentication and authorization providing via configuration | 2024-02-22T08:17:35 |
WWW-Suffit-Plugin-FileAuth | ABALAMA | The Suffit plugin for authentication and authorization by password file | 2024-02-22T11:39:04 |
WWW-Suffit-Plugin-SuffitAuth | ABALAMA | The Suffit plugin for Suffit API authentication and authorization providing | 2024-02-22T08:16:09 |
Weather-API-Base | DKECHAG | Base/util module for Weather API clients | 2024-10-30T23:56:19 |
Weather-OWM | DKECHAG | Perl client for the OpenWeatherMap (OWM) API | 2024-10-06T19:54:59 |
Web-ACL | VVELOX | A helper for creating basic apikey/slug/IP based ACLs. | 2024-01-02T20:17:08 |
Web-Async | TEAM | Future-based web+HTTP handling | 2024-04-23T16:50:24 |
WebGPU-Direct | ATRODO | Direct access to the WebGPU native APIs. | 2024-01-29T03:54:38 |
WebService-Chroma | LNATION | chromadb client | 2024-12-16T20:45:59 |
WebService-GrowthBook | DERIV | … | 2024-07-02T11:17:20 |
WebService-Hydra | DERIV | … | 2024-09-30T03:56:43 |
WebService-Xential | WATERKIP | A Xential REST API module | 2024-09-18T01:25:55 |
Webservice-CVEDB-API | HAX | Fast Vulnerability lookups using CVE_ID and CPE23 | 2024-09-15T22:49:58 |
Webservice-InternetDB-API | HAX | Fast IP Lookups for Open Ports and Vulnerabilities using InternetDB API | 2024-09-08T20:11:16 |
Webservice-Ipify-API | HAX | Lookup your IP address using Ipify.org | 2024-09-08T21:44:08 |
Webservice-KeyVal-API | OODLER | Perl API client for the REPLACE API service, https://keyval.org/. | 2024-09-23T19:21:15 |
Webservice-Purgomalum-API | HAX | Filter and removes profanity and unwanted text from input using PurgoMalum.com's free API | 2024-09-20T22:41:00 |
Webservice-Sendy-API | OODLER | Sendy's integration API Perl client and commandline utility | 2024-12-29T21:28:13 |
Whelk | BRTASTIC | A friendly API framework based on Kelp | 2024-07-03T13:59:32 |
Win32-Console-DotNet | BRICKPOOL | Win32 Console .NET interface | 2024-07-29T07:05:25 |
Win32API-RecentFiles | CORION | recently accessed file API functions on Windows | 2024-07-29T18:16:24 |
XDR-Gen | EHUELS | Generate (de)serializers for XDR definitions | 2024-06-08T15:05:53 |
XDR-Parse | EHUELS | Parse XDR (eXternal Data Representation) definitions into an AST (Abstract Syntax Tree) | 2024-05-17T13:01:54 |
XTP | DYLIBSO | XTP Perl SDK | 2024-10-30T23:18:31 |
Xerarch | LNATION | The great new Xerarch! | 2024-10-09T19:27:16 |
YAGL | RLOVELAND | Yet Another Graph Library | 2024-12-08T23:12:44 |
YAML-Ordered-Conditional | LNATION | A conditional language within an ordered YAML struct | 2024-04-06T06:05:51 |
Zleep | LNATION | zleep | 2024-09-07T06:11:28 |
_Acme-Local | CONTRA | The great new _Acme::Local! | 2024-11-19T07:35:37 |
blib-tiny | PLICEASE | Like blib but lighter | 2024-12-22T23:29:29 |
e | TIMKA | The great new e! | 2024-05-08T15:09:45 |
eugener-test | EUGENER | A test package. DOnt install. | 2024-11-20T12:58:19 |
kraken | PHILIPPE | api.kraken.com connector | 2024-04-05T09:11:35 |
kura | KFLY | Store constraints for Data::Checks, Type::Tiny, Moose and more. | 2024-08-18T13:31:06 |
lib-root | HERNAN | find perl root and push lib modules path to @INC | 2024-03-30T17:27:02 |
mojo-util-benchmark | CRLCU | A set of utilities for working with collections of data. | 2024-02-07T13:49:03 |
optional | EXODIST | Pragma to optionally load a module (or pick from a list of modules) and provide a constant and some tools for taking action depending on if it loaded or not. | 2024-05-14T21:33:17 |
papersway | SPWHITTON | PaperWM-like window management for Sway/i3wm | 2024-04-12T07:52:39 |
perl-libssh | QGARNIER | Support for the SSH protocol via libssh. | 2024-05-28T08:35:27 |
perlwhich | MMCCLENN | locate a Perl module | 2024-01-03T04:29:55 |
this_mod | PERLANCAR | Load "this module" | 2024-01-06T07:55:12 |
Number of new CPAN distributions this period: 897
Number of authors releasing new CPAN distributions this period: 195
Authors by number of new CPAN distributions this period:
No | Author | Distributions |
---|---|---|
1 | PERLANCAR | 206 |
2 | JGNI | 100 |
3 | SKIM | 36 |
4 | LNATION | 26 |
5 | PLICEASE | 20 |
6 | LEONT | 20 |
7 | CHRISARG | 17 |
8 | ETJ | 16 |
9 | TYRRMINAL | 15 |
10 | JANESKIL | 11 |
11 | BRTASTIC | 10 |
12 | DJERIUS | 10 |
13 | CONTRA | 10 |
14 | PEVANS | 9 |
15 | SANKO | 9 |
16 | GDT | 8 |
17 | OODLER | 8 |
18 | MATHIAS | 8 |
19 | DRCLAW | 7 |
20 | HANJE | 7 |
21 | CHENGYU | 7 |
22 | GENE | 7 |
23 | TULAMILI | 6 |
24 | CARELINE | 6 |
25 | UTASHIRO | 6 |
26 | JDEGUEST | 6 |
27 | ABALAMA | 6 |
28 | GBROWN | 6 |
29 | DCONWAY | 5 |
30 | EHUELS | 5 |
31 | KIMOTO | 5 |
32 | MJOHNSON | 5 |
33 | RAWLEYFOW | 5 |
34 | EUGENER | 5 |
35 | HAX | 5 |
36 | LION | 4 |
37 | WATERKIP | 4 |
38 | JJNAPIORK | 4 |
39 | DAVECROSS | 4 |
40 | CORION | 4 |
41 | JMATES | 4 |
42 | TEODESIAN | 4 |
43 | HAARG | 4 |
44 | RRWO | 4 |
45 | BLIAKO | 4 |
46 | SAMYOUNG | 4 |
47 | JBARRETT | 3 |
48 | ARTHAS | 3 |
49 | ARISTOTLE | 3 |
50 | CEEJAY | 3 |
51 | NJFRANCK | 3 |
52 | EXODIST | 3 |
53 | SVW | 3 |
54 | POLETTIX | 3 |
55 | RJBS | 3 |
56 | DERIV | 3 |
57 | OLOOEEZ | 3 |
58 | BPS | 3 |
59 | NHORNE | 3 |
60 | STIGTSP | 3 |
61 | VVELOX | 3 |
62 | PERIGRIN | 3 |
63 | SHINGO | 3 |
64 | GHE | 3 |
65 | SHE | 3 |
66 | KARJALA | 2 |
67 | TIMKA | 2 |
68 | DILLANBH | 2 |
69 | SCHROEDER | 2 |
70 | SSMN | 2 |
71 | NHUBBARD | 2 |
72 | EXTISM | 2 |
73 | WDAEMS | 2 |
74 | KFLY | 2 |
75 | CDRAKE | 2 |
76 | MARKOV | 2 |
77 | GETTY | 2 |
78 | BIGPRESH | 2 |
79 | SMONFF | 2 |
80 | CADE | 2 |
81 | MMCCLENN | 2 |
82 | YOSHIMASA | 2 |
83 | ZMUGHAL | 2 |
84 | DKECHAG | 2 |
85 | MANWAR | 2 |
86 | SPWHITTON | 2 |
87 | TEAM | 2 |
88 | BAKERSCOT | 2 |
89 | BOD | 2 |
90 | VKON | 2 |
91 | AALLGOOD | 2 |
92 | BIGFOOT | 2 |
93 | NOBUNAGA | 2 |
94 | GWS | 1 |
95 | DCANTRELL | 1 |
96 | NICEPERL | 1 |
97 | HAIJENP | 1 |
98 | DAKKAR | 1 |
99 | JKEENAN | 1 |
100 | HAKONH | 1 |
101 | SGRAY | 1 |
102 | BDFOY | 1 |
103 | ARFREITAS | 1 |
104 | ATOY | 1 |
105 | CPANIC | 1 |
106 | AJNN | 1 |
107 | DOMM | 1 |
108 | TESSARIN | 1 |
109 | CAVAC | 1 |
110 | DYLIBSO | 1 |
111 | HOEKIT | 1 |
112 | LHRST | 1 |
113 | TOBYINK | 1 |
114 | SIDNEY | 1 |
115 | CRLCU | 1 |
116 | PHILIPPE | 1 |
117 | DAVIDRAAB | 1 |
118 | PTC | 1 |
119 | JANW | 1 |
120 | MAUKE | 1 |
121 | YUPEN | 1 |
122 | DOMERO | 1 |
123 | SOMMREY | 1 |
124 | RES | 1 |
125 | OLEG | 1 |
126 | EPISODEIV | 1 |
127 | NERDVANA | 1 |
128 | ATRODO | 1 |
129 | OLIVER | 1 |
130 | JOSERIJO | 1 |
131 | SHLOMIF | 1 |
132 | CLEACH | 1 |
133 | ABECKER | 1 |
134 | RADIUSCZ | 1 |
135 | NAUTOFON | 1 |
136 | SKAJI | 1 |
137 | HORSHACK | 1 |
138 | ZARABOZO | 1 |
139 | MBARBON | 1 |
140 | HIGHTOWE | 1 |
141 | RLOVELAND | 1 |
142 | HKOBA | 1 |
143 | GREGK | 1 |
144 | JOJESSF | 1 |
145 | TIMLEGGE | 1 |
146 | JOHNMERTZ | 1 |
147 | LANX | 1 |
148 | CHRISC | 1 |
149 | MBALLARIN | 1 |
150 | VASYAN | 1 |
151 | NNZ | 1 |
152 | ANTONOV | 1 |
153 | RSPIER | 1 |
154 | MYSOCIETY | 1 |
155 | TAKEMIO | 1 |
156 | JFORGET | 1 |
157 | SISYPHUS | 1 |
158 | PETDANCE | 1 |
159 | TONYC | 1 |
160 | JAMTWOIN | 1 |
161 | SIMONW | 1 |
162 | JIMAVERA | 1 |
163 | GEEKRUTH | 1 |
164 | BRICKPOOL | 1 |
165 | PDURDEN | 1 |
166 | JV | 1 |
167 | MRDVT | 1 |
168 | QGARNIER | 1 |
169 | BOOK | 1 |
170 | BARBARITO | 1 |
171 | DAMI | 1 |
172 | HESCO | 1 |
173 | HAPPYBEAR | 1 |
174 | CRABAPP | 1 |
175 | SFOBERSKI | 1 |
176 | HAUKEX | 1 |
177 | LITCHIE | 1 |
178 | GUL | 1 |
179 | TRIZEN | 1 |
180 | YTURTLE | 1 |
181 | BPSCHUCK | 1 |
182 | NOTHANS | 1 |
183 | AMIRCANDY | 1 |
184 | HERNAN | 1 |
185 | EGOR | 1 |
186 | BCDE | 1 |
187 | DDROWN | 1 |
188 | CATOUC | 1 |
189 | GAISSMAI | 1 |
190 | VNEALV | 1 |
191 | AAHAZRED | 1 |
192 | MSLM | 1 |
193 | SMSAERO | 1 |
194 | XMOLEX | 1 |
195 | UXYZAB | 1 |
Published by perlancar on Monday 06 January 2025 01:29
dist | author | abstract | date |
---|---|---|---|
Ac_me-Local | CONTRA | The great new Ac_me::Local! | 2024-12-06T13:35:37 |
Acme-Version-Same | CONTRA | Module for testing CPAN Pause indexing | 2024-12-19T08:38:15 |
Acme | STIGTSP | foo() returns "Foo" | 2024-12-05T16:35:21 |
App-Changelog | OLOOEEZ | Simple command-line CHANGELOG.md generator written in Perl | 2024-12-01T14:35:19 |
App-Standup-Diary | SMONFF | Manage a simple Markdown journal for your daily standups | 2024-12-01T17:05:39 |
App-Tickit-Hello | SKIM | Tickit application with hello world. | 2024-12-18T19:17:34 |
App-datasection | PLICEASE | Work with __DATA__ section files from the command line | 2024-12-22T14:02:22 |
App-repeat | PERLANCAR | Repeat a command a number of times | 2024-12-06T06:58:51 |
Bencher-ScenarioBundle-Ref-Util | PERLANCAR | Benchmark Ref::Util | 2024-12-04T03:38:56 |
Bluesky | SANKO | The Bluesky Social Network | 2024-12-03T01:56:20 |
CPAN-Namespace-Check-Visibility | CONTRA | Check if a namespace exists on public CPAN | 2024-12-18T12:39:35 |
Circle-Miner | CHENGYU | The miner module for circle chain sdk. | 2024-12-04T13:38:23 |
CommonsLang | YUPEN | Commonly used functions for Perl language | 2024-12-25T02:31:45 |
Comparer-file_size | PERLANCAR | Compare file's size | 2024-12-01T00:05:53 |
Config-Proxy | SGRAY | Loader class for HTTP proxy configuration parsers. | 2024-12-10T12:47:30 |
Crypt-PQClean-Sign | GUL | Post-Quantum Cryptography with keypair | 2024-12-23T12:37:49 |
Crypt-URandom-Password | STIGTSP | Generate passwords from cryptographically secure pseudorandom bytes | 2024-12-27T22:59:54 |
Crypt-URandom-Token | STIGTSP | Generate secure strings for passwords, secrets and similar | 2024-12-28T20:03:58 |
Daje-Plugin-GeneratePerl | JANESKIL | It's new $module | 2024-12-13T14:31:42 |
Daje-Plugin-GenerateSQL | JANESKIL | It's new $module | 2024-12-12T07:35:48 |
Daje-Plugin-GenerateSchema | JANESKIL | It's new $module | 2024-12-12T16:54:45 |
Daje-Tools-DataSections | JANESKIL | It's new $module | 2024-12-05T16:13:18 |
Daje-Tools-Datasections | JANESKIL | Load and store data sections in memory from a named *.pm | 2024-12-07T14:41:02 |
Daje-Tools-Filechanged | JANESKIL | It's new $module | 2024-12-08T10:11:41 |
Daje-Workflow-Database | JANESKIL | It's new $module | 2024-12-24T15:22:30 |
Daje-Workflow-Database-Model | JANESKIL | It's new $module | 2024-12-29T07:48:59 |
Daje-Workflow-Loader | JANESKIL | It's new $module | 2024-12-22T13:51:40 |
Data-Section-Pluggable | PLICEASE | Read structured data from __DATA__ | 2024-12-05T05:27:18 |
Data-Section-Pluggable-Plugin-Yaml | PLICEASE | Data::Section::Pluggable Plugin for YAML | 2024-12-05T12:02:28 |
Data-Section-Writer | PLICEASE | Write __DATA__ section files for Data::Section, Data::Section::Simple or Mojo::Loader::data_section | 2024-12-04T00:15:50 |
Drought-PET-Thornthwaite | AALLGOOD | Calculate potential evapotranspiration (PET) using the Thornthwaite method | 2024-12-12T21:22:38 |
FFI-Platypus-Lang-V | PLICEASE | Documentation and tools for using Platypus with the V programming language | 2024-12-19T01:16:18 |
File-FindUniq | PERLANCAR | Find unique or duplicate file {contents,names} | 2024-12-05T07:59:52 |
Game-Snake | LNATION | A clone of the classic snake game using raylib | 2024-12-29T20:33:35 |
JSON-LD | PLICEASE | Load and dump JSON files | 2024-12-07T15:40:25 |
Map-Tube-Hamburg | GWS | Interface to the Hamburg U- and S-Bahn maps including AKN | 2024-12-09T01:59:08 |
Math-Symbolic-Custom-CollectSimplify | MJOHNSON | Simplify Math::Symbolic expressions using Math::Symbolic::Custom::Collect | 2024-12-13T10:30:30 |
Math-Symbolic-Custom-Matrix | MJOHNSON | Matrix routines for Math::Symbolic | 2024-12-08T10:54:28 |
Mojo-UserAgent-Role-Retry | SSMN | Retry requests on failure | 2024-12-29T07:39:35 |
MojoUserAgentRoleRetry | SSMN | Retry requests on failure | 2024-12-29T00:59:46 |
Neo4j-Driver-Plugin-LWP | AJNN | Neo4j::Driver plug-in for libwww-perl | 2024-12-11T07:09:57 |
PDL-Fit | ETJ | Levenberg-Marquardt fitting routine for PDL | 2024-12-09T12:50:50 |
PDL-GSL | ETJ | parameter estimations and probability density functions for distributions. | 2024-12-09T06:08:00 |
PDL-IO-Dicom | ETJ | a module for reading DICOM images. | 2024-12-04T02:35:06 |
PDL-IO-ENVI | ETJ | read ENVI data files into PDL | 2024-12-04T04:14:07 |
PDL-IO-GD | ETJ | Interface to the GD image library. | 2024-12-03T05:35:52 |
PDL-IO-HDF | ETJ | A PDL interface to the HDF4 library. | 2024-12-03T04:08:00 |
PDL-IO-IDL | ETJ | I/O of IDL Save Files | 2024-12-04T02:48:09 |
PDL-Opt-Simplex | ETJ | Simplex optimization routines | 2024-12-09T13:46:51 |
PDL-Transform-Proj4 | ETJ | PDL::Transform interface to the Proj4 projection library | 2024-12-03T08:19:30 |
Perl-Critic-Policy-Plicease-ProhibitArrayAssignAref | PLICEASE | Don't assign an anonymous arrayref to an array | 2024-12-28T21:26:23 |
Plack-Middleware-Validate_Google_IAP_JWT | HKOBA | Validate JWT from Google IAP | 2024-12-18T05:31:51 |
Poz | YTURTLE | A simple, composable, and extensible data validation library for Perl. | 2024-12-08T07:09:15 |
SmsAero | SMSAERO | Send SMS via smsaero.ru | 2024-12-09T11:55:19 |
Software-Security-Policy | TIMLEGGE | Create a Security Policy for your distribution | 2024-12-16T03:23:46 |
SortKey-Num-file_num_links | PERLANCAR | File number of (hard) links as sort key | 2024-12-08T00:05:11 |
SortKey-Num-file_size | PERLANCAR | File size as sort key | 2024-12-15T00:05:21 |
Sorter-file_by_num_links | PERLANCAR | Sort files by number of (hard) links | 2024-12-22T00:06:07 |
Sorter-file_by_size | PERLANCAR | Sort files by size | 2024-12-29T00:05:07 |
Sq | DAVIDRAAB | The Sq Language | 2024-12-25T05:30:48 |
Sumu-Windows-Upm4win | CEEJAY | User Pass Manager For Windows | 2024-12-13T16:44:11 |
Sys-GetRandom-FFI | RRWO | get random bytes from the system | 2024-12-27T00:55:22 |
Task-Map-Tube | MANWAR | Bundles Map::Tube::* map packages. | 2024-12-12T11:04:59 |
Task-Map-Tube-Bundle | MANWAR | Bundles Map::Tube::* map packages. | 2024-12-12T11:33:02 |
Task-MemManager-CMalloc | CHRISARG | Allocates buffers using C's malloc | 2024-12-02T03:33:50 |
Temperature-Calculate-DegreeDays | AALLGOOD | Perl package to compute cooling, heating, and growing degree days | 2024-12-03T20:23:00 |
Test-SpellCheck | PLICEASE | Check spelling of POD and other documents | 2024-12-29T15:22:23 |
Test-SpellCheck-Plugin-Lang-EN-US | PLICEASE | US English language dictionary for Test::SpellCheck | 2024-12-29T15:25:31 |
Text-Table-Boxed | JIMAVERA | Automate separators and rules for Text::Table | 2024-12-12T01:46:12 |
Tie-Hash-DataSection | PLICEASE | Access __DATA__ section via tied hash | 2024-12-24T05:56:04 |
Time-RTM | CADE | Run-time metrics stats | 2024-12-01T23:49:06 |
UserPassManagerForWindows | CEEJAY | User Pass Manager For Windows | 2024-12-13T15:39:41 |
WebService-Chroma | LNATION | chromadb client | 2024-12-16T20:45:59 |
Webservice-Sendy-API | OODLER | Sendy's integration API Perl client and commandline utility | 2024-12-29T21:28:13 |
YAGL | RLOVELAND | Yet Another Graph Library | 2024-12-08T23:12:44 |
blib-tiny | PLICEASE | Like blib but lighter | 2024-12-22T23:29:29 |
Number of new CPAN distributions this period: 76
Number of authors releasing new CPAN distributions this period: 33
Authors by number of new CPAN distributions this period:
No | Author | Distributions |
---|---|---|
1 | PLICEASE | 11 |
2 | JANESKIL | 9 |
3 | ETJ | 9 |
4 | PERLANCAR | 8 |
5 | STIGTSP | 3 |
6 | CONTRA | 3 |
7 | AALLGOOD | 2 |
8 | SSMN | 2 |
9 | LNATION | 2 |
10 | MJOHNSON | 2 |
11 | MANWAR | 2 |
12 | CEEJAY | 2 |
13 | CHENGYU | 1 |
14 | GUL | 1 |
15 | OODLER | 1 |
16 | GWS | 1 |
17 | CADE | 1 |
18 | TIMLEGGE | 1 |
19 | DAVIDRAAB | 1 |
20 | YTURTLE | 1 |
21 | JIMAVERA | 1 |
22 | HKOBA | 1 |
23 | YUPEN | 1 |
24 | RRWO | 1 |
25 | CHRISARG | 1 |
26 | SGRAY | 1 |
27 | OLOOEEZ | 1 |
28 | SANKO | 1 |
29 | SMONFF | 1 |
30 | SMSAERO | 1 |
31 | RLOVELAND | 1 |
32 | SKIM | 1 |
33 | AJNN | 1 |
Published by Robert Rothenberg on Sunday 05 January 2025 22:31
Adding a SECURITY
or SECURITY.md
file to your Perl distributions will let people know:
The contact point is very important for modules that have been around for a long time and have had several authors over the years. When there is a long list of maintainers, it's not clear who to contact.
You don't want people reporting security vulnerabilities in public on the RT or GitHub issues for your project, nor do you want a post on IRC, Reddit or social media about it.
If your software is on GitHub, you can set up private vulnerability reporting. GitLab has a similar system.
Otherwise, a single email address is acceptable. An alias that forwards to all of the maintainers or at the very least, a single maintainer who has agreed to that role will work.
It's also important to realise as a maintainer that you are not on your own when you receive a vulnerability report. You are welcome and even encouraged to reach out to CPANSec for assistance triaging and fixing the issue, as well as handling notifications and reporting.
The supported software version may seem obvious, but it's important to spell out: will you be updating only the latest version? What versions of Perl will you support? If your module uses or embeds other libraries, how will they be supported?
Fortunately it is not difficult to write a security policy. The CPAN Security Group (CPANSec) has written Guidelines for Adding a Security Policy to Perl Distributions. (Note: I am one of the authors of that document.)
The guidelines include a template for a Perl distribution with a single author that you can use as a basis for your distributions. There is also Software::Security::Policy that will be integrated with build tools in the future to help generate one.
We'd like more people to use the document and provide feedback to CPANSec to improve the advice, and perhaps add alternative templates.
You don't even have to release a new version of your modules immediately. Just adding a SECURITY.md
file in the root of the GitHub repo will be helpful enough.
Note: edited for typos.
Published by Robert Rothenberg on Sunday 05 January 2025 17:15
Adding a SECURITY
or SECURITY.md
file to your Perl distributions will let people know:
The contact point is very important for modules that have been around for a long time and have had several authors over the years. When there is a long list of maintainers, it’s not clear who to contact.
You don’t want people reporting security vulnerabilities in public on the RT or GitHub issues for your project, nor do you want a post on IRC, Reddit or social media about it.
If your software is on GitHub, you can set up private vulnerability reporting. GitLab has a similar system.
Otherwise, a single email address is acceptable. An alias that forwards to all of the maintainers or at the very least, a single maintainer who has agreed to that role will work.
It’s also important to realise as a maintainer that you are not on your own when you receive a vulnerability report. You are welcome and even encouraged to reach out to CPANSec for assistance triaging and fixing the issue, as well as handling notifications and reporting.
The supported software version may seem obvious, but it’s important to spell out: will you be updating only the latest version? What versions of Perl will you support? If your module uses or embeds other libraries, how will they be supported?
Fortunately it is not difficult to write a security policy. The CPAN Security Group (CPANSec) has written Guidelines for Adding a Security Policy to Perl Distributions. (Note: I am one of the authors of that document.)
The guidelines include a template for a Perl distribution with a single author that you can use as a basis for your distributions. There is also Software::Security::Policy that will be integrated with build tools in the future to help generate one.
We’d like more people to use the document and provide feedback to CPANSec to improve the advice, and perhaps add alternative templates.
You don’t even have to release a new version of your modules immediately.
Just adding a SECURITY.md
file in the root of the GitHub repo will be helpful enough.
This was originally posted on blogs.perl.org.
Published by Unknown on Saturday 04 January 2025 22:56
Published by Unknown on Saturday 04 January 2025 22:52
This is the weekly favourites list of CPAN distributions. Votes count: 43
Week's winner: Mojolicious (+2)
Build date: 2025/01/04 21:51:46 GMT
Clicked for first time:
Increasing its reputation:
Published by Dave Cross on Saturday 04 January 2025 11:19
The London Perl Mongers have had a website for a very long time. Since some time in 1998, I think. At first, I hosted a static site for us. Later on, we bought our own server and hosted it at a friendly company around Silicon Roundabout. But for most of the lifetime of the organisation, it’s been hosted on a server donated to us by Exonetric (for which we are extremely grateful).
But all good things come to an end. And last week, we got an email saying the Exonetric was closing down and we would need to find alternative hosting by the end of February.
The code for the site is on GitHub, so I had a quick look at it to see if there was anything easy we could do.
I was slightly surprised to find it was a PSGI application. Albeit a really simple PSGI application that basically served content from a /root directory, having passed it through some light Template Toolkit processing first. Converting this to a simple static site that could be hosted on GitHub Pages was going to be simple.
Really, all it needed was a ttree configuration file that reads all of the files from /root, processes them and writes the output to /docs. The configuration file I created looked like this:
src = root dest = docs copy = \.(gif|png|jpg|pdf|css|js)$ copy = ^CNAME$ recurse verbose
To be honest, most of the static web site work I do these days uses a static site builder that’s rather more complex than that, so it was really refreshing to remind myself that you can do useful things with tools as simple as ttree.
The next step was to add a GitHub Actions workflow that publishes the site to the GitHub Pages server each time something changes. That’s all pretty standard stuff too:
name: Generate web page on: push: branches: 'master' workflow_dispatch: jobs: build: if: github.repository_owner == 'LondonPM' runs-on: ubuntu-latest steps: - name: Install TT run: | sudo apt-get update sudo apt-get -y install libtemplate-perl - name: Checkout uses: actions/checkout@v4 - name: Create pages run: ttree -f ttreerc 2>&1 > ttree.log - name: Archive ttree logs uses: actions/upload-artifact@v4 with: name: ttree.log path: ./ttree.log retention-days: 3 - name: Update pages artifact uses: actions/upload-pages-artifact@v3 with: path: docs/ deploy: needs: build if: github.repository_owner == 'LondonPM' permissions: pages: write id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4
The only slightly complex lines here are the two lines that say if: github.repository_owner == 'LondonPM'. We’re hoping that other people will fork this repo in order to work on the site, but it’s only the main fork that should attempt to publish the current version on the GitHub Pages servers.
There was a bit of fiddling with DNS. Temporarily, we used the domain londonperl.com as a test deployment (because I’m the kind of person who just happens to have potentially useful domains lying around, unused!) but enough of us are slightly obsessed about using the correct TLD so we’ve settled on londonperl.org[*]. We’ve asked the nice people at the Perl NOC to redirect our old domain to the new one.
And it’s all working (well, with the exception of the redirection of the old domain). Thanks to Sue, Lee and Leo for the work they’ve done in the last few days to get it all working. And a big thanks to Mark and Exonetric for hosting the site for us for the last couple of decades.
These changes are already having the desired effect. People are submitting pull requests to update the website. Our website is probably more up-to-date than it has been for far too long. It’s even responsive now.
I realise there has been very little Perl in this post. But I thought it might be useful for other Perl Mongers groups who are looking for a simple (and free!) space to host their websites. Please let me know if you have any questions about the process.
[*] We wanted to use Cloudflare to manage the domain but their free service only supports top-level domains and london.pm.org (our original domain) is a subdomain – and none of us wanted to pay for the enterprise version.
The post London Perl Mongers on GitHub Pages appeared first on Perl Hacks.
Published by Robert Rothenberg on Friday 03 January 2025 15:30
Any secret token that allows someone to access a resource or perform an action should be generated with a secure random number generator. Perl’s built-in rand function is not suitable for this: it is seeded by only 32-bits (4 bytes), and the output can be predicted easily.
There are many modules on CPAN with random number generators. Which ones should you use?
We have added a CPAN Author’s Guide to Random Data for Security. This recommends a few modules that are generaly portable, easy to use and have good defaults.
Published by Perl Steering Council on Thursday 02 January 2025 23:59
Just Aristotle and Graham for our first meeting of the new year. Not much progress since the last one due to Christmas, New Year’s, sickness, and other personal circumstances. We discussed our framing of the version bump, the timeline for a decision, and the fact that constraints push us toward a dummy .0 third version component as the simplest way forward.
Published by Mayur Koshti on Thursday 02 January 2025 13:36
Published by Robert Rothenberg on Tuesday 31 December 2024 11:00
Some end of year reminders for CPAN Authors:
Do all of your modules have up-to-date contact information? If not, please release new versions with an updated email address in the AUTHOR section.
(And while you’re at it, why not add a security policy to your distribution, so that people know how and where to report a security issue with your module.)
If you have a cpan.org email alias, does it forward to the correct email address? (You can edit your email settings in PAUSE.)
And most importantly, if you are taking time away from maintaining Perl modules, please add ADOPTME or NEEDHELP as co-maintainers to mark your modules as available.
Thanks, and best wishes for 2025.
This was originally posted on blogs.perl.org.
Published by Amber Krawczyk on Sunday 29 December 2024 13:47
The first deadline for the 2025 TPRC papercall is on January 15th! Get those papers in! Go to http://tprc.us for more information!
Published by Kavya on Saturday 28 December 2024 16:27
In corporate IT environments, Perl scripts and PARMS (parameters) play crucial roles in batch processing workflows. They are widely used…
Published on Wednesday 25 December 2024 00:00
The James Webb Space Telescope hangs like a Christmas tree ornament over Africa as it prepares to deploy its mirror and travel to its distant orbit. Image credit: NASA/ESA 25/Dec/2021.
I am an astronomer who has for 30 years used PDL for processing of astronomical data. (In fact this is one of the reasons I created it :-) see 'The Beginnings of PDL').
Welcome to “What’s new on CPAN”, a curated look at last month’s new CPAN uploads for your reading and programming pleasure. Enjoy!
Published by Mark Fowler on Tuesday 24 December 2024 00:00
Merry Christmas, one and all.
A quarter of a century (and, obviously, twenty five days) ago I created the very first Perl Advent Calendar entry. This Christmas, I'd like to take a minute to talk about all the wonderful presents this has given us over the years.
Over this time the world has changed, and Perl's role in the world with it. Twenty five years ago was the dawn of the dot-com boom, where we were concerned with the battle between Perl and Python, and the new upstart of Java. Which would be the dominant language? Twenty five years later we can see the fallacy of this debate - all of the languages won. We live in a vastly more complicated world where the Internet is now always on, in our pockets and on our wrists, controlling everything from our light switches to how we interact with our governments. And powering this is a plurality of programming languages, each with its own advantages and disadvantages, quirks and foibles, allowing people to use the best language for the task at hand - Java, or Python, or our beloved Perl - or any number of interesting and exciting language brethren new and old.
With the development of the cloud and cloud technologies the old battle for the server is immaterial. Where we once used to fret over if Perl or Python was installed on "the server" and available for us (and which version!), we now have the ability to easily obtain whatever environment we want to use whichever language we want. Where we used to have to hand install software on the operating system this was supplanted by package systems, then by fleet management systems like ansible and puppet that could reproducibly install and manage any language we wanted across a whole fleet of machines, and then by virtual machine technologies like Amazon's AMI system that can be used to quickly create a "throwaway" machine with whatever machine image we want installed on it.
We now live in the Docker (container) world where each script can have its own version of Perl, with exactly the dependencies it needs installed just for it.
This changing environment has influenced Perl, allowing it to flourish in a way that we never would have imagined twenty five years ago. By making installing software easier we freed ourselves from the concept of the "core" Perl language extending what we could use out into reliably having whatever bits of CPAN we want at hand. This brought the philosophy of "CPAN is the language" to the forefront.
Much of the work on Perl in the last twenty five years has been adapting the core interpreter so that it's possible to modify it more and more by simply installing a module. Where we once begrudgingly relied on source code modifications to blindly manipulate the raw text of the code in order to add brand new language features we now exist in a world of pluggable syntax where such changes can be made in collaboration with perl's own parser giving us (almost) infinite adaptability.
The history of Perl has moved from higher level abstractions being built in large swaths of Perl code to code being made available on the CPAN that can integrate ever closer with the interpreter itself. Now finally in the last few years we've seen some of these experiments being turned into best practices that are built into the language itself.
When I first learned Perl in 1998, I started with the "pink" version of Larry Wall's "Programming Perl" book, which only covered Perl 4 and hence didn't cover the topic of objects at all. This resulted in some of the first Perl 5 code I wrote looking like this:
sub triangle_area {
my $tri = shift;
return $tri->{width} * $tri->{height} / 2;
}
my $shape = { width => 3, height => 4 };
print triangle_area($shape), "\n";
My understanding of Perl changed pretty quickly when I started reading the man pages for the version of Perl I had installed and got my hands on the "blue" second edition of "Programming Perl".
Learning Perl 5's object model meant the kind of Perl I would write would be more like this:
use strict;
use warnings;
package Triangle;
sub new {
my $class = shift;
my %args = @_;
my $self = { width => $args{width}, height => $args{height} };
return bless $self, $class;
}
sub width { return $self->{width} }
sub height { return $self->{height} }
sub area {
my $self = shift;
return $self->width * $self->height / 2;
}
package main;
my $shape = Triangle->new(width => 3, height => 4);
print $shape->area(), "\n";
This was so much better. We did't have to name the area
method with a triangle_
prefix in order to disambiguate it from the any rectangle_area
or circle_area
function we might have in our code base.
Then in 2006 along came Moose which enabled a much more powerful way to write objects:
package Triangle;
use Moose;
has width => (is => 'ro');
has height => (is => 'ro');
sub area {
my $self = shift;
return $self->width * $self->height / 2;
}
package main;
use strict;
use warnings;
my $shape = Triangle->new(width => 3, height => 4);
print $shape->area(), "\n";
Later, in 2017, we had the first release of Mu which allows us to write something quite a bit shorter:
package Triangle;
use Mu;
ro 'width';
ro 'height';
sub area {
my $self = shift;
return $self->width * $self->height / 2;
}
package main;
use strict;
use warnings;
my $shape = Triangle->new(width => 3, height => 4);
print $shape->area(), "\n";
This is not only shorter, but it also has more error checking - if you don't pass both width
and height
to new
, you'll get an error.
Last year we had the release of Perl 5.38. This brought into core a bunch of syntax (admirably still experimental) that we started as a community playing with in 2008 with MooseX::Declare - but implemented much better and safer. The code above can now be simplified even further:
use v5.38;
use feature 'class';
class Triangle {
field $width :param;
field $height :param;
method area { return $width * $height / 2 }
}
my $shape = Triangle->new(width => 3, height => 4);
say $shape->area();
Perl has changed throughout the years, but it's always been Perl, with core ideas that have defined not only the "Perl way" of doing things for Perl, but elsewhere too!
These ideas have infected the way we write code, not just in Perl, but in all modern programming languages. Amazing things that sprung up in Perl have become table stakes in other languages. The idea of having a central repository such as the CPAN that is the defacto place where you can find open source libraries that can be easily installed have become the norm - just look at JavaScript's ever popular npm, Python's PyPI, and Ruby's RubyGems as examples. The concept of shipping these libraries with working tests like everything on the CPAN has been adopted everywhere, with CI/CD systems validating any serious software dependency on any commit.
These aspects of Perl - along with countless other things - means that twenty five years on not only can I still enjoy writing Perl code in more powerful and fun ways than I ever could, but when I need to use another language better suited to a particular niche, a lot of the things I loved about Perl twenty five years ago are already there waiting for me.
Speaking of things that other languages have adopted...
The story of the Perl Advent Calendar starts, like all good stories, with a bad idea in the pub the night before. I don't eat chocolate, so I was lamenting at a Perl Mongers social meeting that there were no good advent calendars for me - just the ones with a little picture behind the windows. I was saying that you should be able to get some other kind of reward. And then I thought how about some cool code?
So the Perl Advent Calendar was born. Or it was the very next day, when I put the whole thing together during my lunch break. Back then I just named a module for the day and included the module's POD as the advent calendar - next year I introduced a small description of why I'd picked it along with the POD, and the year after is when the full article format was introduced with links to search.cpan.org (and later metacpan) for the documentation.
This turned out to be surprisingly popular, with the website being featured in NTK and on Slashdot (getting mentioned by Slashdot was a big deal in 1999).
It wasn't long before there were imitators for other programming languages. It became a tradition in the software world to have an Advent Calendar for your language of choice (or area of programming language - Perl has seen quite a few separate calendars for things like Perl 6, Catalyst, and even futures and promises). We now even have the Advent of Code which is a small advent calendar of programming puzzles that's hugely popular (I, for my part, have never had time to do it since I was always spending time tweaking the Perl Advent Calendar at this time of year).
Through the years the Advent Calendar became too much for me to manage on my own. I could no longer keep writing all twenty five articles myself, and so I started taking submissions. I eventually gave the calendar away for others to maintain, took it back again years later, and then finally gave it away again. Like any good project, it's totally grown beyond me and has a life of its own.
The crucial thing about the calendar - more important than any one article, any one year, or even any one programming language - is the idea that we can all enjoy learning about our favorite programming language. Things don't have to be taught in boring instructions, but instead learning about something can be silly, daft, fun, and a treat! Why not have a story about how Santa's elves are all Perl programmers faced with saving Christmas, and the only way to do it is to use this module? Why not have a tale about how we can control Christmas lights with Perl? Create Christmas memes? Many a clever person in the Perl community has talked about optimizing projects for fun - attracting volunteers by making projects enjoyable to be a part of - and the Perl Advent Calendar embraces this idea of rewarding everyone that reads it with joy. This popular format has been copied by other languages.
So, as I try to do every year, and on this twenty-fifth year of the Perl Advent Calendar, I wish you a Merry Christmas.
Perl has this concept of "blessing" references to turn them into objects. One way to look at it is it's Perl's way of making something more than what it is - improving it, giving it powers and abilities it didn't have before.
So when I say "Bless us, everyone" I'd like you to think about how Perl has "bless"ed everyone. Each programming language that has their own version of CPAN. Each programming language that has their own Advent Calendar. Everyone who's written something daft just for the heck of it, and then shared it so people can learn. These are all things that Perl has helped bring to the world, and means that - even if you've never used Perl - you're still a sort of Perl programmer. Because Perl is more than a language, a run-time - it's about the people and ideas behind it.
merry $christmas; bless $us, Everyone
Published on Tuesday 24 December 2024 00:00
Published by Olaf Alders on Monday 23 December 2024 00:00
For many years, the question of what is or is not Perl's logo has been quite murky. There is a fair amount of backstory and there is too much to dive into right here, but I will happily point you at Neil Bowers' tackling of the subject from 4 years ago: https://neilb.org/2020/12/04/perl-and-camels.html It's a quick, but informative read. I encourage you to take a peek.
The TL;DR of it all is that Neil suggested:
At the Perl Toolchain Summit in Lisbon this year, a small group of us had a chat about the logo (or non-logo) situation. The majority opinion was that someone should come up with a logo and present it for community use. Neil Bowers and I were nominated to take this on. Over the summer, Neil commissioned Zach Roszczewski to come up with a new camel. Babs Veloso, Neil and I gave some initial feedback. After we were happy that we could proceed, we widened the circle of participants to a small, shadowy cabal, chatting on TPRF Slack. Among these were Sébastien Feugère and Thibault Duponchelle, who had previous wrestled with the logo topic. Later, I participated in a call with the PSC, where they gave some feedback on the logo. When I was at the London Perl and Raku Workshop this past October, Sébastien, Thibault, Leo Lapworth and I got in a room together to discuss a path forward. We aimed to have a logo available for announcement as part of the Perl Advent Calendar, and here we are.
Over the past week, we have polled various others for opinions on the logo and various versions of it. The result of all of this is the logo you see at the top of this article.
Feedback on Sébastien Feugère's work was kindly given (in first name alphabetical order) by:
The license for this logo is CC-BY. That means that you are free to create derivative works based on the logo, with the only condition being attribution. We would love for this logo to be used widely, so we wanted to go with a permissive license.
Nobody. In the absence of a benevolent dictator or a governing body for all of the Perl communities, nobody has a mandate to create a logo for the Perl language and all of the communities attached to it. Our only hope is that this logo is seen as a gift and that if you like it, you will use it.
Traditionally the way that things work in our communities is that someone creates something in the hopes that it will be adopted by others. That may or may not happen, but the end result is entirely up to the various Perl communities. We hope that you like this logo. We hope that you use it. We are happy to work with you on using it, but we are not in charge of anything and it would be disingenuous to pretend it were otherwise.
This logo is not affiliated with The Perl and Raku Foundation in any way. The foundation has its own logo (the onion), which can also be used under certain conditions. As far as I can tell, there is no appetite within the foundation to wade into the Perl logo debate. Having said that, an onion is not a camel, and I refer you to Neil's blog post quoted above for an argument about why the logo really should just be a camel.
The logo currently lives in the metacpan/perl-assets repository. The repo is still a work in progress, but this is where you should be able to find static assets moving forward.
Please open an issue if there's something you'd like to discuss. Items worth discussing are:
Items not currently worth discussing:
Yes, we would like you to have many different options when it comes to using the camel. I'm including some of them below.
Neil Bowers was the initial owner of the new camel. He has transferred the ownership of the original camel artwork to me. My preference would be to transfer ownership to MetaCPAN, but it's not a legal entity, so I'm not entirely sure if that is possible. Since the license is CC-BY, I don't actually know how much ownership matters, but for the time being, I am the camel's steward.
We consider the logo to be production ready, but also a work in progress. We are in the process of approaching stakeholders and speaking to them about integrating the logo. In addition this, I expect the logo to be used in some way on both perl.com and metacpan.org.
There are still some assets which need to be settled -- like a favicon.ico
, but if all goes well that should be taken care of in the very near future.
It is my hope that the various Perl communities can standardize on this logo in the near term. This doesn't have to be the forever logo for Perl, but just generally getting people on the same page would be an excellent start.
If you think this logo is a good fit, please accept it as our gift to you. If it's not a good fit for you, that's ok too. TIMTOWTDI!
Published on Monday 23 December 2024 00:00
If you are generating truly random numbers for esoteric distributions, you need PDL!
Published by alh on Sunday 22 December 2024 09:34
Dave writes:
This is my monthly report on work done during November 2024 covered by my TPF perl core maintenance grant.
I spent most of last month continuing to do various bits of code refactoring and test-adding on Extutils::ParseXS, as a precursor to adding reference-counted stack (PERL_RC_STACK) abilities to XS.
Summary: * 5:09 process p5p mailbox * 39:10 refactor Extutils::ParseXS
Total: * 44:19 TOTAL (HH::MM)
Published by alh on Sunday 22 December 2024 09:30
Tony writes:
``` [Hours] [Activity] 2024/10/01 Tuesday 1.17 #22547 work on revert, test code, testing, make PR 22635 0.08 #22633 review and approve 0.22 #22634 review and approve 1.17 #22629 review and comments
2.84
2024/10/02 Wednesday 0.23 #22629 review and approve 0.15 github notifications 0.30 #22639 review and comment 0.33 #22638 review and comment 0.50 #22614 check build logs and close
2.33
2024/10/03 Thursday 1.27 #22641 review and comment 0.42 #22623 review code, try to work out a fix
2.77
2024/10/08 Tuesday 0.20 #22644 comment 0.22 #22627 comment on how to get pre-processed source 0.10 #22642 comments 0.22 #22643 review discussion, research 2.02 #22644 revert ‘ in names removal, remove deprecation
2.76
2024/10/09 Wednesday 0.28 email to list about scheduled goto into block removal 2.92 #22644 finish up removing deprecation, work on feature guarding 0.32 Paul’s XSUB: research, comment
4.95
2024/10/10 Thursday 0.23 #22649 review and approve with comment 1.47 #22644 more guarding, test updates
2.60
2024/10/14 Monday 2.15 #22644 feature specific tests 0.75 #22644 fix minor issues, testing, push for CI 1.78 #22647 review up to “ParseXS: refactor: centralise default
4.68
2024/10/15 Tuesday 0.10 github notifications 1.17 #22644 check CI results, recheck changes and polish 2.25 #22647 more review up to refactor add Node::Sig class
3.52
2024/10/16 Wednesday 0.60 github notifications 0.77 #22644 more polish, testing, push for CI 1.18 #22647 more review
4.93
2024/10/17 Thursday 0.58 #22609 follow-up comment 0.98 #22647 more review and approve
1.76
2024/10/21 Monday 1.65 #22541 follow-up review and comments 0.58 github notifications 0.47 #22653 research and comment 0.48 #22681 review and approve 0.67 #22683 review and comments
4.23
2024/10/22 Tuesday 0.22 #22530 comment
1.59
2024/10/23 Wednesday 0.70 #22683 research and comment 0.30 #22541 review updates and follow-up comment 1.35 cygwin address conflict #22695, open ticket, work on fix, testing, push for CI 0.38 #22588 testing, research and comment 0.12 #22695 check CI results, make PR 22696 0.10 #22690 review and approve 0.20 #22674 review and approve
3.95
2024/10/24 Thursday 0.97 #22696 adjust for comments, testing, force push 0.33 #22683 comment
1.53
2024/10/28 Monday 1.23 #22638 long comment 0.33 #22653 comment 0.62 #22530 research, comment
2.53
2024/10/29 Tuesday 1.37 #22638 review updates 0.32 #22638 more review 0.47 #22641 review, comment 0.65 #22641 more review, comment 0.73 #22710 try to work out cause 0.17 #22696 recheck and apply to blead
4.41
2024/10/30 Wednesday 1.57 #22541 review,comments 0.48 #22712 review, brief comment and approve
3.68
2024/10/31 Thursday 0.22 github notifications 0.30 #22696 checks, comment 1.70 #22638 review, research, comment 1.30 #15861 research and comment 0.55 #15861 follow-up
4.80
Which I calculate is 59.86 hours.
Approximately 33 tickets were reviewed or worked on, and 1 patches were applied. ```
December is not the only season for gifts. Every year, sometime around the end of May, the Perl 5 Porters gift us with a new version of Perl. Or is it perl
?
To quote "What's the difference between "perl" and "Perl"?" in perlfaq1:
"Perl" is the name of the language. Only the "P" is capitalized. The name of the interpreter (the program which runs the Perl script) is "perl" with a lowercase "p".
You may or may not choose to follow this usage. But never write "PERL", because perl is not an acronym.
The version of perl
(the interpreter) is what you get when you type perl -v
on the command-line:
$ perl -v
This is perl 5, version 40, subversion 0 (v5.40.0) built for x86_64-linux-gnu
Copyright 1987-2024, Larry Wall
Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.
Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl". If you have access to the
Internet, point your browser at https://www.perl.org/, the Perl Home Page.
You can also run this (the interpreter version is available in two different formats, either as a floating-point number, or a v-string):
$ perl -E 'say for $], $^V'
5.040000
v5.40.0
New versions of Perl come with bug fixes, speed improvements, deprecations and also new features. It's still the same old Perl, that will continue to run your existing code. Perl version upgrades are so simple and drama-less they're almost boring. (The Perl 5 Porters support the two most recent stable releases of Perl, which should be reason enough to upgrade your binary.)
Long story short, Perl is extremely backwards compatible. This means that sensibly-written code from 20 years ago should still run under the next release of Perl. In other words, any perl
interpreter should understand code written against older versions of the Perl language just fine. As I have personally experimented, this actually even applies to scripts targeting version 4 of the language!
With such a strong commitment to backwards compatibility, how does one even introduce new features to the language? To quote from "DESCRIPTION" in feature:
It is usually impossible to add new syntax to Perl without breaking some existing programs. This pragma provides a way to minimize that risk.
The feature module was introduced in Perl v5.10, to make it possible to introduce new features to the language without breaking existing scripts. A typical example would be the say
feature, which makes it possible to add the say
keyword to the language without breaking older scripts that might have defined a sub say
.
If one wanted to use the say
builtin, they would write:
use feature 'say';
say "Hello, world!";
And of course, code that already has a say
subroutine defined would continue to work the same, as long as the feature is not enabled. This leaves time for the code's author to look at their code and decide if they want to update it to take advantage of new features of the language.
Note that enabling features always happens in the current lexical scope.
Backwards-compatibility is preserved thanks to a compromise: people have to opt in to the new features. The unfortunate side-effect is that Perl will continue to look the same (sometimes giving the feeling it's stagnating) until you enable the new features!
It should be noted, however, that not all Perl features are guarded by the feature
module. Syntax constructs that were syntax errors in previous versions of Perl can be introduced without a guard, and many were. The Syntax::Construct module has an exhaustive list of "syntactic constructs that are not implemented via the feature
pragma, but are still not compatible with older versions of Perl".
Perl v5.40 knows about 25 features. While they can be enabled or disabled (some features are used to disable undesirable constructs, like indirect object notation) one by one, there is a better way than 25 lines of boilerplate.
use VERSION
A special case of the use
builtin is use VERSION
. It performs several operations:
The first thing that use VERSION
does is to declare which minimum version of perl
(the interpreter) you expect to run your code. If you demand a version later than that of the perl binary currently running your code, it's going to die at compile time.
$ perl -Mv5.38 -e1
Perl v5.38.0 required--this is only v5.36.0, stopped.
BEGIN failed--compilation aborted.
Although the version in use VERSION
can be written in either v-string style (v5.36
) or numeric style (5.036
), it is strongly recommended to use the former, as it's more readable and matches with the way people talk about Perl versions. (Unless the code is expected to actually be run on a perl
older than v5.8, which was released in 2002).
The feature
module also defines "feature bundles", which allows to load (or disable) multiple features together. (See "FEATURE-BUNDLES" in feature for their definitions.)
The special use VERSION
construct will implicitly load the corresponding feature bundle. For example:
use v5.10;
will implicitly load the corresponding feature bundle:
use feature ':5.10';
which will enable the say
, state
and switch
features. The parser will now understand the corresponding keywords, and behave accordingly. Error messages might differ a lot when a feature is enabled or not.
Note that adding a subversion (e.g. use v5.36.3
) will have no effect on the bundles loaded (feature bundles are guaranteed to be the same for all sub-versions). It will of course have an effect on the interpreter version check described above.
In addition to the implicit loading of features, use v5.12
or greater will enable strict
, use v5.36
will enable warnings
and use v5.40
will import builtin functions (using a similar version bundle scheme: see "Version-Bundles" in builtin).
Note that use VERSION
is a lexical pragma, meaning that you could declare different version bundles for different parts of your code. For consistency, it's really recommended that you pick one version, and stick with it for the entire file. In fact, a future release of perl
will disallow changing the version once one has been declared. So, really, don't do that.
The features included in bundles fall in two broad categories: new features, and deprecated features. Loading a bundle will perform the following two operations in a single line of code:
Before features become part of the "official" language, they are often introduced as experiments. Experimental features are available only when requested via use feature
, and aren't part of a bundle. In fact, they'll even issue warnings when you use them. (See experimental for more about this.)
For example, the signatures
feature was introduced in perl
v5.20, and remained experimental until v5.34. That meant that, as long as the perl
binary was more recent than v5.20, one could use signatures in their code with use feature 'signatures'
. That specific feature has been added to all bundles since :5.36
, which means that a single declaration (use v5.36
, or any later version) allows one to write Perl subroutines using signatures.
Not all new features have to wait that long to become part of the language: module_true
was introduced in perl
v5.38, and was immediately added to the :5.38
feature bundle.
Bundles have also been used to disable features that have become discouraged. These are made part of the :default
feature bundle, and not included in later bundles.
Doing it this way makes it possible to preserve the behaviour of ancient, unmaintained scripts and modules. Even if they load some feature bundle (via use VERSION
), any discouraged feature they might use will also be (retroactively) included in that bundle, preserving backward compatibitlity.
This is how the features indirect
, multidimensional
and bareword_filehandles
came to be "removed" from later versions of Perl.
This is the strategy the Perl Steering Council has chosen to best stretch the language between the continuous introduction of new features and the preservation of backwards compatibility. For the record, many Perl 4 (which is functionally identical to Perl 3, from late 1989) scripts still run fine with perl
v5.40.
When perl
compiles Perl code, before it even reads the first byte of source code, it is in "Perl v5.8 compatibility mode". The :default
bundle is implicitly loaded (it only contains features that are backward-compatible with traditional Perl).
This means that the "sensibly-written code from 20 years ago" mentioned above is very likely to just run unmodified, and simply work.
As explained above, unguarded syntactic features are available from line 0.
We've seen that use VERSION
automatically loads the corresponding feature bundle (and associated builtins). This single line of code enables all official features and disables all deprecated features for the given version of the Perl language.
In other words, putting a use VERSION
line at the top (line 1) of a Perl script or module declares which version of the Perl language the code that follows is written under. And because the effect is lexical, a script can load other modules that declare they were written against a different version of the language, and everything works transparently.
I strongly believe that use VERSION
should be the first line of any Perl code.
For decades, the first recommendation made to Perl beginners and people asking for help on a Perl forum was to add use strict; use warnings
at the beginning of their code. This made Perl more helpful, as it would point to likely errors in programming (like undeclared variables or undefined values).
Since Perl v5.36, these two statements are implicitly included via use VERSION
. It also enables the official features of that version of the language, and disables the deprecated ones.
Therefore, use VERSION
helps define a good baseline for the Perl language, so that the compiler can fully understand the code that follows. It's of course possible to include experimental features, or re-enable deprecated features (the latter is really not recommended), to fine-tune the specific dialect of Perl in which the code is written.
Future versions of perl
implicitly promise that they will understand the dialect of Perl declared by use VERSION
, and that they will run it to the best of their abilities.
Since the time of "use strict and warnings" as minimum requirements for decent Perl code, the language has evolved, and brought in more useful features. It also deprecated ancient features that were deemed bad ideas in a modern context (such as the indirect object notation).
Writing use VERSION
as the first (active) line of any Perl code will declare to the perl
interpreter (and to whoever is reading the code) which version of the Perl language the code that follows is written in.
So, just put a use VERSION
line at the top of all your Perl files.
Whenever a new stable version is released (which happens every year towards the end of May), you should at least read the perldelta that accompanies it, and check if you see some new feature you think you'd want to use.
There might exist some compatibility modules that you can use, to get a taste of those new features on a version of perl
that doesn't support them natively. They might behave slightly differently, though.
v5.36
(released in May 2022) contains a very good mix of stable features (default strict
and warnings
, signatures
, isa
), as well as the removal of deprecated features (indirect
, multidimensional
).
Most Linux distributions released in 2024 include a version of Perl that will support use v5.36
.
The version of the Perl language you want to use will depend on the context in which the code is run. Private or proprietary code has different constraints than an Open Source project or library distributed on CPAN.
Private or company code is only limited by the version of Perl used internally. It might be the stock perl
from the operating system selected by the organization. It might be a perl
compiled specifically for that purpose. Internal company or personal code can run on the bleeding edge!
An Open Source project will usually be shipped with or installed on top of common operating systems, and will usually be tied to the version of perl
these operating systems package.
The authors of modules distributed via CPAN distributions might want their code run on a broader selection of perl
versions.
However, you're unlikely to be able to start writing code with the latest version of the language very quickly:
maybe you're stuck with the version released by your OS vendor (but, do you know how easy it is to compile your own Perl?);
maybe you compile your own Perl, but it's a core part of your infrastructure, and upgrading takes time; (although the Perl parts are likely to be the easiest ones, given Perl's track record with backward and forward compatibility)
maybe you're a CPAN author, and you still want to support older versions of Perl.
At work, across all of our tens of thousands of Perl modules, over 30 different VERSION
are declared with use VERSION
. From v5.1 (someone typoed 5.010 as 5.001) up to v5.36, going through v5.10, v5.18.2 (someone didn't know the sub-version is ignored), v5.24, etc. Many files don't have a use VERSION
line (using the Perl flavor of 2002, when our Perl code base was started). The word for this is "legacy".
Since the effect of use VERSION
is lexical, it's possible to upgrade the version of the language your code uses one file at a time (or even one scope at a time, but see "Restrictions to use VERSION declarations" in perl5400delta for why you'll probably want to stick with the whole file).
I can confirm it's really nice to be able to first upgrade the Perl binary without changing a single line of code, and then choose which files to upgrade first.
That transition can be difficult, though:
v5.28 subtly changed the meaning of |
, &
, ^
and ~
(the bitwise
feature is enabled)
v5.36 won't understand $fido = new Camel "Amelia"
(the indirect
feature is disabled)
v5.36 will complain about sub foo ($$)
(the signatures
feature is enabled, prototypes must be declared as an attribute)
v5.38 will complain about open FH, $file
(the bareword_filehandles
feature is disabled)
Picking exactly which version of the Perl language to use is the actual question one has to answer. And the answer will be different if one is a CPAN author, a hobbyist writing their own tools, an Open Source application developer or a developer for a company's web application or internal tooling. The answer depends on several factors: one of them is the programmer's desire to use recent Perl language features, and another is their expectations regarding the minimum version of the perl
binary the code is expected to run on.
With tens of thousands of files to potentially update, I didn't imagine for one second that I would do it manually. And even if my colleagues would also help, I knew that upgrading the use VERSION
line in their code would make some of them uneasy.
So I wrote a module that would take Perl code, statically analyze it using PPI, and bump the declared version to the requested one, while being extra careful about the issues detailed above (and others). Since that code contained no company secret, I was allowed to open source it.
The code now lives on CPAN as Perl::Version::Bumper. And has improved a lot since my last commit in the company repository. I've actually deleted the code since, and we now depend on the CPAN module.
Here are a few examples of what it does, assuming we want to bump the example code to v5.40:
simply bump the version number:
print "Hello, world!\n";
becomes:
use v5.40;
print "Hello, world!\n";
remove compatibility modules that become unnecessary once the corresponding feature is enabled by the bundle:
use Say::Compat;
say "Hello, world";
becomes:
use v5.40;
say "Hello, world";
remove warnings about experimental signatures, once they come out of experimental:
use v5.20;
use feature 'signatures';
no warnings 'experimental::signatures';
sub greeting ( $who ) { "Hello, $who!" }
say greeting "world";
becomes:
use v5.40;
sub greeting ( $who ) { "Hello, $who!" }
say greeting "world";
rewrites prototypes when enabling the signatures
feature:
use v5.10;
sub greeting ( $ ) { sprintf "Hello, %s!", shift }
say greeting "world";
becomes:
use v5.40;
sub greeting :prototype( $ ) { sprintf "Hello, %s!", shift }
say greeting "world";
disable features that might cause problems, and add a warning about them:
use v5.10;
say "hello, world!" | "@"; # flip the capital bit on the first letter
becomes:
use v5.40;
# IMPORTANT: Please double-check the use of bitwise operators
# before removing the `no feature 'bitwise';` line below.
# See manual pages 'feature' (section "The 'bitwise' feature")
# and 'perlop' (section "Bitwise String Operators") for details.
no feature 'bitwise';
say "hello, world!" | "@"; # flip the capital bit on the first letter
In safe mode, stop at the last version that compiles (v5.38 disabled the bareword_filehandles
feature, turning their use into a compile-time error):
open FH, 'greeting.txt' or die "Can't open file: $!";
print FH "Hello, world!\n";
becomes:
use v5.36;
open FH, 'greeting.txt' or die "Can't open file: $!";
print FH "Hello, world!\n";
Since the module is meant to upgrade older Perl code, I made sure it can run on perl
binaries as old as v5.10. And it can use perl5.10.0
to upgrade source code up to v5.40!
The distribution contains a small command-line utility to process many files at a time: perl-version-bump.
It runs in safe mode by default, meaning it will start at the version of the perl
used to run it, and decrease the target version number until the generated code compiles, or give up.
If I can suggest some New Year Resolutions for 2025:
decide which version of the Perl language you want to code against in your various projects,
start consistently adding use VERSION
on line 1 of all your Perl files,
and bump the existing versions where it makes sense!
The North Pole Workshop’s logistics department has been busy this year with upgrading the sleigh’s dash-cam with a new more powerful computer. They’ve asked the research and development department if there is anything that can be done with the extra cycles to make the journey safer. After bouncing around a couple ideas, they came up with a plan for an autoland system…but since Santa and the reindeer do not operate in typical environments (such as roofs), this would not be a typical autoland system (which use microwave/radio guidance) so they couldn’t select anything off-the-shelf (and they do know their shelves).
Published by oodler on Saturday 21 December 2024 00:00
It was Christmas Eve, and Santa was facing a nightmare. The sleigh’s new GPS system, upgraded for the first time in centuries from following the Star of Bethlehem, was malfunctioning. As Santa checked the screen, the map was a chaotic mess — locations were wrong, some coordinates did not make sense, and the sleigh was heading straight into the mountains instead of over the City of New Orleans!
Santa's CURRENT position read-out indicated he was over the Appalachians at the following coordinates:
Sleigh Latitude Sleigh Longitude Sleigh Altitude (ft)
38.0000° N -81.500° W 300
When he should be nearly exactly at,
Sleigh Latitude Sleigh Longitude Sleigh Altitude (ft)
29.9500° N -90.070° W 300
"We can't afford this!" Santa shouted. "We’ve got billions of presents to deliver, and the system's down. And I want some gumbo!"
"Santa, we can fix it," Jingles, the lead elf, said with a frantic smile. "We just need more computing power. If we use OpenMP, we can parallelize the calculations and solve this quickly."
Santa stared. “What are you talking about, Jingles? I'm a giant elf, not a Scottish engineer on a television show for nerds!”
“We’ll solve the GPS problem by recalculating the distances to each delivery location. The sleigh’s GPS relies on triangulating data from multiple satellites. Right now, it's too slow to process the data for each location one by one. We can use OpenMP to divide the problem into smaller parts, calculate distances in parallel, and get this fixed fast!”
The data format of the positional satellites consisted of their positions in latitude, longitude, with an altitude; so the computations are necessarily in 3D space, and could contain any number of lines:
39.2497677581748 -66.1173923826129 29161.8658117126
-39.9677413540029 -41.3796046007432 23577.9949741844
82.2366387689737 153.417562140013 20022.1066827945
-43.4383552881127 -44.5406041422343 28011.9118605715
14.0767035175103 4.23608833137735 27766.7951824014
40.8573795733001 162.321349651587 25625.9162363042
-26.9656081428904 27.0935089406365 23681.3611776769
70.1644045619636 6.85910122836034 26946.7435635683
-64.5469805915126 -14.9091572762404 24893.2320145114
80.875127931392 -109.736894500449 23367.1123572306
5.62494420727084 -70.3599057022578 24677.4930437516
-78.594647140356 -69.1886836681495 21775.1041983417
-20.7093134304093 50.3824566178804 28396.5251214701
-8.19130244183 28.3379349990834 24113.3081697615
-62.1626942859846 -165.892484372947 27881.1415552865
-39.1505435434735 -14.0167682855066 25391.598017652
-14.701859640773 33.3797684668173 28958.4392020613
22.7094543766397 28.9295184727116 29847.2350918171
58.9987788505186 -87.6847921052664 29544.1317147911
83.6874173858257 -149.058764882263 22417.9224396509
-8.01336267852399 97.8876777856595 27879.4674084787
-55.3867650906297 -107.353651427755 21389.9702111095
-78.8588152992001 -155.558248147836 25430.0402744441
9.88995820014878 -0.204367766261981 20832.5618074863
-76.8565255868645 -14.4804333171123 27013.5287117141
-0.16890065869049 -40.7974093702016 22440.2960018416
8.56759320194605 14.0242190926548 24229.1350707098
89.3116725410715 19.3710347706399 28181.9446348641
...
The distance formula was computationally expensive, involving square roots and trigonometry. But by using OpenMP, the task could be split into multiple OS threads, each calculating the distance to one satellite at the same time. The results would then be aggregated, and the sleigh's GPS would know exactly where to direct its heading.
The code would parallelize the work by dividing the list of satellites among a number of pthreads using OpenMP's #pragma omp for
construct! OpenMP::Simple
handles the required #include <omp.h>
and makes it easy to query the environment for information, such as OMP_NUM_THREADS
.
Jingles quickly typed up a solution in Perl, his favorite programming language. The solution was simple but critically offloaded the triangulation computations to Inline::C
code containing OpenMP directives, using the OpenMP
module on CPAN.
use strict;
use warnings;
use OpenMP;
use Inline (
C => 'DATA',
with => qw/OpenMP::Simple/,
);
my $omp = OpenMP->new;
$omp->env->omp_num_threads($ENV{OMP_NUM_THREADS} // 8); # accept number of threads via commandline
# Sleigh's CURRENT position (latitude, longitude, altitude)
my $sleigh_lat = 38.0000;
my $sleigh_lon = -81.500;
my $sleigh_alt = 300.0; # in meters
# Satellite positions in tab-delimited format
my @satellites = ();
open my $FH, "<", "./satellite-data.txt" || die $!;
foreach my $line (<$FH>) {
chomp $line;
push @satellites, [split(/[\s\t]+/, $line)];
}
# Function to calculate distance from sleigh to each satellite using OpenMP::Simple,
# a subclass of Inline::C!
my $distances = calculate_distances($sleigh_lat, $sleigh_lon, $sleigh_alt, \@satellites);
# Print the calculated distances
foreach my $distance (@$distances) {
print "Distance: $distance meters\n";
}
Santa watched as Jingles split up the computational load. Each satellite’s data was handled in parallel by different threads, and within seconds, the recalculations were done. The GPS latency issues were fixed. Jingles already had some thoughts of improvements he could make using OpenMP's ability to target GPUs directly, but was quite happy with the current resolution.
Just as the final calculations finished, a gentle voice spoke, “When Faith sanctifies morally neutral technology, Good things are possible at Internet scale.”
Santa turned to see the Holy Family - Baby Jesus in the arms of His Virgin Mother accompanied by His foster father, Joseph; by the sleigh, smiling serenely. “Thank you,” Santa whispered. “Merry Christmas.”
With the sleigh back on track, Santa soared into the night sky.
When things settled down, Santa was able to look at Jingles' full code, and it was something to behold!
use strict;
use warnings;
use OpenMP;
use Inline (
C => 'DATA',
with => qw/OpenMP::Simple/,
);
my $omp = OpenMP->new;
$omp->env->omp_num_threads($ARGV[0] // 8); # accept number of threads via commandline
# Sleigh's CURRENT position (latitude, longitude, altitude)
my $sleigh_lat = 38.0000;
my $sleigh_lon = -81.500;
my $sleigh_alt = 300.0; # in meters
# Satellite positions in tab-delimited format
my @satellites = ();
open my $FH, "<", "./satellite-data.txt" || die $!;
foreach my $line (<$FH>) {
chomp $line;
push @satellites, [split(/[\s\t]+/, $line)];
}
# Function to calculate distance from sleigh to each satellite using Inline::C
my $distances = calculate_distances($sleigh_lat, $sleigh_lon, $sleigh_alt, \@satellites);
# Print the calculated distances
foreach my $distance (@$distances) {
print "Distance: $distance meters\n";
}
__DATA__
__C__
#include <math.h>
// Function to convert geographic coordinates to Cartesian (x, y, z)
void geo_to_cartesian(double lat, double lon, double alt, double *x, double *y, double *z) {
double R = 6371000; // Earth's radius in meters
double lat_rad = lat * M_PI / 180.0;
double lon_rad = lon * M_PI / 180.0;
*x = (R + alt) * cos(lat_rad) * cos(lon_rad);
*y = (R + alt) * cos(lat_rad) * sin(lon_rad);
*z = (R + alt) * sin(lat_rad);
}
// Function to calculate Euclidean distance between two points (x1, y1, z1) and (x2, y2, z2)
double calculate_distance(double x1, double y1, double z1, double x2, double y2, double z2) {
return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2) + pow(z2 - z1, 2));
}
/* C function parallelized with OpenMP */
// Main function to calculate the distances from the sleigh to each satellite
AV* calculate_distances(double sleigh_lat, double sleigh_lon, double sleigh_alt, AV *satellites) {
int satellite_count = av_len(satellites) + 1; // The number of satellites
AV* distances = newAV(); // Create a new Perl array (AV) to hold the distances
double sleigh_x, sleigh_y, sleigh_z;
// Convert sleigh's geographic coordinates to Cartesian coordinates
geo_to_cartesian(sleigh_lat, sleigh_lon, sleigh_alt, &sleigh_x, &sleigh_y, &sleigh_z);
// Fetch satellite data into local arrays
double *sat_latitudes = malloc(satellite_count * sizeof(double));
double *sat_longitudes = malloc(satellite_count * sizeof(double));
double *sat_altitudes = malloc(satellite_count * sizeof(double));
// Populate satellite data into local arrays (from the Perl array)
for (int i = 0; i < satellite_count; i++) {
AV *satellite = (AV*) SvRV(*av_fetch(satellites, i, 0)); // Fetch the satellite at index i
sat_latitudes[i] = ((double) SvNV(*av_fetch(satellite, 0, 0))); // Latitude
sat_longitudes[i] = ((double) SvNV(*av_fetch(satellite, 1, 0))); // Longitude
sat_altitudes[i] = ((double) SvNV(*av_fetch(satellite, 2, 0))); // Altitude
}
// Declare a temporary array to hold distances for each thread
double *_distances = malloc(satellite_count * sizeof(double));
PerlOMP_GETENV_BASIC // read common environmental variables, provided by OpenMP::Simple
// Start parallel region
#pragma omp parallel shared(_distances)
{
// Parallel for loop to compute distances in parallel
#pragma omp for
for (int i = 0; i < satellite_count; i++) {
_distances[i] = 0.0; // Initialize
double sat_x, sat_y, sat_z;
// Convert satellite's geographic coordinates to Cartesian coordinates
geo_to_cartesian(sat_latitudes[i], sat_longitudes[i], sat_altitudes[i], &sat_x, &sat_y, &sat_z);
// Calculate the distance from the sleigh to this satellite
_distances[i] = calculate_distance(sleigh_x, sleigh_y, sleigh_z, sat_x, sat_y, sat_z);
}
}
// Combine the results from all threads into the main distances array inside the parallel region
for (int i = 0; i < satellite_count; i++) {
av_push(distances, newSVnv(_distances[i]));
}
// Free the private distance arrays and satellite data arrays
free(_distances);
free(sat_latitudes);
free(sat_longitudes);
free(sat_altitudes);
// Return the AV containing the distances to Perl
return distances;
}
__END__
See More:
Published on Saturday 21 December 2024 00:00
If you are manipulating images, you need PDL!
For years, I've had this urge to use PDL to take a bitmap image and trace the outlines to make an SVG file that I could scale up to A0 poster size without the resulting pixelation. Yes, there are already tools that do that, but where's the fun in that? It's Christmas, the time for Fun and Games!
Tags: image processing broadcasting