File Coverage

blib/lib/Dancer2/Core/Role/Hookable.pm
Criterion Covered Total %
statement 54 55 98.1
branch 17 20 85.0
condition 2 3 66.6
subroutine 12 12 100.0
pod 0 5 0.0
total 85 95 89.4


line stmt bran cond sub pod time code
1             package Dancer2::Core::Role::Hookable;
2             # ABSTRACT: Role for hookable objects
3             $Dancer2::Core::Role::Hookable::VERSION = '0.400001';
4 156     156   169270 use Moo::Role;
  156         427  
  156         2130  
5 156     156   71627 use Dancer2::Core;
  156         398  
  156         3592  
6 156     156   4731 use Dancer2::Core::Types;
  156         465  
  156         1148  
7 156     156   1936611 use Carp 'croak';
  156         404  
  156         18198  
8 156     156   13409 use Safe::Isa;
  156         13122  
  156         143072  
9              
10             requires 'supported_hooks', 'hook_aliases';
11              
12             # The hooks registry
13             has hooks => (
14             is => 'ro',
15             isa => HashRef,
16             builder => '_build_hooks',
17             lazy => 1,
18             );
19              
20       879 0   sub BUILD { }
21              
22             # after a hookable object is built, we go over its postponed hooks and register
23             # them if any.
24             after BUILD => sub {
25             my ( $self, $args ) = @_;
26             $self->_add_postponed_hooks($args)
27             if defined $args->{postponed_hooks};
28             };
29              
30             sub _add_postponed_hooks {
31 801     801   2110 my ( $self, $args ) = @_;
32 801         1890 my $postponed_hooks = $args->{postponed_hooks};
33              
34             # find the internal name of the hooks, from the caller name
35 801         1973 my $caller = ref($self);
36 801         7042 my ( $dancer, $h_type, $h_name, @rest ) = map lc, split /::/, $caller;
37 801 50       3426 $h_name = $rest[0] if $h_name eq 'role';
38 801 100       5706 if ( $h_type =~ /(template|logger|serializer|session)/ ) {
39 602         1549 $h_name = $h_type;
40 602         1344 $h_type = 'engine';
41             }
42              
43             # keep only the hooks we want
44 801         2380 $postponed_hooks = $postponed_hooks->{$h_type}{$h_name};
45 801 100       18116 return unless defined $postponed_hooks;
46              
47 5         16 foreach my $name ( keys %{$postponed_hooks} ) {
  5         27  
48 5         19 my $hook = $postponed_hooks->{$name}{hook};
49 5         17 my $caller = $postponed_hooks->{$name}{caller};
50              
51             $self->has_hook($name)
52             or croak "$h_name $h_type does not support the hook `$name'. ("
53 5 50       26 . join( ", ", @{$caller} ) . ")";
  0         0  
54              
55 5         177 $self->add_hook($hook);
56             }
57             }
58              
59             # mst++ for the hint
60             sub _build_hooks {
61 365     365   4823 my ($self) = @_;
62 365         1899 my %hooks = map +( $_ => [] ), $self->supported_hooks;
63 365         6895 return \%hooks;
64             }
65              
66             # This binds a coderef to an installed hook if not already
67             # existing
68             sub add_hook {
69 328     328 0 9424 my ( $self, $hook ) = @_;
70 328         5984 my $name = $hook->name;
71 328         2963 my $code = $hook->code;
72              
73 328 100       1090 croak "Unsupported hook '$name'"
74             unless $self->has_hook($name);
75              
76 327         3586 push @{ $self->hooks->{$name} }, $code;
  327         5250  
77             }
78              
79             # allows the caller to replace the current list of hooks at the given position
80             # this is useful if the object where this role is composed wants to compile the
81             # hooks.
82             sub replace_hook {
83 1659     1659 0 4840 my ( $self, $position, $hooks ) = @_;
84              
85 1659 100       3188 croak "Hook '$position' must be installed first"
86             unless $self->has_hook($position);
87              
88 1658         33983 $self->hooks->{$position} = $hooks;
89             }
90              
91             # Boolean flag to tells if the hook is registered or not
92             sub has_hook {
93 26026     26026 0 45085 my ( $self, $hook_name ) = @_;
94 26026         395345 return exists $self->hooks->{$hook_name};
95             }
96              
97             # Execute the hook at the given position
98             sub execute_hook {
99 22135     22135 0 33650 my $self = shift;
100 22135         29812 my $name = shift;
101              
102 22135 100 66     70224 $name and !ref $name
103             or croak "execute_hook needs a hook name";
104              
105             $name = $self->hook_aliases->{$name}
106 22134 100       48001 if exists $self->hook_aliases->{$name};
107              
108 22134 50       47505 croak "Hook '$name' does not exist"
109             if !$self->has_hook($name);
110              
111 22134 100       180767 $self->$_isa('Dancer2::Core::App') &&
112             $self->log( core => "Entering hook $name" );
113              
114 22134         263648 for my $hook ( @{ $self->hooks->{$name} } ) {
  22134         331624  
115 633         6175 $hook->(@_);
116             }
117             }
118              
119             1;
120              
121             __END__
122              
123             =pod
124              
125             =encoding UTF-8
126              
127             =head1 NAME
128              
129             Dancer2::Core::Role::Hookable - Role for hookable objects
130              
131             =head1 VERSION
132              
133             version 0.400001
134              
135             =head1 AUTHOR
136              
137             Dancer Core Developers
138              
139             =head1 COPYRIGHT AND LICENSE
140              
141             This software is copyright (c) 2023 by Alexis Sukrieh.
142              
143             This is free software; you can redistribute it and/or modify it under
144             the same terms as the Perl 5 programming language system itself.
145              
146             =cut