File Coverage

blib/lib/CatalystX/ASP/GlobalASA.pm
Criterion Covered Total %
statement 52 59 88.1
branch 4 6 66.6
condition 1 3 33.3
subroutine 15 18 83.3
pod 9 11 81.8
total 81 97 83.5


line stmt bran cond sub pod time code
1             package CatalystX::ASP::GlobalASA;
2              
3 9     9   5030 use namespace::autoclean;
  9         12  
  9         84  
4 9     9   642 use Moose;
  9         14  
  9         56  
5 9     9   38656 use MooseX::Types::Path::Tiny qw(Path);
  9         16  
  9         107  
6 9     9   15253 use Path::Tiny;
  9         33  
  9         499  
7 9     9   36 use File::Slurp qw(read_file);
  9         10  
  9         3130  
8              
9             our @Routines = qw(
10             Application_OnStart
11             Application_OnEnd
12             Session_OnStart
13             Session_OnEnd
14             Script_OnStart
15             Script_OnEnd
16             Script_OnParse
17             Script_OnFlush
18             );
19              
20             has 'asp' => (
21             is => 'ro',
22             isa => 'CatalystX::ASP',
23             required => 1,
24             weak_ref => 1,
25             );
26              
27             =head1 NAME
28              
29             CatalystX::ASP::GlobalASA - global.asa
30              
31             =head1 SYNOPSIS
32              
33             ### in global.asa
34             sub Script_OnStart {
35             printf STDERR "Executing script: %s\n", $Request->ServerVariables('SCRIPT_NAME');
36             }
37              
38             =head1 DESCRIPTION
39              
40             The ASP platform allows developers to create Web Applications. In fulfillment of
41             real software requirements, ASP allows event-triggered actions to be taken,
42             which are defined in a F<global.asa> file. The global.asa file resides in the
43             C<Global> directory, defined as a config option, and may define the following actions:
44              
45             Action Event
46             ------ ------
47             Script_OnStart * - Beginning of Script execution
48             Script_OnEnd * - End of Script execution
49             Script_OnFlush * - Before $Response being flushed to client.
50             Script_OnParse * - Before script compilation
51             Application_OnStart - Beginning of Application
52             Application_OnEnd - End of Application
53             Session_OnStart - Beginning of user Session.
54             Session_OnEnd - End of user Session.
55              
56             * These are API extensions that are not portable, but were
57             added because they are incredibly useful
58              
59             These actions must be defined in the C<< "$self->Global/global.asa" >> file as
60             subroutines, for example:
61              
62             sub Session_OnStart {
63             $Application->{$Session->SessionID()} = started;
64             }
65              
66             Sessions are easy to understand. When visiting a page in a web application, each
67             user has one unique C<$Session>. This session expires, after which the user will
68             have a new C<$Session> upon revisiting.
69              
70             A web application starts when the user visits a page in that application, and
71             has a new C<$Session> created. Right before the first C<$Session> is created,
72             the C<$Application> is created. When the last user C<$Session> expires, that
73             C<$Application> expires also. For some web applications that are always busy,
74             the C<Application_OnEnd> event may never occur.
75              
76             =cut
77              
78             has 'filename' => (
79             is => 'ro',
80             isa => Path,
81             default => sub { shift->asp->Global->child( 'global.asa' ) },
82             );
83              
84             has 'package' => (
85             is => 'ro',
86             isa => 'Str',
87             lazy_build => 1,
88             );
89              
90             sub _build_package {
91 6     6   11 my ( $self ) = @_;
92 6         132 my $asp = $self->asp;
93 6         125 my $id = $asp->file_id( $asp->Global, 1 );
94 6   33     140 return $asp->GlobalPackage || "CatalystX::ASP::Compiles::$id";
95             }
96              
97             sub BUILD {
98 6     6 0 14 my ( $self ) = @_;
99 6         138 my $asp = $self->asp;
100 6         119 my $c = $asp->c;
101              
102 6 50       27 return unless $self->exists;
103              
104 6         287 my $package = $self->package;
105 6         138 my $filename = $self->filename;
106 6         127 my $global = $asp->Global;
107 6         39 my $code = read_file( $filename );
108 6         1328 my $match_events = join '|', @Routines;
109 6         610 $code =~ s/\<script[^>]*\>((.*)\s+sub\s+($match_events).*)\<\/script\>/$1/isg;
110             $code = join( '',
111             "\n#line 1 $filename\n",
112             join( ' ;; ',
113             "package $package;",
114             'no strict;',
115 6         32 'use vars qw(' . join( ' ', map {"\$$_"} @CatalystX::ASP::Objects ) . ');',
  30         107  
116             "use lib qw($global);",
117             $code,
118             'sub exit { $main::Response->End(); }',
119             "no lib qw($global);",
120             '1;',
121             )
122             );
123 6         69 $code =~ /^(.*)$/s; # Realized this is for untainting
124 6         18 $code = $1;
125              
126 9     9   44 no warnings;
  9         12  
  9         3611  
127 6         1646 eval $code; ## no critic (BuiltinFunctions::ProhibitStringyEval)
128 6 50       2586 if ( $@ ) {
129 0         0 $c->error( "Error on compilation of global.asa: $@" ); # don't throw error, so we can throw die later
130             }
131             }
132              
133 6     6 0 157 sub exists { shift->filename->exists }
134              
135             =head1 METHODS
136              
137             =over
138              
139             =item $self->execute_event($event);
140              
141             Execute the event defined in F<global.asa>
142              
143             =cut
144              
145             sub execute_event {
146 45     45 1 61 my ( $self, $event ) = @_;
147 45         1000 my $asp = $self->asp;
148 45 100       977 $asp->execute( $asp->c, $event ) if "$self->package"->can( $event );
149             }
150              
151             =item Application_OnStart
152              
153             This event marks the beginning of an ASP application, and is run just before the
154             C<Session_OnStart> of the first Session of an application. This event is useful
155             to load up C<$Application> with data that will be used in all user sessions.
156              
157             =cut
158              
159             sub Application_OnStart {
160 6     6 1 9 my ( $self ) = @_;
161 6         143 $self->execute_event( join( '::', $self->package, 'Application_OnStart' ) );
162             }
163              
164             =item Application_OnEnd
165              
166             The end of the application is marked by this event, which is run after the last
167             user session has timed out for a given ASP application.
168              
169             =cut
170              
171             sub Application_OnEnd {
172 0     0 1 0 my ( $self ) = @_;
173 0         0 $self->execute_event( join( '::', $self->package, 'Application_OnEnd' ) );
174             }
175              
176             =item Session_OnStart
177              
178             Triggered by the beginning of a user's session, C<Session_OnStart> gets run
179             before the user's executing script, and if the same session recently timed out,
180             after the session's triggered C<Session_OnEnd>.
181              
182             The C<Session_OnStart> is particularly useful for caching database data, and
183             avoids having the caching handled by clumsy code inserted into each script being
184             executed.
185              
186             =cut
187              
188             sub Session_OnStart {
189 0     0 1 0 my ( $self ) = @_;
190 0         0 $self->execute_event( join( '::', $self->package, 'Session_OnStart' ) );
191             }
192              
193             =item Session_OnEnd
194              
195             Triggered by a user session ending, C<Session_OnEnd> can be useful for cleaning
196             up and analyzing user data accumulated during a session.
197              
198             Sessions end when the session timeout expires, and the C<StateManager> performs
199             session cleanup. The timing of the C<Session_OnEnd> does not occur immediately
200             after the session times out, but when the first script runs after the session
201             expires, and the C<StateManager> allows for that session to be cleaned up.
202              
203             So on a busy site with default C<SessionTimeout> (20 minutes) and
204             C<StateManager> (10 times) settings, the C<Session_OnEnd> for a particular
205             session should be run near 22 minutes past the last activity that Session saw.
206             A site infrequently visited will only have the C<Session_OnEnd> run when a
207             subsequent visit occurs, and theoretically the last session of an application
208             ever run will never have its C<Session_OnEnd> run.
209              
210             Thus I would not put anything mission-critical in the C<Session_OnEnd>, just
211             stuff that would be nice to run whenever it gets run.
212              
213             =cut
214              
215             sub Session_OnEnd {
216 0     0 1 0 my ( $self ) = @_;
217 0         0 $self->execute_event( join( '::', $self->package, 'Session_OnEnd' ) );
218             }
219              
220             =item Script_OnStart
221              
222             The script events are used to run any code for all scripts in an application
223             defined by a F<global.asa>. Often, you would like to run the same code for every
224             script, which you would otherwise have to add by hand, or add with a file
225             include, but with these events, just add your code to the F<global.asa>, and it
226             will be run. This runs before a script is executed.
227              
228             =cut
229              
230             sub Script_OnStart {
231 9     9 1 12 my ( $self ) = @_;
232 9         254 $self->execute_event( join( '::', $self->package, 'Script_OnStart' ) );
233             }
234              
235             =item Script_OnEnd
236              
237             Like C<Script_OnStart> except at the end.
238              
239             There is one caveat. Code in C<Script_OnEnd> is not guaranteed to be run when
240             C<< $Response->End() >> is called, since the program execution ends immediately
241             at this event. To always run critical code, use the API extension:
242              
243             $Server->RegisterCleanup()
244              
245             =cut
246              
247             sub Script_OnEnd {
248 9     9 1 13 my ( $self ) = @_;
249 9         211 $self->execute_event( join( '::', $self->package, 'Script_OnEnd' ) );
250             }
251              
252             =item Script_OnParse
253              
254             This event allows one to set up a source filter on the script text, allowing one
255             to change the script on the fly before the compilation stage occurs. The script
256             text is available in the C<< $Server->{ScriptRef} >> scalar reference, and can
257             be accessed like so:
258              
259             sub Script_OnParse {
260             my $code = $Server->{ScriptRef}
261             $$code .= " ADDED SOMETHING ";
262             }
263              
264             =cut
265              
266             sub Script_OnParse {
267 12     12 1 22 my ( $self ) = @_;
268 12         321 $self->execute_event( join( '::', $self->package, 'Script_OnParse' ) );
269             }
270              
271             =item Script_OnFlush
272              
273             API extension. This event will be called prior to flushing the C<$Response>
274             buffer to the web client. At this time, the C<< $Response->{BinaryRef} >> buffer
275             reference may be used to modify the buffered output at runtime to apply global
276             changes to scripts output without having to modify all the scripts.
277              
278             sub Script_OnFlush {
279             my $ref = $Response->{BinaryRef};
280             $$ref =~ s/\s+/ /sg; # to strip extra white space
281             }
282              
283             =cut
284              
285             sub Script_OnFlush {
286 9     9 1 14 my ( $self ) = @_;
287 9         209 $self->execute_event( join( '::', $self->package, 'Script_OnFlush' ) );
288             }
289              
290              
291             __PACKAGE__->meta->make_immutable;
292              
293             =back
294              
295             =head1 SEE ALSO
296              
297             =over
298              
299             =item * L<CatalystX::ASP>
300              
301             =back