Browse Source

ADD: initial commit of the plugin

master
Dominik Meyer 10 months ago
parent
commit
02cbaa4b70
No known key found for this signature in database
8 changed files with 389 additions and 0 deletions
  1. +2
    -0
      .gitignore
  2. +27
    -0
      dist.ini
  3. +199
    -0
      lib/Mojolicious/Plugin/RuntimeConfig.pm
  4. +26
    -0
      lib/Mojolicious/Plugin/RuntimeConfig/Backend.pm
  5. +64
    -0
      lib/Mojolicious/Plugin/RuntimeConfig/Backend/SQLite.pm
  6. +22
    -0
      lib/Mojolicious/Plugin/RuntimeConfig/Test.pm
  7. +42
    -0
      t/99-testMojo.t
  8. +7
    -0
      test.conf

+ 2
- 0
.gitignore View File

@@ -0,0 +1,2 @@
Mojolicious-Plugin-RuntimeConfig*
.build

+ 27
- 0
dist.ini View File

@@ -0,0 +1,27 @@
name = Mojolicious-Plugin-RuntimeConfig
author = Dominik Meyer <dmeyer@federationhq.de>
license = Perl_5
copyright_holder = Dominik Meyer
copyright_year = 2019

main_module = lib/Mojolicious/Plugin/RuntimeConfig.pm

[MetaResources]
repository.url = https://gitcloud.federationhq.de/git/App-Git-IssueManager.git
repository.type = git
bugtracker.mailto = byterazor@federationhq.de

[@Git]
[Git::NextVersion]
first_version = 0.1 ; this is the default
version_by_branch = 1 ; this is the default
[PkgVersion]
use_package = 1

[@Basic]
[AutoPrereqs]

[PodWeaver]
[ChangelogFromGit]
[@TestingMania]
disable = Test::CPAN::Changes

+ 199
- 0
lib/Mojolicious/Plugin/RuntimeConfig.pm View File

@@ -0,0 +1,199 @@
package Mojolicious::Plugin::RuntimeConfig;

#ABSTRACT: Mojolicious Plugin implementing a runtime configurable configuration storage on top of Mojolicious::Plugin::Config
use strict;
use Mojo::Base 'Mojolicious::Plugin::Config';
use Role::Tiny;
use Try::Tiny;
use Module::Load '';
use Hash::Merge;
use Storable qw(dclone);

=head1 DESCRIPTION

Sometimes it is desireable to change webservice configuration options at runtime of the webservice
without restarting it. While the Plugin Mojolicious::Plugin::Config is a very good configuration
plugin, it is static and requires the restart of the webserver.

This Plugin uses Mojolicious::Plugin::Config as the source for the default configuration options. It
provides a Mojolicious helper B<runtimeConfig> through which the configuration hash can be accessed.
The Plugin uses different I<backends> to store the runtime configuration. The default Perl Module of
this plugin provides the backends:

=over

=item B<SQLite> - stores the configuration data in a SQLite Database

=back

For backend specific documentation, e.g. how the hashes are stored in the backend look at the
backend module.

The Mojolicious::Plugin::Config plugin is loaded by this plugin. You must B<not> load it yourself.

=head2 PARAMETERS

=head3 parameters of Mojolicious::Plugin::Config

all the parameters of Mojolicious::Plugin::Config are given to the instantiation of the plugin.

=head3 backend

this parameters is B<required> by Mojolicious::Plugin::RuntimeConfig. It selects which backend to
load. The backend name is the end of the perl backend module.

E.g. For Mojolicious::Plugin::RuntimeConfig::SQLite you just give SQLite as the backend name.

=head2 USAGE

# Load configuration file
my $config = $self->plugin('RuntimeConfig' =>
{
file => "test.conf",
backend => "SQLite",
dbfile => "/tmp/test.db"
}
);

$t->app->runtimeConfig()->{value1}=10;
print $t->app->runtimeConfig()->{value1};

=cut


# This is just an internal attribute for storing an instance of the selected backend
has 'backend' => undef;

# The values from the default config file loaded by Mojolicious::Plugin::Config
has 'default_config' => undef;

# internal attribute for storing all top level keys of the configuration hash for iterating over it
has 'keys' => undef;


# constructor for tieing a hash
sub TIEHASH {
my $self = shift;

return $self;
}


# This method will be triggered every time an element in the tied hash is accessed (read).
# It takes one argument beyond its self reference: the key whose value we're trying to fetch.
sub FETCH
{
my $self = shift;
my $key = shift;
my $value = $self->default_config()->{$key} || $self->backend()->store()->{$key};

return $value;
}

# This method will be triggered every time an element in the tied hash is set (written).
# It takes two arguments beyond its self reference: the key at which we're trying to store
# something, and the value we're trying to put there.
sub STORE
{
my $self = shift;
my $key = shift;
my $value = shift;

$self->backend()->store()->{$key}=$value;
}

# This method is triggered when we remove an element from the hash, typically by using the delete() function.
sub DELETE
{
my $self = shift;
my $key = shift;

delete($self->backend()->store()->{$key});
}

# This method is triggered when the whole hash is to be cleared, usually by assigning the empty list to it.
sub CLEAR
{
my $self = shift;
$self->backend()->store()={};
}

# This method is triggered when the user uses the exists() function on a particular hash.
sub EXISTS
{
my $self = shift;
my $key = shift;

return exists($self->backend()->store()->{$key}) || exists($self->default_config()->{$key});

}

# This method will be triggered when the user is going to iterate through the hash,
# such as via a keys(), values(), or each() call.
sub FIRSTKEY {
my $self = shift;

my %hash=();
foreach my $k (keys %{$self->default_config()})
{
$hash{$k}=1;
}

foreach my $k (keys %{$self->backend()->store()})
{
$hash{$k}=1;
}
$self->keys(\%hash);

keys %{$self->keys()};

return each(%{$self->keys()});
}

# This method gets triggered during a keys(), values(), or each() iteration.
# It has a second argument which is the last key that had been accessed. This
# is useful if you're caring about ordering or calling the iterator from more than one sequence,
# or not really storing things in a hash anywhere.
sub NEXTKEY
{
my $self = shift;

return each(%{$self->keys()});
}

#
# subrouting called by Mojolicious on plugin registration
#
sub register {
my $self = shift;
$self->default_config($self->SUPER::register(@_));
my $app = shift;
my $config = shift;

my %tiedConfig;

# check if a backend was selected
die("no backend given in configuration") unless $config->{backend};

# load the requested backend module
my $module="Mojolicious::Plugin::RuntimeConfig::Backend::" . $config->{backend};
Module::Load::load($module);

# instantiate backend module
$self->backend($module->new($config));
$self->backend()->connect();

# we use a tied hash to make everything available to the user
tie %tiedConfig, $self;

# register the helper for this plugin
$app->helper(runtimeConfig => sub {
shift;
return \%tiedConfig;
}
);

return $self->default_config();
}

1;

+ 26
- 0
lib/Mojolicious/Plugin/RuntimeConfig/Backend.pm View File

@@ -0,0 +1,26 @@
package Mojolicious::Plugin::RuntimeConfig::Backend;

#ABSTRACT: Backend role for Mojolicious::Plugin::RuntimeConfig backends
use strict;
use warnings;
use Mojo::Base -base;
use Role::Tiny;

=method connect

connect is required as a backend method. It is called on plugin registration and should initialize
the backend, e.g. connect to the database

=cut


=attr store

the store attribute is used to access the backend configuration options. E.g. in SQLite backend
the hashref stored inside store is tied to an SQLite database.

=cut
requires qw(connect store);


1;

+ 64
- 0
lib/Mojolicious/Plugin/RuntimeConfig/Backend/SQLite.pm View File

@@ -0,0 +1,64 @@
package Mojolicious::Plugin::RuntimeConfig::Backend::SQLite;

#ABSTRACT: SQLite Backend for Mojolicious::Plugin::RuntimeConfig to store configuration options in a SQLite database
use strict;
use warnings;
use Mojo::Base -base;
use Mojo::SQLite;
use Try::Tiny;
use Role::Tiny::With;
use DBI;
use Tie::Hash::DBD;


=attr dbfile

B<private attribute>

path to the sqlite database file

=cut
has 'dbfile' => undef;

=attr dbh

B<private attribute>

database handle to the sqlite file

=cut
has 'dbh' => undef;

has 'store' => undef;

=method connect

connect the sqlite database

=cut
sub connect
{
my $self = shift;

die("no database file given") unless $self->dbfile();

# create a database handle to the sqlite database file
$self->dbh(Mojo::SQLite->new($self->dbfile()));

# do db structure migration
#$self->migrate();


tie my %hash, "Tie::Hash::DBD", $self->dbh()->db()->dbh(), {
tbl => "config_values",
key => "option",
fld => "value",
str => "Storable",
trh => 0,
};

$self->store(\%hash);

}

1;

+ 22
- 0
lib/Mojolicious/Plugin/RuntimeConfig/Test.pm View File

@@ -0,0 +1,22 @@
package Mojolicious::Plugin::RuntimeConfig::Test;
use Mojo::Base 'Mojolicious';
use Mojolicious::Plugin::RuntimeConfig;

# This method will run once at server start
sub startup {
my $self = shift;

# Load configuration file
my $config = $self->plugin('RuntimeConfig' =>
{
file => "test.conf",
backend => "SQLite",
dbfile => "/tmp/test.db"
}
);

}



1;

+ 42
- 0
t/99-testMojo.t View File

@@ -0,0 +1,42 @@
use 5.006;
use strict;
use warnings;
use Test::More tests => 24;
use Test::Mojo;
use Test::Exception;

my $t;

lives_ok ( sub {$t = Test::Mojo->new('Mojolicious::Plugin::RuntimeConfig::Test');}, "starting plugin in Mojolicious");



#clear runtime config
for my $k (keys %{$t->app->runtimeConfig()})
{
delete($t->app->runtimeConfig()->{$k});
}

#check if only the default config is available
my @keys = keys %{$t->app->runtimeConfig()};
is (@keys,3, "default config has three entries");

for(my $i=0; $i < 5; $i++)
{
# check default config
is ($t->app->runtimeConfig()->{value1},20,"default value1 is 20");
is ($t->app->runtimeConfig()->{value2},30,"default value2 is 30");
is (ref($t->app->runtimeConfig()->{value3}),"HASH","default value3 is a HASH");
is ($t->app->runtimeConfig()->{value3}->{subvalue}, "hallo", "default subvalue of value 3 is hallo");
}

#create new value
lives_ok ( sub { $t->app->runtimeConfig()->{value4}="Test" }, "creating new value value4");
is ($t->app->runtimeConfig()->{value4}, "Test", "value4 is Test");


#clear runtime config
for my $k (keys %{$t->app->runtimeConfig()})
{
delete($t->app->runtimeConfig()->{$k});
}

+ 7
- 0
test.conf View File

@@ -0,0 +1,7 @@
{
value1 => 20,
value2 => 30,
value3 => {
subvalue => "hallo"
}
}

Loading…
Cancel
Save