UP / HOME

Ara

Table of Contents

Ara is a simple cli program that prints Covid-19 stats. Currently it only prints India's Covid stats.

Documentation

ara by default will first look for the file in $XDG_CACHE_HOME, if that is not set then HOME/.cache is used, the file name is assumed to be ara.json, there is currently no option to override this or the full path.

If you run ara on OpenBSD then it should use OpenBSD::Unveil.

Default logic is to check if the file is available locally & if it's not older than 8 minutes, in any other case it fetches the latest data. This can be controlled with local & latest option.

The file is downloaded over a secure connection & the server's identity is verified.

Options

local

This option forces ara to use the data available locally, it will only override this option when the file doesn't exist on disk.

latest

This will force ara to fetch the latest data.

Note: local & latest option cannot be used together, ara will print a warning & latest option will be ignored.

notes

Only state notes will be printed if this option is passed.

rows

rows option takes an integer as argument which can be passed as --rows n, where n is an integer.

ara will only print maximum these many number of rows, if you pass 0 or a negative number then ara will ignore it & print all the rows.

showdelta

This will show delta values for every row, default is to show delta only on rows that were updated "Today".

Note: This can be disabled by autohide option.

nodelta

This will remove delta values from every column.

nototal

This will remove the "Total" or "India" row from the table.

hide option should be used for this purpose, this option is only kept for backwards compatibility.

nowords

"Confirmed", "Recovered" & "Deaths" column format numbers in words. For example, "1.6 lakhs" instead of "1,60,000" which makes it easier to read. This option will disable this behaviour.

"Active" column doesn't format numbers in words because it's alignment is set as "right" & formatting it this way doesn't look good. There is currently no option to change this behaviour.

autohide

This will automatically hide some columns if the term size is smaller than expected, it's just a bunch of `if' blocks.

push @to_hide, "updated" if $t_columns < 110;
push @to_hide, "active" if $t_columns < 100;
undef $show_delta if $t_columns < 80;
$no_delta = 0 if $t_columns < 80;

Currently (2020-08-03) it's just these lines pasted above but that might change so look at the source for latest rules.

hide

hide is able to hide states & columns from the table, the values should be space seperated like --hide active "last updated" recovered. These are case sensitive & should be lowercase.

Arguments can be passed as they're printed, for example --hide "jammu and kashmir" is equivalent to --hide jk because "JK" is what's printed on the table.

Only "States" & "Notes" column cannot be hidden, ara will print a warning if you try to do so.

Note: "updated" is aliased to "last updated", so you can pass --hide updated & it would hide the "last updated" column.

Note: The feature to get space seperated values is marked as experimental in Getopt::Long so the behaviour can change in future, worse even get removed. To guarantee backwards compatibility pass each value by itself like --hide jk --hide active, this is equivalent to --hide jk active.

  • Implementation

    %hide hash is created from @to_hide which was created from user arguments by Getopt::Long.

    undef @hide{ @to_hide }
        if scalar @to_hide;
    

    %hide contains values of @to_hide as keys & the value to those keys is not defined, hence undef. This one line says Perl to "undef these keys from the hash %hide" where these refers to the values of @to_hide. This will fail if @to_hide is empty so we have to check for that.

    Alternatively we can do @hide { @to_hide } = () which works even if @to_hide is empty & does the same thing otherwise, this looks more cryptic so I use the first way.

    To check if a specific column is to be hidden or not we use exists like exists $hide{something}.

    There are other ways of doing this & maybe those would be better, I didn't test which one was the best.

    • Columns

      To make hide work we put create @columns & push columns to it unless the user has asked to hide it.

      my @columns;
      
      push @columns, 'Confirmed' unless exists $hide{confirmed};
      push @columns, 'Active'    unless exists $hide{active};
      
    • States

      The whole block is skipped if the user has asked to hide the state. As said above, statecode is also check if that's what is printed in the table which is true only if length $state > 16. There is no good reason for not checking statecode for everything.

      next
          if exists $hide{lc $state}
          # User sees the statecode if length $state > 16 so we also match
          # against that.
          or ( length $state > 16
                   and exists $hide{lc $statewise->[$i]{statecode}});
      

show

show also accepts space seperated values & just like in hide's case it's experimental & can change in future.

show will only show states that are passed. For example, --show jk will only print data for Jammu & Kashmir. If both show & hide is used for states then hide is ignored. show for states can be used with hide for columns.

  • Implementation

    show's implementation is similar to hide's. %show hash is created from @to_show.

    undef @show{ @to_show }
        if scalar @to_show;
    

    If user has used show then hide is ignored, this is achieved by an if-else block. This also means that invalid values in state would cause hide to be ignored, for example passing --show invalid wouldn't match anything but hide will still be ignored. This is intentional.

    if ( scalar @to_show ) {
        next
            unless exists $show{lc $state}
            or ( length $state > 16
                 and exists $show{lc $statewise->[$i]{statecode}});
    } else { ... }
    

help

help will print help for ara which will have little information about all these options listed above.

  • nototal was removed from help because hide option does the same thing & is recommended.

Cross-platform compatibility

Previously ara had OpenBSD specific code & would simply fail to run on other OSes, now it runs on all platforms. There is still OpenBSD specific code but it's used only when ara detects to be running on OpenBSD.

use constant is_OpenBSD => $^O eq "openbsd";
require OpenBSD::Unveil
    if is_OpenBSD;
sub unveil {
    if (is_OpenBSD) {
        return OpenBSD::Unveil::unveil(@_);
    } else {
        return 1;
    }
}

is_OpenBSD is a constant so the if-else block is optimized at compile time. Another way would be to define the sub inside the if-else block which is what I did initially but that is not the same thing as this.

You cannot define sub like that in Perl because this step happens at compile time & so the if-else block is ignored, which means the code will be equivalent to else block being true all the time because that's what comes later.

if (is_OpenBSD) {
    require OpenBSD::Unveil;
    OpenBSD::Unveil->import;
} else {
    sub unveil { return 1; }
}

Above code block will override the unveil sub to be return 1; everytime, this was fixed in commit 245aebe3da915afc0feafc7257f025e2e66a987f.

This will still fail on OpenBSD if users don't have OpenBSD::Unveil in @INC, this shouldn't be an issue with Perl in base but if user runs custom Perl then it might not be in @INC, in that case user is expected to fix this by adding the path to OpenBSD:: in @INC.

Demo Videos

Andinus / / Modified: 2021-04-02 Fri 21:29 Emacs 27.2 (Org mode 9.4.4)