2015 twenty four merry days of Perl Feed

Configuration Station

Config::Station - 2015-12-12

The best option for configuration.

I wrote Config::Station after implementing a pattern I wrote about a while ago another time or two.

The overall gist of Config::Station is that you define a simple configuration class that knows nothing about the file the config is loaded from:


1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
10: 
11: 
12: 
13: 
14: 
15: 

 

package MyApp::Config;

use Moo;
use Types::Standard 'Int';

has port => (
  is => 'ro',
  default => 0,
  isa => Int,
);

has root_dir => (
  is => 'ro',
  default => '/var/myapp',
);

 

And then you inflate it with Config::Station:


1: 
2: 
3: 
4: 
5: 
6: 
7: 

 

my $station = Config::Station->new(
  config_class => 'MyApp::Config',
  env_key => 'MYAPP',
  location => '.config.json',
);

my $config = $station->load;

 

So already we have a few nice details over most other config options out there:

  • We know exactly what config values are supported

    I often get frustrated at trying to discover exactly what all config keys are used. Sometimes this isn't even possible when it's a mere hash.

  • We have defaults defined in a central location

  • We have all of the validation that we've become used to

Config::Station, like any good gift, gets progressively better as you learn more about it. Simply blessing a JSON file into an object is not the most impressive idea ever (though it is still pretty convenient, if I may say so myself.) One of the most important features of Config::Station is that it overlays the loaded config with environment variables. So if you wanted to start two separate web servers running your application running on two servers it should be as easy as:

 $ MYAPP_SERVER=Gazelle MYAPP_PORT=8080 myapp_server.pl &
 $ MYAPP_SERVER=Starman MYAPP_PORT=8081 myapp_server.pl &

That's really great, and really works well for my personal development flow, but it's still just a taste of what you can do with it.

See, the real beauty of Config::Station is the delegation of the hard work to the configuration class. Here's another example of cool things you can do, again using Moo as the object system backing the configuration class:


1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
10: 
11: 
12: 

 

package MyApp::Config;

use Moo;
use JSONY;

has dsn => (
  is => 'ro',
  required => 1,
  coerce => sub {
    ref $_[0] ? $_[0] : JSONY->new->load($_[0])
  },
)

 

We're adding smarts here: If the value passed to us isn't exactly in the format we want, we're coercing it into the right format.

You may not have heard of JSONY; it's a weird thing but it's great in cases like this. Here is how you could use it:

 $ MYAPP_DSN='dbi:ODBC:server=10.6.6.17 frew Password1!' myapp_server.pl

In this example, the attribute's coercion uses JSONY to parse our string into an arrayref with three values, suitable for passing to DBI or DBIx::Class.

Of course, nothing requires you to use Moo or Moose. The only requirement is that your class take a flat hashref as it's sole argument. So if you wanted to do something really crazy you could go with raw OO:


1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 

 

package MyApp::Config;

use strict;
use warnings;

sub new {
  my ($class, $hash) = @_;

  bless [
    $hash->{dsn},
    $hash->{username},
    $hash->{password},
  ], $class;

}

sub dsn { $_[0][0] }

sub username { $_[0][1] }

sub password { $_[0][2] }

 

Honestly I struggle to come up with an example that would warrant non-Moo OO, but it is explicitly supported nonetheless. Maybe if you wanted to do some crazy OO like IO::All does where you do bless Symbol::gensym(), $class?

SEE ALSO

Gravatar Image This article contributed by: Arthur Axel "fREW" Schmidt <frew@cpan.org>