A concise mtime sorted directory listing application

Today we will focus on a simple task: listing the files contained in a directory, sort them by modification time (see mtime) and display the result in a JSON array.

We are gonna use Mojo::File for the file system part and Mojolicious::Lite to expose those data on a simple but effective JSON API.

This blog post got first published on dev.to in february 2020

Prerequisites

apt install libmojolicious-perl

touch mtime.pl && chmod +x mtime.pl

The code

Our lite application is separated in two main parts:

  • a helper, the get_paths_by_ctime() method
  • an unique route that returns an array reference containing our file list

Let's see a first version of the script:

#!/usr/bin/env perl

# Automagically import Mojo::Base -base
use Mojolicious::Lite -signatures;
use Mojo::File 'path';

helper get_paths_by_mtime => sub {
  my $files_list = path( '/tmp' )->list;

  # Returns an array of Mojo::File
  my @files = sort { $a->stat->mtime <=> $b->stat->mtime }
    map { $_ } $files_list->each;

  # Returns an array of paths sorted by modification date
  return map { $_->realpath->to_string } @files;
};

get '/' => sub( $c ) {
  $c->render( json => \$c->app->get_paths_by_mtime );
};

app->start;

Starting the app

Mojolicious comes with the morbo HTTP and WebSocket development server

morbo mtime.pl
Server available at http://127.0.0.1:3000

If you visit this adress in your browser, it should display the list of files contained in the /tmp directory of your computer. On mine it returns:

[
  "/tmp/sddm-:0-RweLaZ",
  "/tmp/xauth-1000-_0",
  "/tmp/cpan_install_3QQI.txt",
  "/tmp/!home!sfeu!.emacs.d!savefile!ido.hist~",
  "/tmp/!home!sfeu!.emacs.d!savefile!recentf~",
  "/tmp/!home!sfeu!dev!processing-perl!etc!crontab~",
  "/tmp/!home!sfeu!.emacs.d!personal!custom.el~",
  "/tmp/!home!sfeu!dev!processing!.env~",
  "/tmp/liste_desabonnement_20200218.csv",
  "/tmp/!home!sfeu!dev!processing!.git!COMMIT_EDITMSG~",
  "/tmp/!home!sfeu!scripts!mtime.pl~"
]

It is just a bunch of temporary files, mostly those I edit in my text editor. Note that the directories and special files (., ..) are not listed.

Releasing the power of Mojo::File

Retrieving a list of files

Mojolicious provide a lot of utilities that makes your Perl programmer life almost bearable. Note that Mojolicious needs zero dependencies, this is the reason it comes with a great toolkit for doing simple tasks. Mojo::File is one of those. Let's see how to use it.

use Mojo::File 'path';

By using Mojo::File and loading it's path function, you will be able to construct a Mojo::File object by passing it any kind of path:

my $files_list = path( '/tmp' )->list;

The list method called on a Mojo::File directory will return all files contained in this directory. The returned object is a Mojo::Collection object, an array-based container for collections.

Sorting the objects by modification date

To read the following code, start by the end.

my @files = sort {
  $a->stat->mtime <=> $b->stat->mtime
} $files_list->each;
  • first, you return all the elements of the collection by using each
  • it will return an array, that can be passed to sort

sort is being passed a comparison function. In our case we will compare by mtime (modification time). The reason we can access to mtime from the array of Mojo::Files is that this module got it's own stat method that returns itself a File::stat object for the path. This is where the magic happend!

Retrieving the files paths

By using a simple map function and the Mojo::File chainedrealpath->to_string methods we will transform our array of objects to a simple array of strings:

return map { $_->realpath->to_string } @files;

The route

The result of our helper is then rendered as JSON (must be passed to the front as an arrayref).

Making it even more concise

Marcus Ramberg explained me how to make this even more concise by using Mojo::Collection->sort

The helper can be rewritten:

helper get_paths_by_mtime => sub {
  return path( '/tmp' )->list
    ->sort( sub { $a->stat->mtime <=> $b->stat->mtime })
    ->map('realpath');
};

Conclusion

The conciseness and readability of this program is quite surprising thanks to the effective tools used.

I hope you enjoyed reading this presentation and that it gave you the wish to use the formidable Mojolicious framework and it's utilities.

Acknowledgments

Thanks to Marcus Ramberg who read this, improved and simplified the code.

Leave a comment

About Sébastien Feugère

user-pic I am a Perl culture enthusiast since 2011. Currently working at a french opinion institute, I try to build tools using the Perl toolkit, but also Debian, LXC and a bit of JavaScript. Other hobbies: Blogger of dreams about art school (FR) Professional can opener of a teenage cat Psychotherapist for sad computers, would wrap them in blankets and make them tea Translator of a book about net.art (FR) Go to my Gitlab account to discover more.