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
RFC: Concise OOP Syntax
1 direct reply — Read more / Contribute
by LanX
on Aug 04, 2019 at 20:59
    Dear monastery!

    In continuation to

    I tried to hack a proof of concept for a concise OO syntax

    The basic ideas are that:

    Declaration of instance variable
    • my on the class level with :attributes
    • the TYPE is given right after the my
    • attributes reflect the Moo(se-)model of has keys where possible
    • the assigned values are defaults for the new-constructor
    Access of instance variables inside methods
    • an instance variable x is readable and writable via $$x
    • this is automatically mirrored in $self->{x}
    • $self->{x} is an alternative syntax for the same access
    $self
    • $self is already shifted from @_ and directly available
    Methods
    • all subs declared inside the scope of a class are methods
    • imported subs (like pp) are ignored

    { use Class BLA => ISA-LIST; use Data::Dump qw/pp/; my Int ($x,$y) :has :rw = (10,11); sub set_x { my ($var) = @_; #warn "set_x $$x -> $var \n"; $$x = $var; } sub print_x { print "x = $$x \n"; } sub print_self_x { print "x = $self->{x} (via \$self)\n"; } sub dump { warn '$self:',\$self; warn pp '$self: ', $self; warn pp '$$x: ', $$x; warn pp '$$y: ', $$y; } }

    The implementation is done via a macro expansion from use Class which injects some boilerplate into the head of the class, which handles the creation.

    Injecting is basically done via a source filter or alternatively via Keyword::Simple. NB: just injecting some code doing introspection. No parsing, regexing or modification of the code you see.

    I'm supposing this concise syntax could be used as a front end for all current OO models in Perl and might help offering a stable backwards compatible syntax if it's hardcoded into the engine.

    A rough proof of concept follows here:

    NB: This example is pretty barebone, and not meant to be an alternative to other OO Frameworks, but rather a frontend. It doesn't create accessors and the constructor is only simplistic.

    Comments?

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

parent::versioned supports minimum version specification in parent classes
3 direct replies — Read more / Contribute
by davido
on Aug 04, 2019 at 14:58

    A question came up here recently where a user wished to specify a minimum version for a parent class. base doesn't support specifying minimum versions, nor does parent. base and parent are core Perl modules. I don't think it would be appropriate to add complexity to the core Perl parent by adding this feature.

    Perl facilitates setting minimum versions in use statements:

    use Time::HiRes 1.9;

    use has a compiletime effect, so that code behaves like this:

    BEGIN { require Time::HiRes; Time::HiRes->VERSION(1.9) Time::HiRes->import }

    However, BEGIN blocks require us to consider the order of loading more than we may want to. This doesn't fit the model of keeping simple things simple, if the simple thing we want to do is fairly common.

    For inheritance we can do this:

    package MyAgent; BEGIN { require LWP::Usergent; LWP::UserAgent->VERSION(6.39); # Assert a minimal version. push @MyAgent::ISA, 'LWP::UserAgent'; }

    To me that's too much boilerplate for a common need. Another way:

    package MyAgent; use parent 'LWP::UserAgent'; BEGIN {LWP::UserAgent->VERSION(6.39)}

    But that's a little fragile; someone may refactor and forget to keep the BEGIN block after the call to parent causing things blow up. Sometimes code becomes complex, masking simple mistakes. I would prefer a solution that doesn't require the user to maintain several steps to set up inheritance while assuring minimal versions for parent classes.

    parent::versioned makes the inheritance and compile-time version checking more convenient, and harder to get wrong. I uploaded it to CPAN a few hours ago.

    package Myagent; use parent::versioned ['LWP::UserAgent' => 6.39];

    parent and parent::versioned behave the same for all import lists unless an element in the import list is an array ref. Any array ref passed to the import list will be treated as a tuple that specifies a module name, and a minimum version for that module. Multiple inheritance works like this:

    use parent::versioned ['LWP::UserAgent' => 6.39], ['Mojo::DOM' => 7.2 ], 'Foo::Base';

    This example sets up triple inheritance: two modules that are version checked, and one that is not. As with parent, parent::versioned accepts the -norequire flag.

    The parent module has 100% code test coverage. parent::versioned passes the parent test suite and maintains 100% coverage. It should work as a drop-in replacement for parent, but with the additional functionality exposed by passing an array-ref as part (or all) of the import list. Most of the code is a direct fork from parent. You can use it just like you use parent:

    use parent::versioned qw(Foo);

    I prefer keeping the module small, but if you're interested please do look at the GitHub repository. Patches, issues, and suggestions are welcomed. I hope it becomes useful.


    Dave

Hashes do preserve insertion order after all
5 direct replies — Read more / Contribute
by kikuchiyo
on Jul 31, 2019 at 12:00

    It is often repeated that hashes in Perl do not preserve insertion order. Yet:

    #!/usr/bin/perl use strict; use warnings; use feature qw/say/; my %hoh = ( foo => { value => 'first' }, bar => { value => 'second' }, baz => { value => 'third' }, ); for my $elem (sort values %hoh) { say "value => " . $elem->{value}; }

    Output:

    value => first value => second value => third

    Edit and clarification: I'm not saying that you should ever use this (in fact, I'm saying that you shouldn't, the comments below describe why). It looks like as if it really preserved insertion order, but actually the trick relies on implementation details (memory allocation) you can't (or shouldn't want to) control.

Thx, St. Larry, for the Beauty of Sigils
4 direct replies — Read more / Contribute
by msouth
on Jul 26, 2019 at 13:12
    TL;DR Thanks, Larry, for sigils

    I recently took a job at a Python shop, helping out a friend. I have tried to be patient with the new-to-me language and to realize that there are different ways of doing things and that I should expect some mental friction simply due to my unfamiliarity. But, man, I just can't escape the idea that sigils were a good idea.

    I needed to access a dynamically specified method and run a dynamically specified method against that method's result. Python can't tell the difference between a variable you created that contains a method name and a method name itself. You have to use a function called getattr to cumbersomely retrieve the reference you want. And if you go any deeper than one, it gets ugly very quickly. In my case, I have something like

    getattr(getattr(object_identifier,variable_containing_the_name_of_meth +od_one), variable_containing_the_name_of_method_two)

    (I'm using ridiculously long variable names for clarity about what the variables contain.)

    With sigils to let Perl know that I mean 'grab the contents of the variable and execute the method having that name', I can do:

    $object_identifier->$variable_containing_the_name_of_method_one->$obje +ct_containing_the_name_of_method_two

    With the ridiculous names it may be less clear how much nicer this is, but look at:

    $obj->$var1->$var2

    vs

    gettattr( getattr(obj,var1) , var2)

    I hate going to the inside and working my way out, trying to remember what I picked up along the way.

    I realize that Python people hate having to type sigils all over the place, and that they would find it ironic that this would be the thing I would write in praise of. But I am doing a lot of work right now with dynamic method/attribute names and it really seems to me that Larry made the right call on this.

response to "The Perl Community - a mixed bag of sometimes intollerance and sometimes fantastic help"
4 direct replies — Read more / Contribute
by daxim
on Jul 24, 2019 at 03:19
    mje says he was ridiculed and alludes to intolerance (in the heading):
    GIven my new enthusiasm for the Perl community I dare to post an issue I was having to IRC on #perl-help and it all fell down. [] anyone posting in #perl-help, needs help and I'd forgotten that certain people view this channel as a shooting ground where you pour ridicule on posters "who don't understand their problem" - of course they don't or why would they post on #perl-help. I got some helpful comments but I also got the ridicule.
    That sounds untypical for #perl-help, in which I occasionally participate on both the Q and A sides. (There's my disclosure of bias.) mje did not substantiate, so I went and looked what happened. I did not find ridicule, not even a heated discussion. I notice I am confused.

    I decided to post the log because the Perl community as a whole was blamed, not individuals, and in order to provide accurate first-hand information so that anyone can form their own opinion.

    2019-07-23 18:47:42 mje__ I have a strange warning that I cannot + provide a runnable example for. The code "if (defined($match->{in_co +ntrol}) && ($match->{in_control} == 1))" ends up issuing a warning sa +ying "Use of uninitialized value in numeric eq (==)" but I cannor und +erstand how this can happen. $match is defined and even if it wasn't +I'd expect a warning saying trying to deref and undef. I don't think +it is autovivification as my tests show 2019-07-23 18:47:42 mje__ defined doesn't do this. Any ideas? 2019-07-23 18:48:44 mje__ Perl 5.24.4 2019-07-23 18:56:48 alh in_control could have operator overloa +ding 2019-07-23 18:57:00 alh Alternatively, the warning isn't happe +ning where you think it is which seems more likely 2019-07-23 18:57:21 alh Line numbers can be wrong if it's part + of a giant if () { } elsif () { } else {} block 2019-07-23 18:57:35 alh So it could be a different conditional + triggering it 2019-07-23 18:58:54 mje__ Simple hash ref, so no overloading. Th +e warning seems pretty good since the line mentioned contains an == 2019-07-23 18:59:58 alh Is it part of a multi-line conditional +? 2019-07-23 19:00:02 alh Can you show us more of the code 2019-07-23 19:00:08 mst the warning is happening somewhere els +e 2019-07-23 19:00:10 mst 99% chance 2019-07-23 19:02:28 mje__ Here is what it looks like now https:/ +/pastebin.com/fsTnLRaS, the only thing changed from when the warning +was output was defined changed to exists 2019-07-23 19:02:29 Repaster Repasted text from mje__: http +://perl.bot/p/mmydbc 2019-07-23 19:03:03 alh That's using exists 2019-07-23 19:03:04 alh not defined 2019-07-23 19:03:07 alh So it can exist and e undef 2019-07-23 19:03:13 alh Which is your problem 2019-07-23 19:03:46 mje__ I said, the original was defined when +the warning was issued and what I pasted was afterwards when it was c +hanged to exists 2019-07-23 19:04:48 mje__ the change from defined to exists is l +ikely wrong but the warning came from the defined case 2019-07-23 19:06:29 alh Can you show more context? 2019-07-23 19:12:38 mje__ https://pastebin.com/sYC3XR31 2019-07-23 19:12:39 Repaster Repasted text from mje__: http +://perl.bot/p/t8p5x4 2019-07-23 19:16:48 alh before line 18 warn the value of $exis +ting_match->{in_control} 2019-07-23 19:17:01 alh Beore line 14 do the same for $events{ +$event_id}->{event_status_id} 2019-07-23 19:17:05 alh That may be where the real warning is +coming from 2019-07-23 19:22:02 mje__ ok, so the crux of the opinion is the +line number is possibly wrong and so other == tests could be at fault +? 2019-07-23 19:24:47 mst the crux of the opinion is "you're con +fused about *something* 2019-07-23 19:24:51 alh Yes. And you can figure out by adding +debug everywhere 2019-07-23 19:24:57 mst I'd probably apply warn + Data::Dumper 2019-07-23 19:25:12 alh If you think $x->{foo} == 1 is warning +, warn reight before that conditional, warn inside that conditional, +warn after that conditional 2019-07-23 19:25:31 alh Where the unintialized value warning a +ppears in regards to what you've added should help you track it down 2019-07-23 19:28:21 Grinnz source filters are also known to screw + up line numbers 2019-07-23 19:29:32 mst oh. yes. if you have 'use Switch;' in +that code, you can assume all line numbers are wrong 2019-07-23 19:30:00 Grinnz (and in that case, that it also caused + the error) 2019-07-23 19:31:00 mje__ Unfortunately this is code working on +live data and although it is possible to play it back the playback is +n't EXACTLY as it was live. We don't use Switch. I hadn't considered +the warning line number was wrong so I've got something to work on. T +hanks 2019-07-23 19:31:43 Grinnz there are still some core bugs that ma +ke wrong line numbers too 2019-07-23 19:34:35 alh Also if a comment like this is anywher +e in the file: 2019-07-23 19:34:38 alh # line 5 10 2019-07-23 19:35:07 alh Or even: 2019-07-23 19:35:09 alh # line 10 2019-07-23 19:35:12 mst honestly I still thing you're just con +fused about the data
    Timestamps are in Europe/Vienna timezone. The log is complete and unmodified. I'd appreciate replies of confirmation that it is so.
Off by one key... or yet another example of why strict and warnings are always a good idea (even for "one-liners")
3 direct replies — Read more / Contribute
by atcroft
on Jul 17, 2019 at 20:02
    "Just sit right back and you'll hear a tale
    a tale of a fateful trip,
    that started from this simple term,
    aboard this single slip."

    Please, dear reader, learn from my mistake. (It will likely be less painful than repeating it yourself.)

    Earlier today I did something I have done dozens of times before-created a command-line perl script (a "one-liner", for some very long definition of a "line") to create a file containing a subset of messages from a set of compressed logs, with the intent of pulling them back to my machine for additional processing. I launched it in a screen session, saw it was running and would take some time to begin generating output, and stepped away for lunch. I was rather surprised, however, when I returned to find it throwing "No space left on device" messages-especially when I looked to find that the partition's previous 40GB of free space was now zero. After cleaning up the space, I began trying to find the cause.

    In digging through my code, I almost missed the issue-a line of the form if ( $str -~ m/some pattern/ ) { print $str; }. Notice the issue? An off-by-one-key error from a en.US keyboard, where [-/_] and [=/+] are beside one another. This was compounded by skipping the well-espoused logic of use strict; use warnings; because it was a "one-liner" (where "-Mstrict -Mwarnings" would have added a mere 19 characters in length). Had I have done so, instead of:

    # made-up example with actual issue $ perl -le 'my $str = q{zxcv}; if ( $str -~ m/some pattern/i ) { print + $str; }' zxcv
    I would have seen:
    # made-up example with actual issue, with strict and warnings $ perl -Mstrict -Mwarnings -le 'my $str = q{zxcv}; if ( $str -~ m/some + pattern/i ) { print $str; }' Use of uninitialized value $_ in pattern match (m//) at -e line 1. Argument "zxcv" isn't numeric in subtraction (-) at -e line 1. zxcv
    Seeing unexpected output like that (that early) would likely have resulted in my cancelling the command to investigate, and thus likely not filling up 40GB of storage.

    I know this will probably be like the tone after the television broadcast day ended to a sleeping viewer, but maybe, just maybe, it will help someone. In the end, learning from others' mistakes is often less painful (but not necessarily as memorable).

    (And yes, I do use the the duo on most every script I write-just not always on "one-liners".)

File::Spec->case_tolerant() is broken
No replies — Read more | Post response
by afoken
on Jul 13, 2019 at 20:42

    This is the long version of Re^2: open file error and Re^5: Unify windows filenames (from ten years ago), triggered by Re^2: perl script to compare two directories, plus a little bit of bean counting on the code for non-Unix systems.


    File::Spec has a severe design problem that will cause wrong results on modern Unix systems (*BSD, Linux, MacOS X). It assumes that all filesystems are equal on any Unix operating system. And this assumtion is plain wrong since decades:

    • Any modern Unix can mount FAT and NTFS, two filesystems that are commonly used in a case insensitive way, and so they are generally mounted in a case insensitive way.
    • At the same time, the native filesystems (ext2/3/4, btrfs, ufs, ufs2, zfs) are mounted in a case sensitive way.
    • ISO9660 is case insensive, Rock Ridge extensions make it case sensitive, so a CDROM / DVD-ROM / Blueray mounted on a Unix system may be either case sensitive or case insensitive.
    • Mounting a case insensitive FAT filesystem on Linux is quite common: All Raspberry Pis boot from a FAT partition later mounted as /boot
    • And to drive people mad, Linux 5.2 also can mount its native ext4 filesystem in a case insensitive way.

    To make things worse, the code for at least Windows and cygwin is broken, too.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
"open" Best Practices
10 direct replies — Read more / Contribute
by haukex
on Jul 11, 2019 at 10:41

    open Best Practices

    open my $fh, '<', $filename or die "$filename: $!";

    You will see styles of open such as "open FILE, $filename;" or "open(LOG, ">$filename") || die "Could not open $filename";" in many places. These mainly come from versions of Perl before 5.6.0 (released in 2000), because that version of Perl introduced lexical filehandles and the three-argument open. Since then, these new features have become a best practice, for the reasons below.

    1. Use Lexical Filehandles

    Instead of open FILE, ..., say: open my $fh, ....

    Lexical filehandles have the advantage of not being global variables, and such filehandles will be automatically closed when the variable goes out of scope. You can use them just like any other filehandle, e.g. instead of print FILE "Output", you just say print $fh "Output". They're also more convenient to pass as parameters to subs. Also, "bareword" filehandles like FILE have a potential for conflicts with package names (see choroba's reply for details).

    2. Use the Three-Argument Form

    Instead of open my $fh, ">$filename", say: open my $fh, '>', $filename.

    In the two-argument form of open, the filename has to be parsed for the presence of mode characters such as >, <+, or |. If you say open my $fh, $filename, and $filename contains such characters, the open may not do what you want, or worse, if $filename is user input, this may be a security risk! The two-argument form can still be useful in rare cases, but I strongly recommend to play it safe and use the three-argument form instead.

    In the three-argument form, $filename will always be taken as a filename. Plus, the mode can include "layers", so instead of having to do a binmode after the open, you can just say e.g. open my $fh, "<:raw", $filename.

    3. Check and Handle Errors

    open my $fh, '<', $filename;                          # Bad: No error handling!
    open my $fh, '<', $filename  || die ...;              # WRONG!1
    open my $fh, '<', $filename  or die "open failed";    # error is missing info
    
    open my $fh, '<', $filename  or die "$filename: $!";  # good
    open(my $fh, '<', $filename) or die "$filename: $!";  # good
    open(my $fh, '<', $filename) || die "$filename: $!";  # works, but risky!1
    
    use autodie qw/open/;  # at the top of your script / code block
    open my $fh, '<', $filename;                          # ok, but read autodie!
    

    You should check the return value of the open function, and if it returns a false value, report the error that is available in the $! variable. It is best to also report the filename as well, and of course you're free to customize the message as needed (see the tips below for some suggestions).

    1 It is a common mistake to use open my $fh, '<', $filename || die ... - because of the higher precedence of ||, it actually means open( my $fh, '<', ($filename || die ...) ). So to avoid mistakes, I would suggest just staying away from || in this case (as is also highlighted in these replies by AM and eyepopslikeamosquito).

    Note that open failing does not necessarily have to be a fatal error, see some examples of alternatives here. Also, note that the effect of autodie is limited to its lexical scope, so it's possible to turn it on for only smaller blocks of code (as discussed in kcott's reply).

    4. Additional Tips

    • Make sure that the filename you're opening always matches the filename in the error message. One easy way to accomplish this is to use a single variable to hold the filename, like $filename in the above examples (as described in Eily's reply).
    • Consider putting the filename in the error message in quotes or similar, such as "'$filename': $!", so that it's easier to see issues arising from whitespace at the beginning or end of the filename (as suggested in Discipulus's reply).
    • In addition, consider adding even more useful details to your error message, such as whether you're trying to read or write from/to the file, and put quotes around $! as well, so it's easier to tell everything apart (as suggested by haj).
    • On Windows, consider also displaying $^E as part of the error message for more information (as suggested in Discipulus's reply).
    • If you're setting global variables that will affect reading the file, like $/, it's best to use local in a new block (as mentioned in stevieb's reply).
    • Remember that it's possible for multiple processes to access the same file at the same time, and you may need to consider a way to coordinate that, such as file locking (as mentioned in davido's reply).

    Fellow Monks: I wrote this so I would have something to link to instead of repeating these points again and again. If there's something you think is worth adding, please feel free to suggest it!

    Update 2019-07-12: Added section "Additional Tips", mentioned bareword filehandles, and added a bit more on autodie. Thanks to everyone for your suggestions! 2019-07-13: Added more suggestions from replies, thanks!

Perl growth in India, China, Russia, Germany and Romania
No replies — Read more | Post response
by Anonymous Monk
on Jul 05, 2019 at 17:22
    A recent comment on r/perl claims that Perl is growing "nicely" in several key nations of Europe and Asia. The author cites their own experience and google as sources:

    "If my google alerts don't mislead me Perl is growing nicely in India, China, Russia and Germany... I can testify to growth in Romania, based on the pressure the HR people are putting on me to recruit people from the places I worked before."

    I share this good news hoping it will make you happy; and also seeking confirmation from those who know where to find such information.

    https://old.reddit.com/r/perl/comments/btt9e9/every_time_i_hear_perl_is_dead_i_think_of_the/eqdno37/

RFC: Basic Testing Tutorial
3 direct replies — Read more / Contribute
by hippo
on Jul 05, 2019 at 04:24

    Fellow monks, I humbly submit this basic testing tutorial for review. All comments are welcomed - feel free to send a personal message instead of posting a reply if you so wish.

    Rationale: There are only a couple of tutorials about testing here in the Monastery and both of them concern mocking. What is missing IMHO is a nice, gentle introduction to testing with some example code to help take out that first massive leap on the learning curve. I was late to the testing game in Perl and it's a regret that I did not get into it sooner. Hopefully this will be useful to those in a similar situation.

    Updates: Add examples of is, like and skip as suggested by haukex and stevieb. Add mention of the t/ directory with prove as suggested by haukex. Add mention of done_testing() as suggested by many respondents.

    Now published in the Tutorials section as Basic Testing Tutorial.


    Basic Testing Tutorial

    One of the widely-acknowledged strengths of Perl is its use of testing as a cornerstone. Almost every module on CPAN comes with its own test suite and there are many modules available to help with testing and to make it as painless as possible. For those looking to add testing to an existing code base or looking to get started with TDD here is a guide to the basics.

    Test scripts and TAP

    Tests in perl are, at their heart, simple scripts which evaluate a series of boolean expressions and output the results. No doubt you could write such a script in a few minutes and the output would tell you how your test criteria have fared. However, there is a standard form to the output of the test scripts which is best adhered to. This is the Test Anything Protocol or TAP for short. By having your script output the results in this format, they can be analysed by a wealth of other programs called TAP Harnesses to provide summary data, highlight failures, etc.

    TAP is an open protocol but was written for Perl originally. It is very simple at its heart. The first line of output is a range of test numbers starting at 1 and each subsequent line consists of three fields: a pass/fail flag which is either "ok" or "not ok", the number of the current test and an optional description of that test. Thus a script containing a single, passing test might output this when run:

    1..1 ok 1 The expected file "foo" is present

    A first test script

    So, let's write a simple test script which will output TAP. Suppose we want to check that nobody is running our code far in the past. Here would be a trivial test.

    use strict; use warnings; use Time::Piece; print "1..1\n"; print localtime(time)->year > 1999 ? 'ok' : 'not ok'; print " 1 Not in a previous century\n";

    When we run this without time-travelling we see this output:

    1..1 ok 1 Not in a previous century

    and we see that our test has passed.

    Test modules

    Of course there are modules on CPAN to help with testing. The simplest of these is, appropriately enough, Test::Simple which is a core module. It is little more than a wrapper with 2 handy functions to ensure that your TAP output is in the correct format. We can rewrite our simple century test with this module:

    use strict; use warnings; use Time::Piece; use Test::Simple tests => 1; ok localtime(time)->year > 1999, 'Not in a previous century';

    Now there are no print statements because the module takes care of all the output. The tests => 1 on line 4 sets the number of tests we expect to run, so we no longer need to print "1..1\n" ourselves. Similarly, the ok function evaluates the first argument as a boolean expresssion and outputs the correct TAP line as a result. The second argument is the optional description.

    Technically it is optional but I would encourage you very strongly to include a description for any test. If you have a script with say 50 tests in it and test 37 fails but has no description, how will you know what is wrong? Make life easy for yourself (and your collaborators and even the users) by describing each test in the TAP output.

    Other testing functions

    While the ok function is useful, the output is a simple pass/fail - it doesn't say how it failed. If our century test fails we don't know what year it thinks it is. For that we would need to write more code or use code someone else has written. Fortunately there is a plethora of other testing modules to choose from, the most common of which is Test::More (also in core). This gives us a heap of other functions so that we can easily perform different types of evaluations and receive better feedback when they fail.

    Let's use Test::More and its handy cmp_ok function in our script.

    use strict; use warnings; use Time::Piece; use Test::More tests => 1; cmp_ok localtime(time)->_year, '>', 1999, 'Not in a previous century';

    Note that I've introduced a bug here (using _year instead of year) so that the test will likely fail. Now our test output looks like this:

    1..1 not ok 1 - Not in a previous century # Failed test 'Not in a previous century' # at /tmp/bar.t line 6. # '119' # > # '1999' # Looks like you failed 1 test of 1.

    We can see at a glance what is being tested and that the year we actually have (119) is clearly wrong so we need to fix the bug. All lines in TAP which start with a hash (#) are comments for the reader: Test::More and friends use this to give us verbose reports about how things have gone wrong.

    There are a number of other useful comparator functions in Test::More such as is for simple equality, like for regex and so on. These are fully explained in the Test::More documentation, but their usage is quite straightforward. Let's add a couple of other tests to see how they are used.

    use strict; use warnings; use Time::Piece; use Test::More tests => 3; my $now = localtime (time); cmp_ok $now->_year, '>', 1999, 'Not in a previous century'; is $now->time, $now->hms, 'The time() and hms() methods give the same +result'; like $now->fullday, qr/day$/, 'The dayname ends in "day"';

    There are also control flow structures such as skip to avoid running tests in certain circumstances such as an invalid underlying O/S or absence of a particular module. We could use this here to skip the test of the dayname if a non-English locale applies.

    use strict; use warnings; use Time::Piece 1.31_02; use Test::More tests => 3; Time::Piece->use_locale; my $now = localtime (time); cmp_ok $now->_year, '>', 1999, 'Not in a previous century'; is $now->time, $now->hms, 'The time() and hms() methods give the same +result'; SKIP: { skip 'Non-English locale', 1 unless substr ($ENV{LANG} // 'en', 0, + 2) eq 'en'; like $now->fullday, qr/day$/, 'The dayname ends in "day"'; }

    Further still there are other modules in the Test::* namespace to help with all manner of scenarios.

    Working to a plan

    It may be the case that the precise number of tests in the script is not known or may change frequently. In those situations, specifying the number of tests like use Test::More tests => 3; can become unwieldy or problematic. Instead we can just use Test::More; and then specify the plan later.

    One method of doing this is to call plan () as a stand-alone statement. If the number of tests is dependent on an array which is only computed at run time we could write

    plan tests => scalar @array;

    once the array has been populated.

    Another approach is to use done_testing (scalar @array); but as its name suggests this must only be called after the final test has been run. The number of tests can even be omitted entirely here but that removes the check that all the tests expected have indeed run, of course.

    done_testing (); exit;

    Using a harness

    If you have installed a module from CPAN you will probably have noticed the test phase running. You can use the same harness on your own test scripts by running the prove command. By default this condenses the results of tests and at the end provides a summary of which tests in which files have failed, how long the run took, etc. eg:

    $ prove /tmp/bar.t /tmp/bar.t .. 1/3 # Failed test 'Not in a previous century' # at /tmp/bar.t line 8. # '119' # > # '1999' # Looks like you failed 1 test of 3. /tmp/bar.t .. Dubious, test returned 1 (wstat 256, 0x100) Failed 1/3 subtests (less 1 skipped subtest: 1 okay) Test Summary Report ------------------- /tmp/bar.t (Wstat: 256 Tests: 3 Failed: 1) Failed test: 1 Non-zero exit status: 1 Files=1, Tests=3, 0 wallclock secs ( 0.03 usr 0.00 sys + 0.06 cusr + 0.01 csys = 0.10 CPU) Result: FAIL

    This is particularly useful for larger projects with many scripts/modules each of which has many tests. If prove is run with no arguments it will look for files matching t/*.t and run all of those in sequence.

    Test Driven Development

    Now that you can test your code you can consider TDD as a methodology. By writing the tests before the code you are setting out what you expect the code to do - it's a formal representation of the specification. Doing so is a skill in itself and many people make a career out of being a tester.

    See Also

Refactoring just to refactor?
8 direct replies — Read more / Contribute
by Lady_Aleena
on Jun 27, 2019 at 20:25

    Hello everyone.

    I wrote the following subroutine recently using map and grep in the BLOCK LIST style. Line 4 had gotten long, so as you can see, I put the map, grep, and sort on separate lines. After doing that and seeing how good it looked, I began thinking about all the times I used map and grep in the EXPR, LIST format and that I may want to rewrite all of them.

    Most, if not all, other people I have chatted with over my years here use the BLOCK LIST format instead of the EXPR, LIST format. I stubbornly stuck with the EXPR, LIST format. Now I am not sure if I am thinking of refactoring to use the format is normally used or if this is refactoring just to refactor. So, I am on the fence on whether or not I should do it.

    What do you think?

    No matter how hysterical I get, my problems are not time sensitive. So, relax, have a cookie, and a very nice day!
    Lady Aleena
What does your old Perl code look like?
12 direct replies — Read more / Contribute
by haukex
on Jun 17, 2019 at 16:47

    To celebrate leveling up today, I dug through my code archives and picked one of the oldest full scripts I could find. Although I wrote my first Perl scripts in about 1995, unfortunately those appear to be lost on an old MacBook that won't boot up anymore. Maybe I'll get around to recovering it someday, but for now, here's a script with a modification date of October 1998.

    I guess we all have to start somewhere ;-) Can you spot all the bad practices? What does your old Perl code look like?

Is Perl on the Raspberry Pi worth it?
11 direct replies — Read more / Contribute
by stevieb
on Jun 16, 2019 at 22:48

    Since 2016, I've been doing a lot of work regarding the Raspberry Pi with Perl.

    Everyone has their interests, engagements and excitement, but I've been working very hard on a significant test platform for my Perl software against the Pi, but I'm starting to understand that there's few others besides me who uses it.

    Yes, it's totally a hobby, and I'm happy doing the datasheet translation, soldering of ICs, calculation of resistors and capacitors etc, but is there any real reason to continue to do this for Perl if it isn't being used by anyone else? I enjoy doing this; I enjoy writing the low-level code and bringing it to front so that there's a Perl API. I enjoy this community.

    Is it only us old-timers here anymore? What if I sold hardware kits to put together? Does it matter anymore?

    I am not saying I will stop writing code for stuff for the Pi in Perl (because Perl is, and will always be my favourite language), I'm just questioning my moves forward. Python clearly has its shit together, but script writers in Python are (typically) not that great, so I'm at a loss here.

    This is a mind-fsck question, so answers be damned. I'm as lost as you are ;)

The XLSX Perl modules have a simple problem
2 direct replies — Read more / Contribute
by BerntB
on Jun 14, 2019 at 05:27

    Hi,

    I write here so people can find the problem when they search.

    XLSX is a Zipped file format with XML. When some applications (it seems Libre Office and others) save as XLSX, they set the dimension size bad. And all the Perl modules I could find failed on that.

    A typical example is that in a sheet file (like xl/worksheets/sheet1.xml, after unpacking) you find

    <dimension ref="G2296"/>
    The Spreadsheet::ParseXLSX module (and others) seems to expect something like "A1:G2296" there. It sets the values for Min/Max Column/Row wrong.

    Spreadsheet::ParseXLSX use Spreadsheet::ParseExcel objects for worksheets etc, so the API is compatible. The get_cell() method there just returns undef if the asked cell is outside the dimension boundaries (which it of course is).

    A temporary fix would be to set the MinRow/MinCol/MaxCol/MaxRow values in the spreadsheet object:

    for my $s ($parsed_document->worksheets() ) { $s->{MinRow}=0; $s->{MinCol}=0; # $s->{MaxCol}=100; (Edit: Not needed) # $s->{MaxRow}=7000; (Edit: Not needed) ... my $c = $s->get_cell($row, $column); ...

    I don't really know enough about the XLSX format. I assume the easy solution is a few lines. Like keeping up max/min column numbers when reading the file. I'll send in a bugfix when I get so far. (With tests so I don't get any grumbles back this time. :-) )

Coding Challenge: Find 6 sided polygon covering 4x4 grid
5 direct replies — Read more / Contribute
by LanX
on May 31, 2019 at 09:38
    There was a recent request* for coding challenges, and I stumbled over this quiz.

    Given are 16 equidistant points in a 4x4 grid.

    The coordinates are given with with @set = ([0,0],[0,1],...[3,3])

    y 3 o o o o <- [3,3] 2 o o o o 1 o o o o 0 o o o o <- [3,0] 0 1 2 3 x

    Task: Find a polygon with 6 edges, crossing each grid-point exactly once.

    @polygon = ([x0,y0],...[x6,y6])

    Example: the edge ([-1,4],[3,0]) is crossing 4 points.

    Hints:

    • The vertices [x_i, y_i] don't need to be part of the integer grid.
    • There is a solution where the polygon is closed - i.e. [x0,y0]=[x6,y6] .
    • Use fractions or a scaling factor in case floating-point accuracy is posing problems.

    Disclaimer: I didn't code it yet, but I saw existing solutions.

    Have fun! :)

    PS: Extra points for generalized solutions for bigger grids. While this can be solved with paper and pen, I'm expecting code to solve it.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

    update

    Changed orientation of coordinate system to have 0,0 at the left lower corner like in math convention.

    your questions:
    • bliako: Are polygon sides allowed to intersect? -> Yes !
    • bliako: Aren't all polygons closed? -> in math: yes, in computer graphics: depends ;-)
      s/Polygon/Polygonal_chain/ if you want.
    I'll be off for the weekend, sorry if I can't reply earlier.

    footnotes

    ) or rather Polygonal_chain

    *) See Coding challenges to PM


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, 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, 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.