Using Perl to drive simple dialog-based widgets

August 08, 2011

Over the years at $work we always used home grown configuration management systems. Some more robot than others. They all had something in common, they lacked polish.

That’s why I decided to step back and approach the problem from a new user’s perspective. They want to set it and forget it. Well designed GUI’s help. I primarily work on the backend so I’m limited to the terminal where editing files by hand reigns supreme.

After a week of hacking and XS I was able to produce a Moose based system to drive libdialog.

sexy

(this picture represents our internal configuration system that is not available publicly, but that may change soon…)

On CPAN

Here’s how it came to be…

Like most configuration systems the history is nearly the same. First you start out with global variables because no one really thought about it nor cared about future maintenance. So you end up with:

Some::Thing::AMBIOUS_ABBREVIATED_CONFIG_VARIABLE = 9999;

And then you realize that was a terrible idea because someone changed it to a value that had disastrous effects in production because any number of reasons, the value didn’t have any consequences in your internal test environment, typo, etc.

Then you move on to config files, possibly Perl or hand made. Then to a standard format, ini or whatever. Then you realize how smart YAML and JSON are and settle with those.

But there still is a problem. What is an easy way for someone new to the application to setup up their own configuration? How about someone not intimately familiar with Perl? I decided that enough was enough and a real solution was required. I browsed and browsed for a solution that would allow me to use some sort of graphical interface in the terminal or possibly even X that I could piggy back off of and build an easy configuration management system. There was no complete solution that met the Adam’s Standard of Software Excellence approval, so like most driven developers, I created (or in this case glued) my own using dialog (more precisely libdialog which dialog is built from), which uses ncurses for drawing.

The result: Hobocamp. Yes, it has a silly name, here’s where it’s from.

Now we needed a way to make config values adjustable. This is an example pulled from our home grown system at work. We have a type library and knobs which are a single item in the menu list. This ties directly into our configuration library (based on YAML) so altering a value here via the GUI results in the data being written back on save.

package Amethyst::BuildConf::Knob::UnixBinary;

use strict;

use Moose;

use File::Spec;

use Amethyst::Types qw(UnixExecutableFile);

has 'conf_key_name' => ('isa' => 'Str',  ... );
has 'name' => ('isa' => 'Str', ... );
has 'question' => ('isa' => 'Str', ... );
has 'value' => ('isa' => UnixExecutableFile, 'is' => 'rw');
has 'data' => ('isa' => 'Str', 'is' => 'ro', 'default' => '/tmp/a.out');

with qw(Amethyst::BuildConf::Roles::Knob Amethyst::BuildConf::Roles::UI::FileSelect);

1;

The role defines what GUI widget is used.

A test would look something like this:

subtype UnixFilename,
as Str,
where { m/[\0|\/]/ },
message { "$_ is not a valid Unix file name" };

And any invalid input catches that message and displays to the user as message box widget.

And now we have a robust solution that both developers and non-developers can easily use. I may be able to release this to CPAN pending approval from my employer.