File Coverage

blib/lib/Mojolicious/Plugin/CHI/Route.pm
Criterion Covered Total %
statement 66 68 97.0
branch 22 26 84.6
condition 10 17 58.8
subroutine 8 8 100.0
pod 1 1 100.0
total 107 120 89.1


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::CHI::Route;
2 2     2   1673 use Mojo::Base 'Mojolicious::Plugin';
  2         5  
  2         15  
3 2     2   420 use Mojo::Util qw'md5_sum';
  2         12  
  2         92  
4 2     2   11 use Mojo::Date;
  2         4  
  2         15  
5              
6             our $VERSION = '0.05';
7              
8             # Register plugin
9             sub register {
10 2     2 1 253193 my ($plugin, $app, $param) = @_;
11              
12             # Plugin parameter
13 2   50     13 $param ||= {};
14              
15             # Load parameter from Config file
16 2 50       13 if (my $config_param = $app->config('CHI-Route')) {
17 0         0 $param = { %$param, %$config_param };
18             };
19              
20             # Load CHI plugin if not already loaded
21 2 50       35 unless (exists $app->renderer->helpers->{chi}) {
22 0         0 $app->plugin('CHI');
23             };
24              
25             # set namespace
26 2   50     29 my $namespace = $param->{namespace} // 'default';
27              
28 2 100       15 unless ($app->chi($namespace)) {
29 1         191 $app->log->error("No cache defined for '$namespace'");
30              
31             # Add condition to check for cache
32             $app->routes->add_condition(
33 2     2   29705 chi => sub { 1 }
34 1         19 );
35              
36 1         38 return;
37             };
38              
39 1   50     191 my $expires_in = $param->{expires_in} // '6 hours';
40              
41             # Set default key
42             my $default_key = $param->{key} // sub {
43 2     2   7 return shift->req->url->to_abs->to_string;
44 1   50     13 };
45              
46             # Cache if required
47             $app->hook(
48             after_render => sub {
49 12     12   2650 my ($c, $output, $format) = @_;
50              
51             # No cache instruction found
52 12 100       35 return unless $c->stash('chi.r.cache');
53              
54             # Only cache successfull renderings
55 5 100 66     59 if ($c->res->is_error || ($c->stash('status') && $c->stash('status') != 200)) {
      66        
56 2         81 return;
57             };
58              
59             # Get key from stash
60 3         123 my $key = $c->stash('chi.r.cache');
61              
62             # Set ETag and last_modified headers
63 3         43 my $last_modified = Mojo::Date->new;
64 3         41 my $headers;
65 3         9 foreach ($c->res->headers) {
66 3         60 $_->last_modified($last_modified);
67 3         26 $_->etag('W/"' . md5_sum($last_modified->to_string). '"');
68 3         131 $headers = $_->to_hash;
69              
70             # Delete Mojolicious server header
71 3         207 delete $headers->{Server};
72             };
73              
74             # Cache
75 3   66     13 $c->chi($namespace)->set(
76             $key => {
77             'body' => $$output,
78             'format' => $format,
79             'headers' => $headers
80             } => {
81             expires_in => $c->stash('chi.r.expires') // $expires_in
82             }
83             );
84             }
85 1         10 );
86              
87             # Add condition to check for cache
88             $app->routes->add_condition(
89             chi => sub {
90 14     14   150085 my ($r, $c, $captures, $arg) = @_;
91              
92             # Only cache GET requests
93 14 50       61 return 1 if $c->req->method ne 'GET';
94              
95 14         242 my $chi = $c->chi($namespace);
96              
97             # No cache associated
98 14 50       268 return 1 unless $chi;
99              
100             # Get the key for the cache
101 14         23 my $key;
102              
103             # Set by argument
104 14 100       42 if ($arg->{key}) {
105 12 100       53 $key = ref $arg->{key} ? $arg->{key}->($c) : $arg->{key} . '';
106             }
107              
108             # Set
109             else {
110 2         7 $key = $default_key->($c);
111             };
112              
113 14 100       1331 return 1 unless $key;
114              
115             # Get cache based on key
116 12         52 my $found = $chi->get($key);
117              
118             # Found cache! Render
119 12 100       1323 if ($found) {
120              
121 7         24 $c->stash->{'mojo.routed'} = 1;
122              
123 7         72 my $headers = $found->{headers};
124 7         15 my $etag = delete $headers->{'ETag'};
125 7         16 my $last_modified = delete $headers->{'Last-Modified'};
126              
127             # Check if client side cache is still fresh
128 7 100       27 if ($c->is_fresh(
129             etag => $etag,
130             last_modified => $last_modified
131             )) {
132 2         524 $c->log->debug('Client side cache is still valid');
133 2         56 $c->rendered(304);
134             }
135              
136             # Client has no valid copy of the cache
137             else {
138              
139 5         961 $c->log->debug('Routing to a cache');
140              
141 5         135 for ($c->res) {
142 5         60 $_->headers->from_hash($headers);
143 5         173 $_->headers->header('X-Cache-CHI' => 1);
144 5         149 $_->code(200);
145             };
146              
147             # Render from cache
148             $c->render(
149             'format' => $found->{format},
150             'data' => $found->{body}
151 5         60 );
152             };
153              
154             # Skip to final
155 7         1523 $c->match->position(1000);
156             }
157              
158             # Render normally and then cache the route
159             else {
160              
161 5 100       15 if (exists $arg->{expires_in}) {
162 1         5 $c->stash('chi.r.expires' => $arg->{expires_in});
163             };
164              
165 5         44 $c->stash('chi.r.cache' => $key);
166             };
167              
168 12         177 return 1;
169             }
170 1         25 );
171             };
172              
173              
174             1;
175              
176              
177             __END__