Skip to content

Commit

Permalink
Add a configuration system to allow users to set their own defaults
Browse files Browse the repository at this point in the history
  • Loading branch information
Arthur Axel 'fREW' Schmidt committed Mar 28, 2015
1 parent 1598864 commit 366d87b
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 4 deletions.
2 changes: 2 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Revision history for {{$dist->name}}

{{$NEXT}}
- Add a configuration system to allow users to set their own defaults
(Fixes GH#10)

0.001002 2015-03-07 13:08:54-06:00 America/Chicago
- Correctly munge the index html so links will work
Expand Down
51 changes: 47 additions & 4 deletions bin/charon
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,25 @@ use Future;
use IO::Socket::IP;

use App::Charon::Web;
use App::Charon::ConfigLoader;
my $c = App::Charon::ConfigLoader->new(
env_key => 'CHARON',
config_class => 'App::Charon::Config',
location => '.charonrc',
)->load;

my ($opt, $usage) = describe_options(
'charon %o [file|dir]',
[ 'listen|l=s', "Listens on an address, whether HOST:PORT or :PORT"],
[ 'autoshutdown=s', 'Shutdown after a given time period (example: 1h30m)'],
[ 'query-param-auth=s', 'Use query param auth. Eg: pass=station will require ?pass=station in requests'],
[ 'index!', 'Show index (default on)', { default => 1 }],
[ 'listen|l=s', "Listens on an address, whether HOST:PORT or :PORT",
$c->opt_default_for('listen')],
[ 'autoshutdown=s', 'Shutdown after a given time period (example: 1h30m)',
$c->opt_default_for('autoshutdown')],
[ 'query-param-auth=s', 'Use query param auth. Eg: pass=station will require ?pass=station in requests',
$c->opt_default_for('query_param_auth')],
[ 'index!', 'Show index', $c->opt_default_for('index', { default => 1 })],
[],
[ 'help|h', "print usage message and exit" ],
{ show_defaults => 1 },
);

print($usage->text), exit if $opt->help;
Expand Down Expand Up @@ -129,6 +139,39 @@ various machines have the right tools and whatnot to serve files from one
machine to another. C<charon> helps with this by making some of what used to be
Plack oneliners into a much more robust and convenient tool.
=head1 CONFIGURATION
C<charon> uses
L<a configuration system I developed|https://blog.afoolishmanifesto.com/posts/configuration-station/>
that allows the user to use either a file, or environment variables, or both.
Currently all the configuration allows you to do is set defaults for the
commandline options. Basically it works like this:
=over 3
=item * if C<CHARON_$FOO> is set, use that to set C<foo>
=item * if C<$foo> exists in the config file, use that to set C<foo>
=back
The location of the config file is similarly dynamic. The default is just
F<.charonrc> in the current directory. The location can be overridden by
setting C<CHARON_CONFLOC>. Currently the configuration file is L<JSONY>.
So to be clear, if you wanted to set a default for C<--query-param-auth>, you
can either:
=over 3
=item 1. Set C<CHARON_QUERY_PARAM_AUTH=password=station>
=item 2. Put a line like C< query_param_auth: password=station > in F<.charonrc>
=item 3. Set C<CHARON_CONFLOC> to F<~/.charonrc> and add a line like the above to that file.
=back
=head1 COMMANDLINE OPTIONS
=head2 single positional argument
Expand Down
6 changes: 6 additions & 0 deletions cpanfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ requires 'Time::Duration::Parse';
requires 'App::Genpass';
requires 'IO::Socket::IP';
requires 'HTML::Zoom';
requires 'Moo' => 2;
requires 'JSONY';
requires 'IO::All';
requires 'Try::Tiny';
requires 'Module::Runtime';
requires 'namespace::clean';

on test => sub {
requires 'Test::More' => 1;
Expand Down
23 changes: 23 additions & 0 deletions lib/App/Charon/Config.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package App::Charon::Config;

use utf8;
use Moo;
use warnings NONFATAL => 'all';

has [qw(listen autoshutdown query_param_auth index)] => (
is => 'ro',
predicate => 1,
);

sub opt_default_for {
my ($self, $attr, @default) = @_;

my $pred = "has_$attr";
if ($self->$pred) {
return { default => $self->$attr }
} else {
return @default
}
}

1;
70 changes: 70 additions & 0 deletions lib/App/Charon/ConfigLoader.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package App::Charon::ConfigLoader;

use utf8;
use Moo;
use warnings NONFATAL => 'all';

use JSONY;
use IO::All;
use Try::Tiny;
use Module::Runtime 'use_module';
use namespace::clean;

has _env_key => (
is => 'ro',
init_arg => 'env_key',
required => 1,
);

has _location => (
is => 'ro',
init_arg => undef,
lazy => 1,
default => sub {
my $self = shift;
$ENV{$self->_env_key . '_CONFLOC'} || $self->__location
},
);

has __location => (
is => 'ro',
init_arg => 'location',
);

has _config_class => (
is => 'ro',
init_arg => 'config_class',
required => 1,
);

sub _io { io->file(shift->_location) }

sub _read_config_from_file {
my $self = shift;
try {
JSONY->new->load($self->_io->all)
} catch {
{}
}
}

sub _read_config_from_env {
my $k_re = '^' . quotemeta($_[0]->_env_key) . '_(.+)';

+{
map {; m/$k_re/; lc $1 => $ENV{$_[0]->_env_key . "_$1"} }
grep m/$k_re/,
keys %ENV
}
}

sub _read_config {
{
%{$_[0]->_read_config_from_file},
%{$_[0]->_read_config_from_env},
}
}

sub load { use_module($_[0]->_config_class)->new($_[0]->_read_config) }

1;

0 comments on commit 366d87b

Please sign in to comment.