File Coverage

blib/lib/Rapi/Blog/Scaffold.pm
Criterion Covered Total %
statement 68 112 60.7
branch 10 38 26.3
condition 1 12 8.3
subroutine 18 38 47.3
pod 0 12 0.0
total 97 212 45.7


line stmt bran cond sub pod time code
1             package Rapi::Blog::Scaffold;
2 2     2   584 use strict;
  2         4  
  2         47  
3 2     2   10 use warnings;
  2         2  
  2         70  
4              
5 2     2   419 use RapidApp::Util qw(:all);
  2         1410921  
  2         805  
6 2     2   875 use Rapi::Blog::Util;
  2         7  
  2         65  
7 2     2   10 use Scalar::Util 'blessed';
  2         4  
  2         77  
8 2     2   10 use List::Util;
  2         5  
  2         83  
9 2     2   10 use String::Random;
  2         3  
  2         78  
10             require Text::Glob;
11              
12 2     2   10 use Moo;
  2         3  
  2         8  
13 2     2   4363 use Types::Standard ':all';
  2         3  
  2         13  
14              
15 2     2   77939 use Rapi::Blog::Scaffold::Config;
  2         6  
  2         59  
16 2     2   762 use Rapi::Blog::Scaffold::ViewWrapper;
  2         7  
  2         64  
17              
18 2     2   795 use Plack::App::File;
  2         13250  
  2         54  
19 2     2   747 use Plack::Builder;
  2         6779  
  2         171  
20 2     2   760 use Plack::Middleware::ConditionalGET;
  2         924  
  2         78  
21              
22             require Path::Class;
23 2     2   36 use YAML::XS 0.64 'LoadFile';
  2         45  
  2         2604  
24              
25              
26             sub factory {
27 0     0 0 0 my ($self, $new) = @_;
28            
29             # passthrough is its already one of us:
30 0 0 0     0 return $new if (ref($new) && ref($new) eq __PACKAGE__);
31            
32 0         0 __PACKAGE__->new( dir => $new )
33             }
34              
35              
36             has 'uuid', is => 'ro', init_arg => undef,
37             default => sub { join('-','scfld',String::Random->new->randregex('[a-z0-9A-Z]{20}')) };
38              
39             has 'dir',
40             is => 'ro',
41             required => 1,
42             isa => InstanceOf['Path::Class::Dir'],
43             coerce => sub { Path::Class::dir($_[0]) };
44              
45              
46             has 'config',
47             is => 'ro',
48             isa => InstanceOf['Rapi::Blog::Scaffold::Config'],
49             default => sub {{}},
50             coerce => sub { blessed $_[0] ? $_[0] : Rapi::Blog::Scaffold::Config->new($_[0]) };
51              
52             # The Scaffold needs to be able to check if a given Post exists in the database
53             #has 'Post_exists_fn', is => 'ro', required => 1, isa => CodeRef;
54              
55              
56 0     0 0 0 sub static_paths { (shift)->config->static_paths }
57 0     0 0 0 sub template_names { (shift)->config->template_names }
58 0     0 0 0 sub private_paths { (shift)->config->private_paths }
59 0     0 0 0 sub default_ext { (shift)->config->default_ext }
60 0     0 0 0 sub view_wrappers { (shift)->config->view_wrappers }
61 0     0 0 0 sub internal_post_path { (shift)->config->internal_post_path }
62              
63             # This is a unique, private path which is automatically generated that allows this
64             # scaffold to own a path which it can use fetch a post, and be sure another scaffold
65             # wont claim the path
66             has 'unique_int_post_path', is => 'ro', init_arg => undef, lazy => 1, default => sub {
67             my $self = shift;
68             join('','_',$self->uuid,'/private/post/')
69             };
70              
71 0     0 0 0 sub not_found_template { (shift)->config->not_found }
72              
73              
74              
75             has 'ViewWrappers', is => 'ro', init_arg => undef, lazy => 1, default => sub {
76             my $self = shift;
77             return [ map {
78             Rapi::Blog::Scaffold::ViewWrapper->new(
79             Scaffold => $self, %$_
80             )
81             } @{$self->config->view_wrappers} ]
82             }, isa => ArrayRef[InstanceOf['Rapi::Blog::Scaffold::ViewWrapper']];
83              
84              
85              
86              
87             sub BUILD {
88 0     0 0 0 my $self = shift;
89 0         0 $self->_load_yaml_config;
90             }
91              
92              
93             sub _load_yaml_config {
94 0     0   0 my $self = shift;
95            
96 0         0 my $yaml_file = $self->dir->file('scaffold.yml');
97 0 0       0 $self->config->_load_from_yaml($yaml_file) if (-f $yaml_file);
98             }
99              
100              
101             sub resolve_ViewWrapper {
102 0     0 0 0 my $self = shift;
103 0 0       0 my $path = shift or return undef;
104            
105 0         0 my $subpath;
106 0     0   0 my $VW = List::Util::first { $subpath = $_->resolve_subpath($path) } @{ $self->ViewWrappers };
  0         0  
  0         0  
107 0 0       0 return undef unless $VW;
108            
109 0 0       0 wantarray ? ($VW, $subpath) : $VW
110             }
111              
112              
113              
114             sub _resolve_path_to_post {
115 0     0   0 my ($self, $path) = @_;
116            
117 0         0 my ($pfx,$name) = split($self->unique_int_post_path,$path,2);
118 0 0 0     0 ($name && $pfx eq '') ? $name : undef
119             }
120              
121              
122              
123             has '_static_path_regexp', is => 'ro', lazy => 1, default => sub {
124             my $self = shift;
125             return $self->_compile_path_list_regex(@{$self->static_paths});
126             };
127              
128             has '_template_name_regexp', is => 'ro', lazy => 1, default => sub {
129             my $self = shift;
130             return $self->_compile_path_list_regex(@{$self->template_names});
131             };
132              
133             has '_private_path_regexp', is => 'ro', lazy => 1, default => sub {
134             my $self = shift;
135             return $self->_compile_path_list_regex(@{$self->private_paths});
136             };
137              
138              
139             sub _compile_path_list_regex {
140 26     26   6971 my ($self, @paths) = @_;
141 26         59 my $reStr = $self->_get_path_list_regex_string(@paths);# or return undef;
142 26         203 qr/$reStr/
143             }
144              
145              
146 0     0   0 sub __re_always { '(^.+$)' } # Always matches
147 0     0   0 sub __re_never { '(?=a)[^a]' } # Never matches
148              
149             sub _glob_to_re_str {
150 36     36   51 my ($self,$glob) = @_;
151            
152 36         75 my $re = Text::Glob::glob_to_regex_string($glob);
153            
154             # unless the glob ends in a *, we do not
155             # want to match past the end of the pattern
156 36 100       2893 $glob =~ /\*$/ ? $re : $re.'$'
157             }
158              
159             sub _get_path_list_regex_string {
160 26     26   43 my ($self, @paths) = @_;
161 26 50       56 return undef unless (scalar(@paths) > 0);
162            
163 26         35 my @gList = ();
164 26         32 my @eqList = ();
165 26         34 for my $path (@paths) {
166 84 50       134 next if ($path eq ''); # empty string match nothing
167            
168             # Either * or / match everything. Bail out and return the match everything regex
169 84 50 33     201 return $self->__re_always if ($path eq '*' || $path eq '/');
170              
171 84         116 $path =~ s/^\///; # strip and ignore leading /
172 84 100       162 $path .= '*' if ($path =~ /\/$/); # append wildcard if we end in / we want to match everything that follows
173            
174             # Check for any glob pattern special characters:
175 84 100       199 $path =~ /[\*\?\[\]\{\}]/ ? push(@gList, $path) : push(@eqList,$path);
176             }
177            
178 26         43 local $Text::Glob::strict_leading_dot = 0;
179 26         27 local $Text::Glob::strict_wildcard_slash = 0;
180            
181             my @re_list = (
182 36         54 (map { $self->_glob_to_re_str($_) } @gList ),
183 26         36 (map { join('','^',quotemeta($_),'$') } @eqList )
  48         99  
184             );
185            
186 26         59 my $reStr = join('','(',join('|', @re_list ),')');
187            
188 26 50       67 $reStr ? $reStr : $self->__re_never
189             }
190              
191              
192              
193              
194             has 'static_path_app', is => 'ro', lazy => 1, default => sub {
195             my $self = shift;
196             my $app = builder {
197             enable "ConditionalGET";
198             Plack::App::File->new(root => $self->dir)->to_app;
199             };
200            
201             sub {
202             my $env = shift;
203             my $res = $app->($env);
204             # limit caching to 10 minutes now that we return 304s
205             push @{$res->[1]}, 'Cache-Control', 'public, max-age=600';
206            
207             $res
208             }
209             };
210              
211              
212              
213             sub _is_static_path {
214 0     0     my ($self, $template) = @_;
215 0           my $Regexp = $self->_static_path_regexp;
216 0 0         $Regexp ? $template =~ $Regexp : 0
217             }
218              
219             sub _is_valid_template_name {
220 0     0     my ($self, $name) = @_;
221 0           my $Regexp = $self->_template_name_regexp;
222 0 0         $Regexp ? $name =~ $Regexp : 0
223             }
224              
225             sub _is_private_path {
226 0     0     my ($self, $template) = @_;
227 0           my $Regexp = $self->_private_path_regexp;
228 0 0         $Regexp ? $template =~ $Regexp : 0
229             }
230              
231              
232             sub resolve_file {
233 0     0 0   my $self = shift;
234 0 0         my $path = shift or return undef;
235            
236 0           my $File = $self->dir->file($path);
237 0 0         -f $File ? $File : undef
238             }
239              
240              
241             sub resolve_static_file {
242 0     0 0   my ($self, $path) = @_;
243 0 0 0       $path && $self->_is_static_path($path) or return undef;
244 0           $self->resolve_file($path)
245             }
246              
247              
248             1;