DEV Community

🌌 Sébastien Feugère ☔
🌌 Sébastien Feugère ☔

Posted on • Updated on

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

Prerequisites

apt install libmojolicious-perl

touch mtime.pl && chmod +x mtime.pl
Enter fullscreen mode Exit fullscreen mode

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
#!/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;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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~"
]
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode
  • 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;
Enter fullscreen mode Exit fullscreen mode

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');
};

Enter fullscreen mode Exit fullscreen mode

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.

Top comments (2)

Collapse
 
manchicken profile image
Mike Stemle

This is some very clean and expressive modern Perl, and the use of Mojo is outstanding.

I also really enjoy a lot of the ancillary modules which come with mojo, such as Mojo::Collection, Mojo::File, and of course the user agent.

Thank you so much for sharing.

Collapse
 
smonff profile image
🌌 Sébastien Feugère ☔ • Edited

Thanks for the encouragement. This is a great warmup in my heart.