From f1c874c3725f159f1b00295bf825b2a270cf57bc Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Thu, 30 Dec 2010 16:32:18 +0100 Subject: [PATCH] =?utf8?q?Ein=20System=20von=20Hooks,=20die=20vor=20oder?= =?utf8?q?=20nach=20Actions=20ausgef=C3=BChrt=20werden=20k=C3=B6nnen?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- SL/Controller/Base.pm | 116 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 7 deletions(-) diff --git a/SL/Controller/Base.pm b/SL/Controller/Base.pm index 0c8147015..8e7e721dd 100644 --- a/SL/Controller/Base.pm +++ b/SL/Controller/Base.pm @@ -77,18 +77,66 @@ sub render { return $output; } +# +# Before/after run hooks +# + +sub run_before { + _add_hook('before', @_); +} + +sub run_after { + _add_hook('after', @_); +} + +my %hooks; + +sub _add_hook { + my ($when, $class, $sub, %params) = @_; + + foreach my $key (qw(only except)) { + $params{$key} = { map { ( $_ => 1 ) } @{ $params{$key} } } if $params{$key}; + } + + my $idx = "${when}/${class}"; + $hooks{$idx} ||= [ ]; + push @{ $hooks{$idx} }, { %params, code => $sub }; +} + +sub _run_hooks { + my ($self, $when, $action) = @_; + + my $idx = "${when}/" . ref($self); + + foreach my $hook (@{ $hooks{$idx} || [] }) { + next if ($hook->{only } && !$hook->{only }->{$action}) + || ($hook->{except} && $hook->{except}->{$action}); + + if (ref($hook->{code}) eq 'CODE') { + $hook->{code}->($self); + } else { + my $sub = $hook->{code}; + $self->$sub; + } + } +} + # # private functions -- for use in Base only # sub _run_action { my $self = shift; - my $action = "action_" . shift; + my $action = shift; + my $sub = "action_${action}"; + + return $self->_dispatch(@_) if $action eq 'dispatch'; - return $self->_dispatch(@_) if $action eq 'action_dispatch'; + $::form->error("Invalid action '${action}' for controller " . ref($self)) if !$self->can($sub); - $::form->error("Invalid action ${action} for controller " . ref($self)) if !$self->can($action); - $self->$action(@_); + $self->_run_hooks('before', $action); + $self->$sub(@_); + $self->_run_hooks('after', $action); } sub _controller_name { @@ -99,10 +147,13 @@ sub _dispatch { my $self = shift; no strict 'refs'; - my @actions = grep { m/^action_/ } keys %{ ref($self) . "::" }; - my $action = first { $::form->{$_} } @actions; + my @actions = map { s/^action_//; $_ } grep { m/^action_/ } keys %{ ref($self) . "::" }; + my $action = first { $::form->{"action_${_}"} } @actions; + my $sub = "action_${action}"; - $self->$action(@_); + $self->_run_hooks('before', $action); + $self->$sub(@_); + $self->_run_hooks('after', $action); } sub _template_obj { @@ -201,6 +252,24 @@ Usage from a template usually looks like this: The dispatching is handled by the function L. +=head2 HOOKS + +Hooks are functions that are called before or after the controller's +action is called. The controller package defines the hooks, and those +hooks themselves are run as instance methods. + +Hooks are run in the order they're added. + +The return value of the hooks is discarded. + +Hooks can be defined to run for all actions, for only specific actions +or for all actions except a list of actions. Each entry is the action +name, not the sub's name. Therefore in order to run a hook before one +of the subs C or C is called the following +code can be used: + + __PACKAGE__->run_before('things_to_do_before_edit_and_save', only => [ 'edit', 'save' ]); + =head1 FUNCTIONS =head2 PUBLIC HELPER FUNCTIONS @@ -298,6 +367,39 @@ Redirects the browser to a new URL by outputting a HTTP redirect header. The URL is generated by calling L with C<%url_params>. +=item C + +=item C + +Adds a hook to run before or after certain actions are run for the +current package. The code to run is C<$sub> which is either the name +of an instance method or a code reference. If it's the latter then the +first parameter will be C<$self>. + +C<%params> can contain two possible values that restrict the code to +be run only for certain actions: + +=over 2 + +=item C<< only => \@list >> + +Only run the code for actions given in C<@list>. The entries are the +action names, not the names of the sub (so it's C instead of +C). + +=item C<< except => \@list >> + +Run the code for all actions but for those given in C<@list>. The +entries are the action names, not the names of the sub (so it's +C instead of C). + +=back + +If neither restriction is used then the code will be run for any +action. + +The hook's return values are discarded. + =back =head2 PRIVATE FUNCTIONS -- 2.20.1