i'd like to be able to create "ghost" packages and subs. I have a configuration (ini) file with entries like this:
[features]
sys.ext.latex = off
gui.super.duper.elastic = off
user.login.rsa = on
This file is parsed, and later developers can ask questions like:
if ( MyApp::Feature->enabled ( 'user.login.rsa' ) { ... }
(The whole idea is based on Martin Fowler's FeatureToggle http://martinfowler.com/bliki/FeatureToggle.html)
Using AUTOLOAD for catching calls in MyApp::Feature, and BEGIN block for parsing ini file we are able to provide this API:
if ( MyApp::Feature->user_login_rsa ) { ... }
The question is: Is it possible to create following API:
if ( MyApp::Feature::User::Login::RSA ) { ... }
having only MyApp::Feature?
Lower,upper case can be modified in the config file, that's not the issue here. And make it clear, implementation is decoupled from the configuration, there is no MyApp::Feature::User::Login::RSA and never will be. Implementation for this feature lies f.e. in MyApp::Humans.
I am aware that putting MyApp::Feature::Foo::Bar suggests there must be such Package. But developers know the convention that Feature package manages feature toggles and they would have no problems with that. I find the first example (using enabled( $string ) bit too complex to read
if ( package::package->method ( string ) )
the second one better:
if ( package::package->method )
the third would be even easier:
if ( package::package::package )
So, is it possible to simulate AUTOLOAD on the package level?
Greetings, Rob.
So it sounds like you have a list of multi-word keys that you want to install into a namespace.
BEGIN {
my %states = ( # the values that should be transformed
on => sub () {1},
off => sub () {''},
);
sub install_config {
my ($package, $config) = @_;
for my $key (keys %$config) {
my @parts = map ucfirst, split /\./, $key;
my $name = join '::' => $package, @parts;
no strict 'refs';
*{$name} = $states{$$config{$key}} # use a tranformed value
|| sub () {$$config{$key}} # or the value itself
}
}
}
BEGIN {
my %config = qw(
sys.ext.latex off
gui.super.duper.elastic off
user.login.rsa on
some.other.config other_value
);
install_config 'MyApp::Feature' => \%config;
}
say MyApp::Feature::Sys::Ext::Latex ? 'ON' : 'OFF'; # OFF
say MyApp::Feature::Gui::Super::Duper::Elastic ? 'ON' : 'OFF'; # OFF
say MyApp::Feature::User::Login::Rsa ? 'ON' : 'OFF'; # ON
say MyApp::Feature::Some::Other::Config; # other_value
The constant subroutines installed here are will be inlined by perl when applicable.
You can make install_config
a bit easier to use by putting it into a package's import function:
BEGIN {$INC{'Install/Config.pm'}++} # fool require
sub Install::Config::import {shift; goto &install_config}
use Install::Config 'MyApp::Feature' => {qw(
sys.ext.latex off
gui.super.duper.elastic off
user.login.rsa on
some.other.config other_value
)};