Dancer2
view release on metacpan or search on metacpan
lib/Dancer2/Plugin.pm view on Meta::CPAN
return;
}
# plugin has been called within a D2 app. Modify
# the app and export keywords
sub _exporter_app {
my( $class, $caller, $global ) = @_;
$exported_app->{$caller} = 1;
# The check for ->dsl->app is to handle plugins as well.
# Otherwise you can only import from a plugin to an app,
# but with this, you can import to anything
# that has a DSL with an app, which translates to "also plugins"
my $app = eval("${caller}::app()") || eval { $caller->dsl->app } ## no critic qw(BuiltinFunctions::ProhibitStringyEval)
or return; ## no critic
return unless $app->can('with_plugin');
my $plugin = $app->with_plugin( '+' . $class );
$global->{'plugin'} = $plugin;
return unless $class->can('keywords');
# Add our hooks to the app, so they're recognized
# this is for compatibility so you can call execute_hook()
# without the fully qualified plugin name.
# The reason we need to do this here instead of when adding a
# hook is because we need to register in the app, and only now it
# exists.
# This adds a caveat that two plugins cannot register
# the same hook name, but that will be deprecated anyway.
{;
foreach my $hook ( @{ $plugin->ClassHooks } ) {
my $full_name = 'plugin.' . lc($class) . ".$hook";
$full_name =~ s/Dancer2::Plugin:://i;
$full_name =~ s/::/_/g;
# this adds it to the plugin
$plugin->hook_aliases->{$hook} = $full_name;
# this adds it to the app
$plugin->app->hook_aliases->{$hook} = $full_name;
# copy the hooks from the plugin to the app
# this is in case they were created at import time
# rather than after
@{ $plugin->app->hooks }{ keys %{ $plugin->hooks } } =
values %{ $plugin->hooks };
}
}
{
# get the reference
my ($plugin_addr) = "$plugin" =~ $REF_ADDR_REGEX;
$instances{$plugin_addr}{'config'} = sub { $plugin->config };
$instances{$plugin_addr}{'app'} = $plugin->app;
Scalar::Util::weaken( $instances{$plugin_addr}{'app'} );
## no critic
no strict 'refs';
# we used a forward declaration
# so the compiled form "plugin_setting;" can be overridden
# with this implementation,
# which works on runtime ("plugin_setting()")
# we can't use can() here because the forward declaration will
# create a CODE stub
no warnings 'redefine';
*{"${class}::plugin_setting"} = sub {
my ($plugin_addr) = "$CUR_PLUGIN" =~ $REF_ADDR_REGEX;
$plugin_addr
or Carp::croak('Can\'t find originating plugin');
# we need to do this because plugins might call "set"
# in order to change plugin configuration but it doesn't
# change the plugin object, it changes the app object
# so we merge them.
my $name = ref $CUR_PLUGIN;
$name =~ s/^Dancer2::Plugin:://g;
my $plugin_inst = $instances{$plugin_addr};
my $plugin_config = $plugin_inst->{'config'}->();
my $app_plugin_config = $plugin_inst->{'app'}->config->{'plugins'}{$name};
return { %{ $plugin_config || {} }, %{ $app_plugin_config || {} } };
};
# FIXME:
# why doesn't this work? it's like it's already defined somewhere
# but i'm not sure where. seems like AUTOLOAD runs it.
#$class->can('execute_hook') or
*{"${class}::execute_hook"} = sub {
# this can also be called by App.pm itself
# if the plugin is a
# "candidate" for a hook
# See: App.pm "execute_hook" method, "around" modifier
if ( $_[0]->isa('Dancer2::Plugin') ) {
# this means it's probably our hook, we need to verify it
my ( $plugin_self, $hook_name, @args ) = @_;
my $plugin_class = lc $class;
$plugin_class =~ s/^dancer2::plugin:://;
$plugin_class =~ s{::}{_}g;
# you're either calling it with the full qualifier or not
# if not, use the execute_plugin_hook instead
if ( $plugin->hooks->{"plugin.$plugin_class.$hook_name"} ) {
Carp::carp("Please use fully qualified hook name or "
. "the method execute_plugin_hook");
$hook_name = "plugin.$plugin_class.$hook_name";
}
$hook_name =~ /^plugin\.$plugin_class/
or Carp::croak('Unknown plugin called through other plugin');
# now we can't really use the app to execute it because
( run in 0.535 second using v1.01-cache-2.11-cpan-39bf76dae61 )