File Coverage

blib/lib/RapidApp/RapidApp.pm
Criterion Covered Total %
statement 61 91 67.0
branch 7 18 38.8
condition 4 13 30.7
subroutine 16 21 76.1
pod 0 11 0.0
total 88 154 57.1


line stmt bran cond sub pod time code
1             package RapidApp::RapidApp;
2              
3 4     4   62 use Moose;
  4         11  
  4         27  
4 4     4   23052 use namespace::autoclean;
  4         9  
  4         35  
5             extends 'Catalyst::Model';
6              
7             # This makes $c->_app available, just like in Controllers:
8             with 'Catalyst::Component::ApplicationAttribute';
9              
10 4     4   342 use RapidApp::Util qw(:all);
  4         9  
  4         1585  
11 4     4   36 use Time::HiRes qw(gettimeofday);
  4         7  
  4         34  
12 4     4   381 use File::Spec;
  4         9  
  4         119  
13 4     4   21 use Module::Runtime qw( use_module );
  4         7  
  4         29  
14              
15             # the package name of the catalyst application, i.e. "GreenSheet" or "HOPS"
16             has 'catalystAppClass', is => 'ro', isa => 'Str', lazy => 1,
17             default => sub { (shift)->_app };
18              
19             # the class name of the root module
20             has 'rootModuleClass' => ( is => 'rw', isa => 'Str', lazy_build => 1 );
21             sub _build_rootModuleClass {
22 0     0   0 return (shift)->defaultRootModuleClass;
23             }
24              
25             # the default root module class name
26             sub defaultRootModuleClass {
27 0     0 0 0 return (shift)->catalystAppClass . '::Modules::Root';
28             }
29              
30             # the config hash for the modules
31             has 'rootModuleConfig' => ( is => 'rw', isa => 'HashRef' );
32              
33             # whether to preload the modules at catalyst setup time
34             has 'preloadModules' => ( is => 'rw', isa => 'Bool', default => 1 );
35              
36             # the root model instance
37             has 'rootModule' => ( is => 'rw', lazy_build => 1 );
38              
39             has 'postprocessing_tasks' => ( is => 'rw', isa => 'ArrayRef', default => sub {[]} );
40              
41             sub add_postprocessing_task {
42 0     0 0 0 my $self= shift;
43 0         0 push @{$self->postprocessing_tasks}, @_;
  0         0  
44             }
45              
46             sub BUILD {
47 4     4 0 7145 my $self = shift;
48 4         39 $RapidApp::CATALYST_CLASS = $self->_app;
49             }
50              
51             sub _setup_finalize {
52 4     4   369 my $self= shift;
53 4 50 33     164 $self->performModulePreload() if ($self->preloadModules && !$ENV{NO_PRELOAD_MODULES});
54             }
55              
56             sub _build_rootModule {
57 0     0   0 my $self= shift;
58            
59 0         0 return;
60            
61             # if we're doing this at runtime, just load the module.
62 0 0       0 if (RapidApp->active_request_context) {
63 0         0 return $self->_load_root_module;
64             }
65             # else, we're preloading, and we want diagnostics
66             else {
67 0         0 $self->performModulePreload;
68 0         0 return $self->rootModule;
69             }
70             }
71              
72             sub _load_root_module {
73 4     4   15 my $self= shift;
74            
75             #my $log= sEnv->log;
76             #sEnv->catalystClass->debug
77             # and $log->debug("Running require on root module ".$self->rootModuleClass);
78             #$log->_flush if $log->can('_flush');
79 4         136 Catalyst::Utils::ensure_class_loaded($self->rootModuleClass);
80            
81 4   50     260 my $mParams= $self->rootModuleConfig || {};
82 4         53 $mParams->{app} = $self->_app;
83 4         198 $mParams->{module_name}= '';
84 4         31 $mParams->{module_path}= '/';
85 4         21 $mParams->{parent_module_ref}= undef;
86            
87 4   50     22 my $cfg = $self->_app->config->{'RapidApp'} || {};
88 4 50       482 $mParams->{auto_init_modules} = $cfg->{load_modules} if ($cfg->{load_modules});
89              
90 4         116 $self->rootModule($self->rootModuleClass->timed_new($mParams));
91            
92             }
93              
94             sub performModulePreload {
95 4     4 0 14 my $self= shift;
96            
97             # Access the root module, causing it to get built
98             # We set RapidAppModuleLoadTimeTracker to instruct the modules to record their load times.
99             #if ($self->catalystAppClass->debug) {
100             # my $loadTimes= {};
101             # sEnv->applyForSub(
102             # { RapidAppModuleLoadTimeTracker => $loadTimes },
103             # sub { $self->rootModule($self->_load_root_module) }
104             # );
105             # scalar(keys %$loadTimes)
106             # and $self->displayLoadTimes($loadTimes);
107             #}
108             #else {
109 4         41 $self->rootModule($self->_load_root_module);
110             #}
111             }
112              
113             #sub displayLoadTimes {
114             # my ($self, $loadTimes)= @_;
115             #
116             # my $bar= '--------------------------------------------------------------------------------------';
117             # my $summary= "Loaded RapidApp Modules:\n";
118             # my @colWid= ( 25, 50, 7 );
119             # $summary.= sprintf(".%.*s+%.*s+%.*s.\n", $colWid[0], $bar, $colWid[1], $bar, $colWid[2], $bar);
120             # $summary.= sprintf("|%*s|%*s|%*s|\n", -$colWid[0], ' Module', -$colWid[1], ' Path', -$colWid[2], ' Time');
121             # $summary.= sprintf("+%.*s+%.*s+%.*s+\n", $colWid[0], $bar, $colWid[1], $bar, $colWid[2], $bar);
122             # my @prevPath= ();
123             # for my $key (sort keys %$loadTimes) {
124             # my ($path, $module, $time)= ($key, $loadTimes->{$key}->{module}, $loadTimes->{$key}->{loadTime});
125             # $path=~ s|[^/]*?/| /|g;
126             # $path=~ s|^ /|/|;
127             # $module =~ s/^(.*::)//; # trim the leading portion of the package name
128             # $module = substr($module, -$colWid[0]); # cut of the front of the string if necesary
129             # $path= substr($path, -$colWid[1]);
130             # $summary.= sprintf("| %*s| %*s| %*.3f |\n", -($colWid[0]-1), $module, -($colWid[1]-1), $path, $colWid[2]-2, $time);
131             # }
132             # $summary.= sprintf("'%.*s+%.*s+%.*s'\n", $colWid[0], $bar, $colWid[1], $bar, $colWid[2], $bar);
133             # $summary.= "\n";
134             #
135             # sEnv->log->debug($summary);
136             #}
137              
138             sub largestCommonPrefix {
139 0     0 0 0 my ($a, $b)= @_;
140 0         0 my $i= 0;
141             }
142              
143             sub module {
144 17     17 0 71 my ($self, @path)= @_;
145 17 50       74 if (scalar(@path) == 1) { # if path is a string, break it into its components
146 17         63 @path= split('/', $path[0]);
147             }
148 17         54 @path= grep /.+/, @path; # ignore empty strings
149            
150 17         444 my $m= $self->rootModule;
151 17         52 for my $part (@path) {
152 0 0       0 $m= $m->Module($part) or die "No such module: ".join('/',@path);
153             }
154 17         76 return $m;
155             }
156              
157             has 'dirtyModules' => ( is => 'rw', isa => 'HashRef', default => sub {{}} );
158              
159             sub markDirtyModule {
160 83     83 0 4542 my ($self, $module)= @_;
161 83         2180 $self->dirtyModules->{$module}= $module;
162             }
163              
164              
165             # The need for this cleanup process is a design flaw in the internals of
166             # RapidApp 'Modules' that will be factored away soon...
167             sub cleanupAfterRequest {
168 50     50 0 3540 my ($self, $c)= @_;
169 50 100       111 return unless scalar(keys %{$self->dirtyModules} );
  50         1502  
170            
171 17   33     69 my ($sec0, $msec0)= $c->debug && gettimeofday;
172            
173 17         168 $self->cleanDirtyModules($c);
174            
175 17 50       99 if ($c->debug) {
176 0         0 my ($sec1, $msec1)= gettimeofday;
177 0         0 my $elapsed= ($sec1-$sec0)+($msec1-$msec0)*.000001;
178            
179 0         0 $c->log->info(sprintf("Module init (ONREQUEST) took %0.3f seconds", $c->stash->{onrequest_time_elapsed}));
180 0         0 $c->log->info(sprintf("Cleanup took %0.3f seconds", $elapsed));
181             }
182            
183             # Now that the request is done, we can run post-processing tasks.
184             # These might also get modules dirty, so we clean again after each one.
185 17 50       80 if (scalar @{$self->postprocessing_tasks}) {
  17         443  
186 0   0     0 my ($sec0, $msec0)= $c->debug && gettimeofday;
187 0         0 my $reqid= $c->request_id;
188 0         0 my $i= 1;
189 0         0 while (my $sub= shift @{$self->postprocessing_tasks}) {
  0         0  
190 0         0 local $c->{request_id}= $reqid.'.'.$i++;
191 0         0 $sub->($c);
192 0         0 $self->cleanDirtyModules($c);
193             }
194            
195 0 0       0 if ($c->debug) {
196 0         0 my ($sec1, $msec1)= gettimeofday;
197 0         0 my $elapsed= ($sec1-$sec0)+($msec1-$msec0)*.000001;
198            
199 0         0 $c->log->info(sprintf("Post-processing tasks took %0.3f seconds", $elapsed));
200             }
201             }
202             }
203              
204             sub cleanDirtyModules {
205 17     17 0 46 my ($self, $c)= @_;
206 17         36 my @modules= values %{$self->dirtyModules};
  17         378  
207 17         45 for my $module (@modules) {
208 83         586 $module->reset_per_req_attrs;
209             }
210 17         38 %{$self->dirtyModules}= ();
  17         419  
211             }
212              
213             has '_requestCount' => ( is => 'rw', default => 0 );
214             sub requestCount {
215 50     50 0 3925 (shift)->_requestCount;
216             }
217             sub incRequestCount {
218 50     50 0 3022 my $self= shift;
219 50         1619 $self->_requestCount($self->_requestCount+1);
220             }
221              
222             # RapidApp System Cache
223             has 'use_cache' => ( is => 'ro', lazy => 1, default => (
224             defined $ENV{RAPIDAPP_NO_CACHE}
225             ? ( $ENV{RAPIDAPP_NO_CACHE} ? 0 : 1 )
226             : 1
227             ));
228              
229             has 'cache_class' => ( is => 'ro', lazy => 1, default => 'CHI' );
230              
231             has 'cache_opts' => ( is => 'ro', predicate => 'has_cache_opts' );
232              
233             has 'cache_dir' => ( is => 'ro', lazy => 1, default => sub {
234             my ( $self ) = @_;
235             return File::Spec->catfile(File::Spec->tmpdir, 'RapidAppCache');
236             });
237              
238             has 'cache' => ( is => 'ro', default => sub {
239             my ( $self ) = @_;
240             my %cache_opts;
241             if ($self->has_cache_opts) {
242             %cache_opts = %{$self->cache_opts};
243             } else {
244             %cache_opts = (
245             driver => 'File',
246             root_dir => $self->cache_dir,
247             depth => 5,
248             namespace => $self->cache_key,
249             );
250             }
251             my $cache_class = $self->cache_class;
252             Catalyst::Utils::ensure_class_loaded($cache_class);
253             my $cache = $cache_class->new( %cache_opts );
254             if ($ENV{RAPIDAPP_CLEAR_CACHE}) {
255             $cache->clear();
256             }
257             return $cache;
258             });
259              
260             # so far for the main namespace, will probably be changed, every
261             # systempart using the cache should make a clear key for the specific
262             # input and code that
263             has 'cache_key' => ( is => 'ro', lazy => 1, default => sub {
264             my ( $self ) = @_;
265             my $class = ref $self;
266             $class =~ s/::/_/g;
267             return join('_',
268             $class, map { use_module($_)->VERSION } (qw(
269             RapidApp DBIx::Class SQL::Translator Catalyst::Runtime
270             )),
271             );
272             });
273              
274             1;