In these modern times, with billions of people in the world, Santa needs a modern system to keep track of his naughty/nice list. Lucky for Santa, modern Perl has a modern web framework, Mojolicious .

Step 1: Build The List

First, we need a database schema. Santa only really needs to know if someone has been naughty or nice, so our schema is pretty simple. We'll start our Mojolicious::Lite app by connecting to a SQLite database using Mojo::SQLite and loading our database schema from the DATA section of our script using Mojo::SQLite migrations:

use v5.28; use Mojolicious::Lite; use Mojo::SQLite; # Connect to the SQLite database and load our schema from the # '@@ migrations' section, below my $db = Mojo::SQLite->new( 'sqlite:thelist.db' ); $db->auto_migrate(1)->migrations->from_data(); # Start the app. Must be the last code of the script. app->start; __DATA__ @@ migrations -- 1 up CREATE TABLE the_list ( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR NOT NULL, address VARCHAR NOT NULL, is_nice BOOLEAN DEFAULT FALSE, is_delivered BOOLEAN DEFAULT FALSE );

With our schema created, we can add Yancy. Yancy is a simple CMS for editing content and streamlining data-driven website development. We'll tell Yancy to read our database schema to configure itself, but we'll also give it a few hints to make editing content easier.

# Configure Yancy plugin Yancy => { backend => { sqlite => $db }, # Read the schema configuration from the database read_schema => 1, schema => { the_list => { # Show these columns in the Yancy editor 'x-list-columns' => [qw( name address is_nice is_delivered )], properties => { # `id` is auto-increment, so hide it when creating rows id => { readOnly => 1 }, }, }, }, };

If we run our app ( perl myapp.pl daemon ) and go to http://127.0.0.1:3000/yancy , we can edit the list data.

Now Santa's data entry elves get to work entering the data for all the people in the universe!

Step 2: View The List

With our data entry Neptunians working hard, we can build a way to view the list. With four arms, they can enter data twice as fast!

Yancy comes with controllers that let us easily list our data just by configuring a route and creating a template to render. First we configure the route to use Yancy::Controller::Yancy list method:

# Display the naughty rows of the list get '/', { controller => 'yancy', action => 'list', template => 'the_list', schema => 'the_list', filter => { is_nice => 0, }, }, 'the_list.list';

Now we build our template. Yancy comes with a Bootstrap 4 we can use to make a pretty list of names and addresses.

@@ layouts/default.html.ep <head> <script src="/yancy/jquery.js"></script> <link rel="stylesheet" href="/yancy/bootstrap.css"> </head> <body> <main class="container"> %= content </main> </body> @@ the_list.html.ep % layout 'default'; <h1>Naughty List</h1> <ul class="list-group"> % for my $item ( @$items ) { <li class="list-group-item d-flex justify-content-between"> <div> %= $item->{name} <br/> %= $item->{address} </div> % end </li> % } </ul>

Step 3: Complete Delivery

Santa's a busy robot, and all that ordnance is expensive. Once he's done a delivery, he needs to mark it as done so he can move on to all the other deserving people.

Stopping to check people off manually really slows down the murder and mayhem.

Yancy makes it easy to update the data, this time with the "set" action in Yancy::Controller::Yancy:

# Set the delivered state of a list row post '/deliver/:id', { controller => 'yancy', action => 'set', schema => 'the_list', properties => [qw( is_delivered )], forward_to => 'the_list.list', }, 'the_list.deliver';

With the route configured, we need to add a form to our template. We'll use the form_for helper from Mojolicious. The form will display a yes/no toggle button for every row. Yancy is secure by default, so we need to make sure that our form contains the CSRF token (using the csrf_field helper) to prevent cross-site requests.

@@ the_list.html.ep % layout 'default'; <h1>Naughty List</h1> <ul class="list-group"> % for my $item ( @$items ) { <li class="list-group-item d-flex justify-content-between"> <div> %= $item->{name} <br/> %= $item->{address} </div> %= form_for 'the_list.deliver', $item, begin Delivered: %= csrf_field % my $delivered = $item->{is_delivered}; <div class="btn-group btn-group-toggle"> <label class="btn btn-xs <%= $delivered ? 'btn-success active' : 'btn-outline-success' %>"> <input type="radio" name="is_delivered" value="true" <%== $delivered ? 'checked' : '' %>> Yes </label> <label class="btn btn-xs <%= $delivered ? 'btn-outline-danger' : 'btn-danger active' %>"> <input type="radio" name="is_delivered" value="false" <%== $delivered ? '' : 'checked' %>> No </label> </div> % end </li> % } </ul>

We'll add some jQuery at the end (using the javascript helper) to automatically submit the form when the value is changed.

%= javascript begin // Automatically submit the form when an input changes $( 'form input' ).change( function ( e ) { $(this).parents("form").submit(); } ); % end

Now our webapp looks like this:

We can view our entire list, and check off the ones who we've delivered to already! View the entire app here.

Another successful Xmas, powered by Mojolicious!