ciarand.me another code blog

using tmux with vim: a screencast

I made another screencast. I think in retrospect I should have recorded the screencast part first and then later added commentary.

Learning!

Refactoring a PHP application, part 1

This is the first post in the series. The second post is available here.

There's been a refreshing wave of change in the way we build web applications in PHP over the last few years. The advent of Composer and the wave of modular, reusable components it's brought us have dramatically improved the architecture of new applications. Unfortunately for the developers forced to maintain them, not all applications are taking advantage of these new and powerful tools. In this series I'll be discussing the process of upgrading and refactoring a typical "old school" site built with PHP into a solid, well tested application.

Let's first discuss what we're working with. I'm assuming a multipage site with multiple entry points. The ones I've seen and worked with tend to have lines like include "inc/header.php". There's frequently code that's been copied and pasted in multiple places, and these snippets often have minute and unmarked differences. There's rarely any form of testing or build process, and the business login is often intertwined with the display login.

Note: this process is not specific to PHP or even to sites structured like this, but for simplicity's sake (and because of how often I see sites like this in the wild) this is the base we'll be working from.

Now that we know what we're working with, let's establish what we'd like to end up with:

  • A single entry point (index.php) in the public web directory

  • A full set of tests, both unit and functional, that insure the site is functioning correctly at all times

  • A modern deployment process, using Docker, Nginx, and PHP-FPM

  • A way of interacting with the code from the command line effectively. Any HTTP request should be able to be perfectly simulated from any SAPI (cli, phpdbg, etc.)

Let's begin.

Writing CLI unit tests in Perl

I was recently asked to write a small C script for reading some basic system stats. Nothing particularly exciting, and certainly not worth including any large unit testing frameworks for. Going without any tests at all (going commando) just doesn't feel right though. Instead, I decided to use Perl and its Test::Simple module to write some quick tests.

Here's our program (stored in script.c, compiled to script):

#include <stdio.h>

int main(int argc, const char* argv[]) {
    printf("argc: %d", argc);

    if (argc == 1) {
        printf("you called this script with no arguments\n");
    } else if (argc == 2) {
        printf("you called this script with 1 argument\n");
    } else {
        printf("you called this script with %d arguments\n", argc - 1);
    }

    return 0;
}

Easy enough, we can test this. Here's our test script:

#!/usr/bin/env perl -w

use strict;
use warnings;

use Test::Simple tests => 4;

ok(`./script` =~ /no arguments$/, "no args");
ok(`./script foo` =~ /1 argument$/, "1 arg");
ok(`./script foo bar` =~ /2 arguments$/, "2 args");
ok(`./script foo bar baz` =~ /3 arguments$/, "3 args");

Super simple. Let's go over the parts of the script.

#!/usr/bin/env perl -w

Perl's she-bang line. This just tells the kernel to run this script using the user's Perl bin with the -w (warn) flag on. Normally Perl let's you get away with a lot of nonsense, and the -w flag restricts the level of nonsense that's acceptable.

Note: this post originally stated that the she-bang line was for the shell. It's actually for the kernel. The More You Know™

use Test::Simple tests => 4;

Use the Test::Simple pod. The most important part here is the tests => 4, which is where we tell the script that we're planning to run 4 tests. That way it knows that if a different number of tests are run something went wrong and it'll tell us.

ok(`./script` =~ /no arguments$/, "no args")
ok(`./script foo` =~ /1 argument$/, "1 arg")
ok(`./script foo bar` =~ /2 arguments$/, "2 args")
ok(`./script foo bar baz` =~ /3 arguments$/, "3 args")

Ah, the meat of the script. The ok subroutine just tallies up a passed test if the first argument is true or a failed test if it evaluates to false. The second (optional) argument is a test title.

The backticks (`) around the ./script execute the command and return the stdout. The =~ just returns true if the regex on the right matches the string on the left.

Running the script gets us:

λ ./test.pl
1..4
ok 1 - no args
ok 2 - 1 arg
ok 3 - 2 args
ok 4 - 3 args

There, that's an easy (and lightweight) way of testing simple cli scripts.

Compiling Vim on Fedora 20

Vim is my editor of choice. I was recently playing around with a Fedora installation and realized that even the vim-enhanced package available via yum was not as feature-complete as the one on my MacBook (compiled with homebrew's help).

I decided I'd need to compile it from scratch. Here's how I did that:

Note: I already had multiple versions of Ruby compiled from a previous Ansible script I ran. You may have to get Ruby before this, either through yum or by compiling it yourself.

# first update yum
sudo yum update -y

# remove any old versions of vim
sudo yum remove vim

# install extra deps
sudo yum install -y lua lua-devel luajit luajit-devel \
    ctags mercurial python python-devel \
    python3 python3-devel tcl-devel \
    perl perl-devel perl-ExtUtils-ParseXS \
    perl-ExtUtils-Xspp perl-ExtUtils-CBuilder

# symlink xsubpp (perl) from /usr/bin to the perl dir
sudo ln -s /usr/bin/xsubpp /usr/share/perl5/ExtUtils/xsubpp

# use ~/src as our compile dir
mkdir -p ~/src && cd ~/src

# clone the vim repo
hg clone https://vim.googlecode.com/hg vim

# configure it
cd vim
./configure --enable-fail-if-missing \
    --enable-luainterp --with-luajit \
    --enable-perlinterp \
    --enable-pythoninterp \
    --enable-python3interp \
    --enable-rubyinterp \
    --enable-tclinterp \
    --enable-multibyte \
    --enable-fontset

# install it in /usr/share/vim/vim74
VIMRUNTIMEDIR=/usr/share/vim/vim74 sudo make install

And there you have it! That should install the supporting files to /usr/share/vim/vim74 and the new Vim binary to /usr/local/bin/vim.

The full Ansible script I've used here is available in my dotfiles.

Bootstrapping a server with Ansible

I've been playing with Ansible recently. I recorded a screencast on how to bootstrap a new cloud server with it.

Here's the video and here's the code that accompanies it.