File Coverage

blib/lib/Dancer2/Plugin/Cache/CHI.pm
Criterion Covered Total %
statement 41 41 100.0
branch 2 2 100.0
condition 6 10 60.0
subroutine 12 12 100.0
pod 1 2 50.0
total 62 67 92.5


line stmt bran cond sub pod time code
1             package Dancer2::Plugin::Cache::CHI;
2             our $AUTHORITY = 'cpan:YANICK';
3             # ABSTRACT: Dancer plugin to cache response content (and anything else)
4             $Dancer2::Plugin::Cache::CHI::VERSION = '2.0.0';
5 6     6   2607439 use strict;
  6         9  
  6         139  
6 6     6   20 use warnings;
  6         7  
  6         111  
7 6     6   16 use Carp;
  6         8  
  6         252  
8 6     6   3026 use CHI;
  6         167358  
  6         166  
9 6     6   2699 use List::MoreUtils qw/ any /;
  6         24889  
  6         44  
10              
11 6     6   5539 use Dancer2::Plugin;
  6         44458  
  6         30  
12              
13             plugin_hooks 'before_create_cache';
14              
15             # actually hold the ref to the args
16             has _cache_page => (
17             is => 'rw',
18             clearer => 'clear_cache_page',
19             predicate => 'has_cache_page',
20             );
21              
22             has cache_page_key_generator => (
23             is => 'rw',
24             lazy => 1,
25             plugin_keyword => 1,
26             default => sub {
27             sub { $_[0]->app->request->path }
28             },
29             );
30              
31             sub BUILD {
32 6     6 0 396 my $plugin = shift;
33              
34             $plugin->app->add_hook(
35             Dancer2::Core::Hook->new(
36             name => 'after',
37             code => sub {
38 32 100   32   140561 return unless $plugin->has_cache_page;
39              
40 13         37 my $resp = shift;
41             $plugin->cache->set( $plugin->cache_page_key_generator->($plugin),
42             {
43             status => $resp->status,
44             headers => $resp->headers_to_array,
45             content => $resp->content
46             },
47 13         24 @{$plugin->_cache_page},
  13         5134  
48             );
49              
50 13         4364 $plugin->clear_cache_page;
51             }
52 6         148 ));
53              
54             $plugin->app->add_hook(
55             Dancer2::Core::Hook->new(
56             name => 'before',
57             code => sub {
58 39     39   2657439 $plugin->clear_cache_page;
59             }
60 6         2195 ));
61             };
62              
63             has _caches => (
64             is => 'ro',
65             lazy => 1,
66             default => sub {
67             {}
68             },
69             );
70              
71             has caches_with_honor => (
72             is => 'ro',
73             default => sub{ {} },
74             );
75              
76             plugin_keywords 'cache';
77              
78             sub cache {
79 60     60 1 655 my( $self, $namespace ) = @_;
80 60   50     237 $namespace //= '';
81 60   66     839 return $self->_caches->{$namespace} ||= _create_cache( $self, $namespace, @_ );
82             };
83              
84             sub _create_cache {
85 5     5   11 my( $dsl, $namespace, $args ) = @_;
86 5   50     26 $args ||= {};
87              
88 5         26 $dsl->execute_hook( 'plugin.cache_chi.before_create_cache' );
89              
90 5         425 my %setting = ( %{ $dsl->config }, %$args );
  5         34  
91              
92 5   66     1769 $setting{namespace} //= $namespace;
93              
94 5         20 $dsl->caches_with_honor->{$namespace} = delete $setting{honor_no_cache};
95              
96 5         49 return CHI->new(%setting);
97             }
98              
99              
100             plugin_keywords check_page_cache => sub {
101             my $dsl = shift;
102              
103             my $hook = sub {
104             my $context = shift;
105              
106             # Instead halt() now we use a more correct method - setting of a
107             # response to Dancer2::Core::Response object for a more correct returning of
108             # some HTTP headers (X-Powered-By, Server)
109              
110             my $cached = cache($dsl)->get( $dsl->cache_page_key_generator->($dsl) )
111             or return;
112              
113             if ( $dsl->caches_with_honor->{''} ) {
114              
115             my $req = $dsl->app->request;
116              
117 6     6   13676 no warnings 'uninitialized';
  6         9  
  6         1499  
118              
119             return if any {
120             $req->header($_) eq 'no-cache'
121             } qw/ Cache-Control Pragma /;
122             }
123              
124             $context->set_response(
125             Dancer2::Core::Response->new(
126             is_halted => 1,
127             ref $cached eq 'HASH'
128             ?
129             ( map { $_ => $cached->{$_} } qw/ status headers content / )
130             :
131             ( content => $cached )
132             )
133             );
134             };
135              
136             $dsl->app->add_hook( Dancer2::Core::Hook->new(
137             name => 'before',
138             code => $hook,
139             ));
140             };
141              
142             plugin_keywords cache_page => sub {
143             my ( $plugin, $content, @args ) = @_;
144              
145             $plugin->_cache_page(\@args);
146              
147             return $content;
148             };
149              
150             plugin_keywords cache_page_key => sub { $_[0]->cache_page_key_generator->($_[0]) };
151              
152             for my $method ( qw/ set get remove clear compute / ) {
153             plugin_keywords "cache_$method" => sub {
154             my $plugin = shift;
155             $plugin->cache->$method(@_);
156             }
157             }
158              
159             1;
160              
161             __END__