Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

Meditations

( [id://480]=superdoc: print w/replies, xml ) Need Help??

If you've discovered something amazing about Perl that you just need to share with everyone, this is the right place.

This section is also used for non-question discussions about Perl, and for any discussions that are not specifically programming related. For example, if you want to share or discuss opinions on hacker culture, the job market, or Perl 6 development, this is the place. (Note, however, that discussions about the PerlMonks web site belong in PerlMonks Discussion.)

Meditations is sometimes used as a sounding-board — a place to post initial drafts of perl tutorials, code modules, book reviews, articles, quizzes, etc. — so that the author can benefit from the collective insight of the monks before publishing the finished item to its proper place (be it Tutorials, Cool Uses for Perl, Reviews, or whatever). If you do this, it is generally considered appropriate to prefix your node title with "RFC:" (for "request for comments").

User Meditations
The Cargo Cult Generation Has Arrived Now What?
3 direct replies — Read more / Contribute
by Anonymous Monk
on Jan 23, 2026 at 10:02
    I asked Claude Haiku 3.5 at duck.ai a simple question and it produced a complete working program. How the heck does an LLM translate 11 somewhat poorly chosen english words into a readable 73 line maintainable perl program with freakin comments? It seems impossible. What is going on? Are we done programming or are us non vibe coders doomed to fix all the input validation and injection and other subtle bugs that are sure to arise from hundreds of monkeys implementing code they do not understand?

    Question:
    write an app that would render a Mandelbrot set in perl

    Answer: I'll help you write a Perl script to render the Mandelbrot set. I'll provide a comprehensive example that creates a visual representation of the Mandelbrot set using a simple ASCII or color output method.

    #!/usr/bin/perl use strict; use warnings; use Term::ANSIColor; # Mandelbrot Set Renderer # Configuration parameters my $width = 80; # Width of the output my $height = 40; # Height of the output my $max_iterations = 100; # Maximum iteration count # Mandelbrot set calculation function sub mandelbrot { my ($c_real, $c_imag) = @_; my $z_real = 0; my $z_imag = 0; for (my $i = 0; $i < $max_iterations; $i++) { # Mandelbrot iteration: z = z^2 + c my $new_real = $z_real * $z_real - $z_imag * $z_imag + $c_real +; my $new_imag = 2 * $z_real * $z_imag + $c_imag; $z_real = $new_real; $z_imag = $new_imag; # If magnitude exceeds 2, it's not in the set return $i if ($z_real * $z_real + $z_imag * $z_imag > 4); } return $max_iterations; } # Render the Mandelbrot set sub render_mandelbrot { for my $y (0 .. $height - 1) { my $line = ''; for my $x (0 .. $width - 1) { # Map pixel coordinates to complex plane my $real = ($x / $width) * 4 - 2; # X-axis from -2 to +2 my $imag = ($y / $height) * 4 - 2; # Y-axis from -2 to +2 my $iterations = mandelbrot($real, $imag); # Color/character selection based on iterations my $char; if ($iterations == $max_iterations) { $char = colored('#', 'black'); # Points inside the se +t } else { # Gradient coloring based on iteration count my $color = _get_color($iterations); $char = colored('*', $color); } $line .= $char; } print "$line\n"; } } # Color selection function sub _get_color { my ($iterations) = @_; my @colors = ( 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'bright_red', 'bright_green' ); return $colors[$iterations % scalar(@colors)]; } # Main execution print "Mandelbrot Set Visualization\n"; render_mandelbrot();
    It also summarized what it did in english:
LeetCode's ignorance of Perl
4 direct replies — Read more / Contribute
by Anonymous Monk
on Jan 11, 2026 at 23:07
    Ignoring a language as venerable as Perl on a site like LeetCode seems really strange. Thoughts?
    
    Supported:
    Bash
    C
    C++
    C#
    Dart
    ECMAScript
    Elixir
    Erlang
    Go
    Java
    JavaScript
    Kotlin
    PHP
    Python
    Python3
    Racket
    Ruby
    Rust
    Scala
    Swift
    TypeScript
    
A Christmas detour: it's the simplest damn things that bite us sometimes
3 direct replies — Read more / Contribute
by Intrepid
on Dec 26, 2025 at 15:49

    It happened to me again. I though I was going to work out a small shortcut on the text interface command line (shell) and it would take me perhaps a couple of minutes. Instead, it became an extreme detour over two days of effort and I must have put at least 2 or 3 hours into it. In the end, the answer was "perl". So here's what happened.

    BTW, I'd first thought to create this node in "Cool Uses For Perl" but there's something HTML-ish messing up the layout there. The page horizontal width is massively enlarged so that one has to scroll drastically to read nodes.

    Good monks (nuns too), here's how it started. I had a snippet of data copied from a metacpan.org web page into the system clipboard. Here's the data:

    
       CPAN::Meta
       ExtUtils::Builder
       ExtUtils::Builder::Compiler
       ExtUtils::Config
       ExtUtils::Helpers
       ExtUtils::InstallPaths
       Getopt::Long
       List::Util
       TAP::Harness

    What I wanted was the transmogrification of this list of lines into a single line (spaces between module names, of course). What I then planned to do with with my string data (again, we're in the shell here) was to run cpanminus on the list, which btw are dependencies of the module Dist::Build, so that I'd be ready to install Dist::Build without having cpanm have to do the depends-on dance for me. Why would want to do that? Sometimes I want to open a shell in a module build dir and build by hand, for a few different reasons. In this case the goal of shaping that string of module names completely eclipsed the original intention. It became an obsession.

    Here's how it unfolded. I went to the toolshed and first I grabbed the hoe (sed). I do things on the principle of finding the smallest, simplest tool to do the jobs, it's just a matter of personal preference to be that way. sed doesn't weight that much.

    I don't have a record to show the reader, of exactly what my invocation of sed looked like, because I had an os crash/reboot that wiped out the bash history before it could be saved, but the result I eventually got looked like this:

    TAP::HarnessstallPathspiler

    I should note that sed scripts can become very elaborate and I am not now (and probably never was) a skilled sed user; I generally just find a matching or substitution expression that will accomplish what I want, and that's as far as it goes. I found some very elaborate sed scripting on Snark-Overflow that didn't work for me. I was starting to feel cursed.

    If anyone can explain to me why I get that string, I'll be very satisfied.

    Next I put the hoe back and grabbed the spade (tr). Once I read the help page for tr I worked out a command line, and what did I get?

    TAP::HarnessstallPathspiler

    That's right. The same damned mojibake mess.

    The same when I went to the toolshed once more and selected the pitchfork (fmt). I'm not sure whether I've ever even used fmt before, I dutifully read the help page and tried. Aughhhhh. More mojibake.

    OK, it's Perlmonks here and so you saw this coming: I put away the hand tools and rolled out the backhoe (perl). Now, I flatter myself that I know perl better than I know sed or fmt, and I wondered why it had taken me so long to turn to our finest data-munger.

    Well, I still had the dreadful line at first,with perl (which was a little bit of a comfort because I had long since started to question my own sanity):

    TAP::HarnessstallPathspiler

    Here's one thing I tried with perl:

    $ </dev/clipboard perl -e 'push @a,$_ while <>;chomp @a;print join(@a,q[ ])'

    Yep, same thing. But at last, I found the/a perl solution.

    $  </dev/clipboard perl -0777 -e 'my ($l)=<>; $l=~s{.\cJ}[ ]g; print $l;'
    OUTPUT:
    CPAN::Meta ExtUtils::Builder ExtUtils::Builder::Compiler ExtUtils::Config ExtUtils::Helpers ExtUtils::InstallPaths Getopt::Long List::Util TAP::Harness

    The obscure -0777 switch in that invocation turns on slurp mode so every line gets read at once, to the end of file.

    The observant reader is perhaps wondering what's this /dev/clipboard. I'm working on Cygwin; that's a special device file that Cygwin makes available, that's all.

    If there's a lesson to be learned here, it might be "use the tool you know best, first."

    Thanks for reading, and I know some of you laughed in rueful recognition (because this sort of thing has happened to me before this time, too).

    Dec 26, 2025 at 20:47 UTC

    A just machine to make big decisions
    Programmed by fellows (and gals) with compassion and vision
    We'll be clean when their work is done
    We'll be eternally free yes, and eternally young
    Donald Fagen —> I.G.Y.
    (Slightly modified for inclusiveness)

Don't Touch The System Perl - revisited
No replies — Read more | Post response
by karlgoethebier
on Dec 19, 2025 at 16:01

    My efforts to learn NixOS and Nix led to this small Nix/Perl project, which I enjoy quite a bit: p-gadget. There is a short manual on how to use it on the start page. Since the code is quite extensive and spread across several files, I didn't want to publish it directly here. Sorry for the inconvenience. Please don't hesitate to send me your comments and criticism.

OK, OK. AI is a handy tool.
2 direct replies — Read more / Contribute
by talexb
on Dec 18, 2025 at 22:42

    I will admit I've been extremely skeptical of Artificial Intelligence, but this week I found that it was a really useful tool in one specific application.

    I've been developing some REST API scripts for a client of mine since 2018, and one of the scripts takes a stroll through his product catalog (hosted on BigCommerce), looking for any items have disagreements between the website and his accounting system. One of the scripts has been taking over an hour to run, because for each product, it has to do a second call to BigCommerce for additional product information. In a single-threaded situation, all of those latencies add up.

    I was already using Parallel::ForkManager for one of the scripts, so I thought I'd use it again for a new version of this script. Unfortunately, the new script didn't work correctly. My client has access to the repositories where I store the code, so he grabbed the code and fed it into Claude Opus 4.5. This AI produced some really helpful pointers, and I incorporated five of the six improvements.

    Really, it provide a second set of eyes on my code. Colour me impressed.

    Alex / talexb / Toronto

    As of June 2025, Groklaw is back! This site was a really valuable resource in the now ancient fight between SCO and Linux. As it turned out, SCO was all hat and no cattle.Thanks to PJ for all her work, we owe her so much. RIP -- 2003 to 2013.

Open Sourcing the Virtues of a Perl Programmer
1 direct reply — Read more / Contribute
by oodler
on Nov 30, 2025 at 11:49
    As promised, below is the full text of the paper I presented at the 2025 London Perl Workshop and follow up to my 2024 talk at the TPRC in Las Vegas on the Virtues of Perl. Since I didn't get through it all, I said I'd post it somewhere. Here that is. Thank you to the organizers of the 2025 LWP for having me on remotely from Texas.

    Open Sourcing the Virtues of a Perl Programmer

    Brett Estrade (OODLER)

    Introduction

    When Larry Wall revealed to the Perl faithful in 1996 1 that the "Three Virtues of a Perl Programmer" were: laziness, impatience, and hubris, he also clearly described what he meant. And what Larry meant was in stark contrast to the actual meanings of the terms he chose to use. Unfortunately today only the words remain, while what he actually meant has been lost to the sands of time. Worst of all, the terms are touted as virtues, when in fact, they are objectively all vices. The Perl Community aims to correct this situation by restoring focus on the actual virtues described by Perl's creator, Larry Wall.

    Aristotle, the ancient Greek philosopher, believed that virtue is the key to a good life. He taught that living with balance and practicing moral virtues, like courage and wisdom, leads to true happiness and fulfillment.

What's the "modern" way to talk to C libraries?
2 direct replies — Read more / Contribute
by haj
on Nov 23, 2025 at 17:32

    When I started with Perl in the previous century, one of its highlights was its use as a "glue" language, and its ability to make simple things easy. GD is the first I recall, later I had fun with the inevitable DBI, did some work with XML::LibXML and a lot of others I am to lazy to recall right now. Then came THE WEB and Perl developers (including me) seem to have changed their focus (and also: Many new developers solved web problems using Perl). In the last two years, when I played with 3D graphics and sound, I was a bit disappointed about the amount of stale / abandoned CPAN modules in that area. I was able to use Audio::PortAudio (most recent release in January 2007) to record sound from my microphone, but only after suppressing a bunch of warnings, and I was surprised that it worked at all.

    Looking for a contemporary way to do sound on Linux, I stumbled over PipeWire. It comes with a set of CLI commands, decent documentation, and a tutorial in C. I want to do two things: 1) record and play sound without shelling out to an external program, and 2) detect whether a microphone is actually plugged in so that I can enable/disable the "record" button in my GUI accordingly. But like many "modern" C libraries, PipeWire utterly sucks at making simple things easy. So I thought: How hard can it be to use that library from Perl?

    I am not at all an expert in this area. I have not used C since about 20 years and never tried XS programming. I have heard good things about FFI::Platypus, so I thought I would give it a try. It turned out to be a roller coaster of success and failures.

    FFI::Platypus does not really support several constructs used by the library (dynamically sized arrays of structs, callbacks returning pointers to a struct, ...). I also need to manually copy-write every single struct in terms of FFI::Platypus (or FFI::C) which stops to be fun after the first two dozen structs. The (very good, BTW) docs point to Convert::Binary::C for these things, so I added that to the mix. I found that it didn't handle current C compilers (the maintainer has fixed this on GitHub, no CPAN release yet) and can not handle many of PipeWire's header files because they make heavy use of GCC extensions. I also learned that Convert::Binary::C is in "bugfix-only" state. I have segfaults (same thing as these) which suggest that the Perl we get from Linux distributions can not handle the library at all, so I build my own Perl for this experiment.

    Of course, there's also the XS route, and Dave Mitchell has announced his plan to Rewrite the XS pod files. I am somewhat impatient to see this, but as far as I know the tedious task of re-writing every struct (called "typemaps" in XS) isn't much easier than doing it with FFI::Platypus.

    My current route is to use Convert::Binary::C to auto-generate Perl classes equivalent to the C structs. This is actually fun, but also time consuming and I am afraid that I get lost in abstractions over abstractions and never get to actually use the Perl library.

    So, while I am still making progress, I wonder: Are there other developers around who work with C libraries these days? How do they do it? Or is this an art soon to be obsolete?

Linux Show Permissions: traverse file access
1 direct reply — Read more / Contribute
by FreeBeerReekingMonk
on Oct 19, 2025 at 17:20
    This script walks the Linux directory structures to files, while checking ACL access to them and displaying the access rights to all subdirectories.

    It can tell you if another user is able to access your files or not, or when used with sudo, check for access of files not owned by you, for any other user on the system. Native permissions (chmod) are shown in uppercase to distinguish them from ACL permissions (setfacl), which are lowercase.

    Let's start with a Usage Example. I naively give user nobody access to t.pl, but as $HOME directories are closed and secure, the actual access is prevented:

    fbrm@debian:~$ setfacl -m u:nobody:r ~/CODE/PERL/t.pl fbrm@debian:~$ find $HOME/CODE/PERL -name '*.pl' | USR=nobody ./showpe +rms.pl | column -t DEBUG: Testing permissions against user 'nobody' / OTHER::r-x /home/ OTHER::r-x /home/fbrm/ --- /home/fbrm/CODE/ OTHER::r-x /home/fbrm/CODE/PERL/ OTHER::r-x /home/fbrm/CODE/PERL/showperms.pl OTHER::r-x /home/fbrm/CODE/PERL/t.pl user:nobody:r-- fbrm@debian:~$ find $HOME/CODE/PERL -name '*.pl' | ./showperms.pl | co +lumn -t DEBUG: Testing permissions against user 'fbrm' / OTHER::r-x /home/ OTHER::r-x /home/fbrm/ USER:fbrm:rwx /home/fbrm/CODE/ USER:fbrm:rwx GROUP:fbrm:r-x /home/fbrm/CODE/PERL/ USER:fbrm:rwx GROUP:fbrm:rwx /home/fbrm/CODE/PERL/showperms.pl USER:fbrm:rwx GROUP:fbrm:rwx /home/fbrm/CODE/PERL/t.pl USER:fbrm:rw- GROUP:fbrm:rw-

    TIP: Instead of a find, you can also cat a file with fully qualified files, or echo a file.

    #!/usr/bin/perl use strict; use warnings; my $DEBUG = 1; my $u = $ENV{USR} || $ENV{USER}; # Set default user to check permissio +ns for warn "DEBUG: Testing permissions against user '$u'\n" if $DEBUG; my $g; my $o; my %P; my $file; while( $file = <STDIN> ) { chomp($file); if ($file && -e $file) { my @F = split /\//, $file; while(@F){ $P{ join("/", @F) || "/" } = 0; # Build a list of Fully Qu +alified paths. Deduplicated. pop @F; $F[-1] .= "/" if @F; # also add the root path to check }; }else{ warn "Could not find file '$file' (maybe run this as root to g +et access to the file?)\n"; } } for my $k (sort keys %P) { my @A = `getfacl "$k" 2>/dev/null | grep -v -e default: -e file:`; + # grab output ($o) = map { /# owner: (\S+)/ } @A; # Get the file owner ($g) = map { /# group: (\S+)/ } @A; # Get the file group grep { s/user::/USER:$o:/} @A; # make native Linux dir/file permis +sions uppercase grep { s/group::/GROUP:$g:/} @A; # idem grep { s/other::/OTHER::/} @A; # idem $P{$k} = join "\n", grep { !/^#/ && /$u|^other/i } @A; $P{$k} =~ s/:[^:]+effective:/:/; # consider only effective permiss +ions $P{$k} =~ s/\S+:---//g; #remove empty permissions $P{$k} =~ s/(?:user|group):[\s\S]*\K(other:.*)//mi; $P{$k} =~ s/[\n\r\s]+/ /g; # remove newlines $P{$k} = '---' if $P{$k} eq " "; # if no permissions, default to - +-- }; for my $k (sort keys %P){ print "$k $P{$k}\n"; }
JIRA-Client-Automated stopped working for me, Atlassian modified the API's. The requested API for searching issues has been removed.
3 direct replies — Read more / Contribute
by gargle
on Sep 18, 2025 at 02:29

    Not really a question, but more like a warning for other users of JIRA-Client-Automated. If this notification doesn't belong here, then please let me know. It's been too long ago since I last visited perlmonks.org, sorry!

    I'm using all_search_results in JIRA-Client-Automated to filter out all issues that are assigned to me. The code is here below. The call doesn't work anymore.

    I already logged bug https://rt.cpan.org/Public/Bug/Display.html?id=170062

    my @open_issues = $jira->all_search_results( "(((assignee = " . $accountId . " or assignee = 557058:03251bcc-c6a3-4137-a7f0-d5c63be68d1d9 +)" . " and project != \"ST - Dev Support\")" . " or ((assignee is empty or assignee =" . " 557058:03251bcc-c6a3-4137-a7f0-d5c63be68d1d9 or assignee = + " . $accountId . ") AND project = \"ST - Dev Support\")) AND status != Done" . " and status != Cancelled order by created DESC");

    This is the error I am getting:

    Unable to POST /rest/api/latest/search/: 410 Gone{ errorMessages => [ "The requested API has been removed. Please migrate to the /rest/a +pi/3/search/jql API. A full migration guideline is available at https +://developer.atlassian.com/changelog/#CHANGE-2046", ], errors => {}, } for request: { fields => ["*navigable"], jql => "(((assignee = 557058:488e7764-b837-4c11-974d-ec9115f47e26 or + assignee = 557058:03251bcc-c6a3-4137-a7f0-d5c63be68d1d9) and project + != \"ST - Dev Support\") or ((assignee is empty or assignee = 557058 +:03251bcc-c6a3-4137-a7f0-d5c63be68d1d9 or assignee = 557058:488e7764- +b837-4c11-974d-ec9115f47e26) AND project = \"ST - Dev Support\")) AND + status != Done and status != Cancelled order by created DESC", maxResults => 100, startAt => 0, } at ./checkJiraIssues.pl line 57.
    --
    if ( 1 ) { $postman->ring() for (1..2); }
OT: Stupid User Interfaces (Part 2)
4 direct replies — Read more / Contribute
by afoken
on Aug 23, 2025 at 07:53

    (Continuing the old rant OT: Stupid User Interfaces)

    How hard can it be to open a door?

    This week, I had to travel in a regional train, a quite modern Stadler KISS train operated by DB Regio (Deutsche Bahn). Very unsurprisingly, it has electrical door openers to enter and leave the train. A big round button surrounded by red and green LEDs. Wait until the green LEDs are on, press the button, and the door will open. Easily done even by a three year old kid. I'm old enough to remember trains where you had to push the door open, requiring quite a lot of force.

    These modern doors (and that's not limited to the KISS) tend to fail quite often, so DB Regio uses large yellow stickers on the door windows telling you that the door is unfortunately broken and that you should use one of the other doors. Well, that's annoying. You would expect that a door that is used daily by literally thousands of people in the not too extreme weather of Germany to be designed to withstand that use for more than a few months. But that's not the point. The yellow sticker tells you that the door won't open, period.

    The KISS train has doors between the segments, designed as a kind of "leaky airlock", with a sealed electric sliding door opened by a small illuminated button on the one side and an unsealed glass swing door on the other side. A very strange design, causing a lot of noise and draught near the glass door, the glass door needs a lot of room in a small area to swing open, and it needs some force to open. Two sliding doors, as in much older trains, would have been the smarter solution.

    Now imagine that ugly yellow sticker of shame on the electric sliding door, telling you to use one of the other doors, and the "open" button not being lit. Are they expecting me to climb out of the train at 100 km/h, and enter the next car through one of the outside doors? Probably not. There is a yellow sticker of shame on the door, so it won't open, period.

    Or will it? After abour half an hour, someone entered the car through the door. So my thought was "poor guy, now you are trapped in this car, too". I was expecting that only the open button on my side of the door was broken. And he went back to his car, through the door with the yellow sticker. So, yes, the door could not be opened electrically. But it still has handles, and can be opened with a little bit of force. D'oh! I could have travelled in a car with way less people if I had actually tried the handles.

    How hard can it be to open a door, again?

    Remember that big round buttons with the LEDs, that can open the door when the green LEDs are on? Three of the four cars in a KISS train are equipped with a large lavatory, usable even if you need a wheelchair. One of the big round buttons with the red and green LEDs is mounted next to the door of the lavatory, with the green LEDs permanently on. Guess what happens if you press that button.

    If you are unlucky, a voice recording tells you that the lavatory is out of order. Or it tells you that the lavatory is currently in use. In both cases, the door won't open. But, if the lavatory is unused, and you press the button, the voice recoding will tell you exactly that. The door won't move a bit.

    This button is not a door opener. It is a method of identifying frequent travelers. If you know the KISS trains, you completely ignore the button and look at the traditional red "WC" light. If it is off, you can enter the lavatory by pulling hard on the door handle. If you don't know the KISS trains, that button will make you look like a fool who does not know the red light.

    The official purpose of the button a replacement for the red light for blind and visually impaired persons.

    Too bad noboby really thought about the lavatory door. It is huge, to allow entering the lavatory in a wheel chair. Good. But it requires a lot of force to open and close. Little kids can't open the door, and even some adults have problems opening and closing it. So, where is the electrical door opener wired to the infrequent traveler identifier button?

    How hard can it be to track working time?

    German law forces us to track working time, and that's a good thing. We don't need to track anything more than "working" or "not working". A time stamp clock at the entrance would be sufficient. Except that we sometimes work from the home office, or spend an entire day at a client. Legally, we could even get away with hand-written notes. The law just requires that the working time is recorded, not how.

    We work for different clients, sometimes with a fixed price, sometimes we are being paid by the hour, sometimes both at once even in the same project (don't ask). So we recently decided to use a cloud-based time tracking app, which can run in a web broswer, in a desktop application (which is just a browser without an address bar), or in an Android or iOS app (which propably also is just a browser without an address bar).

    • You can not change your password. Period.
    • Logout button is hidden behind two menu layers.
    • There is a permanent reminder (in the browser) to install the desktop application, that you need to click away every single time you log in.
    • The desktop application refuses to start after installation ("This is not a Windows application")
    • The Android app is simply unusable. It has the same visual interface as the website, but requires completely different handling than all other Android apps (drag instead of tap for a new record, no way to edit records, and so on).
    • You can track time only in a calender view:
      • Short jobs are so tiny in the default zoom level that you can not see them
      • Long jobs require zooming out and/or scrolling.
      • You need to open a popup to fix start and stop times, or use zoom and drag-and-drop to adjust start and end times
      • Zoom requires you to use a special control on the page, you can not use Ctrl + mouse wheel or keyboard shortcuts
    • You are not allowed to track times of one minute or less. (Not a company policy, the software prohibits that.)
    • There is no list view of tracked time, period.
    • About half of the screen width is reserved for unused features, while at the same time project names are cut of due to width restrictions.
    • The default for the "paid by the hour" switch can be set by the admins for each project. In mixed-payment projects, you may need to change that switch. Which causes more trouble:
      • Admins can't change switch in tracked time, so lists need to be written and users have to toggle that switches for an entire a month.
      • Resuming a job resets the switch to the default value instead of keeping the switch as is

    And that's only the interface for the "worker bees". I did not even bother to look at the admin stuff, or the reporting that is completely useless unless you are working self-employed.

    All we really need is a simple, five-column table:

    • Start time
    • End time
    • Project (from a lookup table)
    • paid by the hour (checkbox)
    • Short description for the invoice

    Invisible to the user, a sixth column should track the user ID, so working times can be properly summed up, and users see only their own time records.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Printing maps with LeafletJS & a potent way to html2pdf
No replies — Read more | Post response
by bliako
on Aug 20, 2025 at 17:47

    Recently I needed to display a map with some polygons, markers and popups on it. I wanted to have both an interactive map but also be able to create a hardcopy from my linux's terminal via a script in a headless server.

    I found a solution which satisfies both requirements. Have the interactive map be from the HTML produced by LeafletJS and then spawn a headless browser (thank you WWW::Mechanize::Chrome & Corion!!) and take a screenshot of the map, via a script from any headless server.

    To me, at least, the screenshot part sounds like a very roundabout way of getting a hardcopy of a map (a PDF or PNG) but after researching this for a few weeks, I realised that this is how it is done in R (and in the serpent world, it is called htmlwebshot).

    Building on WWW::Mechanize::Chrome's screenshot functionality and on a conversation I had with LanX some years ago on how to inject JS into a mechanized browser and manipulate the DOM with it I have now published WWW::Mechanize::Chrome::DOMops which finds/deletes elements of the current DOM of your mechanized browser and then on top of that, WWW::Mechanize::Chrome::Webshot which takes a screenshot of your browser contents onto a local file. In short, it renders a URL or a local file onto a, possibly headless, mechanized browser (with all encompassing CSS and JS included), allows some settle time, optionally removing some DOM elements which clutter the view and takes a screenshot of what's currently rendered. I think this is as good as it gets for html2pdf. I mean the browser is the final arbiter on how html renders right? (well, sort of).

    https://leafletjs.com/ is really very good at displaying map tiles, images/satellite or vectors, from various sources (e.g. OpenStreetMap for navigation vector maps or ArcGIS/ESRI for satellite images, both free). It is also very easy to draw polygons, markers, etc. on top of the map using map coordinates. And then allows you to move/pan/zoom interactively. Really cool software. Alas in Javascript.

    And so, in the below script I combine both to get both a self-contained HTML (it requires lefleat.js external dependency) of an interactive map as well as a printout in the form of PDF/PNG.

    Caveat: the output PDF contains only part of the view (increasing the dpi perhaps?) whereas the PNG contains everything the browser window contains. So, a PNG is output instead.

    PS: I had in mind to create a Just another Perl Hacker all over Africa but the polyline is quite some bytes long and I will spare you the bandwidth. Just imagine Just another Perl Hacker sprayed all over the globe.

    PS2: Geo::Leaflet does a good job at exposing the basic functionality of LeafletJS via Perl. But there are lots more features and options for your map. I propose that once you have a basic HTML map, to template it and keep adding more features there. You do not need to keep using Geo::Leaflet. Check the script's output HTML file.

    # by bliako for perlmonks 21/08/2025 use Geo::Leaflet; use FindBin; use File::Spec; use WWW::Mechanize::Chrome::Webshot; my $lat = 11.1; my $lon = 22.2; my $outbase = 'hack'; my $map = Geo::Leaflet->new( id => "myMap", center => [$lat, $lon], zoom => 3, ); # the tiles are coming from: $map->tileLayer( # navigation vector tiles #url => 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', # satellite images url => 'https://server.arcgisonline.com/ArcGIS/rest/services/World_ +Imagery/MapServer/tile/{z}/{y}/{x}', options => { maxZoom => 19, maxNativeZoom => 23, #attribution => 'openstreetmap', attribution => 'esri', }, ); $map->polyline( coordinates => [ [9.0, 20.0], [11.3, 23.1], [12.1, 19.4], ], options=>{color=>'red', weight=>'5'} ); my $map_html = $map->html; my $outdir = $FindBin::Bin; my $map_html_outfile = File::Spec->catfile($outdir, $outbase.'_map.htm +l'); my $FH; open($FH, '>', $map_html_outfile) or die "failed to open file for writing html '$map_html_outfile', +$!"; print $FH $map->html; close $FH; my $map_pdf_outfile = File::Spec->catfile($outdir, $outbase.'_map.png' +); my $shooter = WWW::Mechanize::Chrome::Webshot->new({ 'settle-time' => 10, 'resolution' => '2000x2000', }); my $local_uri = URI->new('file://'.$map_html_outfile); $shooter->shoot({ 'output-filename' => $map_pdf_outfile, 'url' => $local_uri->as_string, 'remove-DOM-elements' => [ {'element-xpathselector' => '//div[id="leaflet-control-container +"]'}, ], 'exif' => {'created' => 'by the shooter'}, }); print "$0 : done, map saved to PDF '$map_pdf_outfile'\n";

    bw, bliako

Improving core performance with SIMD
3 direct replies — Read more / Contribute
by phizel
on Aug 13, 2025 at 08:22
Anyone migrated to the new 'class' way of OOP?
6 direct replies — Read more / Contribute
by stevieb
on Aug 07, 2025 at 05:28

    ikegami recently posted a question that eventually led me to the new perlclass documentation.

    I haven't been following along that much, but it seems intriguing.

    I have a ton of OOP code using the old-school bless() way of doing things, and after a cursory look at this documentation, my gut says that I shouldn't change anything. There have been so many iterations of OOP modules and such, it just seems that it'll keep changing over and over again. However, with that said, I've been around a long time, and considering almost all of my code will still run on 5.8, Perl is known for its backward-compatibility.

    What are your thoughts on this new way of Perl OOP development? Are you embracing it? Are you sticking with the old? For long-time developers, are you considering transitioning?

    Legitimately curious,

    -stevieb

Yet Another Program on Closures ~ Steven Lembark ~ TPRC 2025 - YouTube
1 direct reply — Read more / Contribute
by LanX
on Jul 30, 2025 at 11:37
Perl and the Unix Philosophy (Keynote) ~ David Both ~ TPRC 2025 - YouTube
No replies — Read more | Post response
by LanX
on Jul 27, 2025 at 11:30

Add your Meditation
Title:
Meditation:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":


  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Domain Nodelet?
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this?Last hourOther CB clients
    Other Users?
    Others rifling through the Monastery: (4)
    As of 2026-02-09 13:48 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      No recent polls found

      Notices?
      hippoepoptai's answer Re: how do I set a cookie and redirect was blessed by hippo!
      erzuuliAnonymous Monks are no longer allowed to use Super Search, due to an excessive use of this resource by robots.