File Coverage

lib/OpenFrame/AppKit/App.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package OpenFrame::AppKit::App;
2              
3 1     1   20560 use strict;
  1         3  
  1         33  
4 1     1   5 use warnings::register;
  1         1  
  1         163  
5              
6             our $VERSION=3.03;
7              
8 1     1   937 use Storable qw ( dclone );
  1         3475  
  1         67  
9 1     1   437 use OpenFrame::AppKit::Segment::DispatchOnURI;
  0            
  0            
10             use base qw ( OpenFrame::AppKit::Segment::DispatchOnURI );
11              
12             sub init {
13             my $self = shift;
14              
15             $self->{'::appkit'} = undef;
16              
17             $self->config( {} ); ## initialize the configuration
18              
19             $self->uri( '/' ); ## create a default place for it to execute on
20             $self->namespace( 'default' ); ## create a default namespace for it to execute with
21              
22             $self->SUPER::init( @_ );
23             }
24              
25             sub request {
26             my $self = shift;
27             my $req = shift;
28             if (defined($req)) {
29             $self->{'::appkit'}->{request} = $req;
30             return $self;
31             } else {
32             return $self->{'::appkit'}{request};
33             }
34             }
35              
36             #sub uri {
37             # my $self = shift;
38             # my $pattern = shift;
39             # if ( defined( $pattern ) ) {
40             # if( ref($pattern) ) {
41             # $self->{'::appkit'}->{ execute_on_uris_matching } = $pattern;
42             # return $self;
43             # } else {
44             # return $self->uri( qr/$pattern/ );
45             # }
46             # } else {
47             # return $self->{'::appkit'}->{ execute_on_uris_matching };
48             # }
49             #}
50              
51             sub namespace {
52             my $self = shift;
53             my $ns = shift;
54             if (defined( $ns )) {
55             $self->{'::appkit'}{ ns } = $ns;
56             return $self;
57             } else {
58             return $self->{'::appkit'}->{ns};
59             }
60             }
61              
62             sub _copy_app_from_namespace {
63             my $self = shift;
64             my $session = shift;
65             my $namespace = $self->namespace || '';
66              
67             $self->emit("namespace is $namespace");
68            
69             if ($session->{application}->{ $namespace }) {
70             my $copy = dclone($session->{application}->{ $namespace });
71             foreach my $key (keys %$copy) {
72             $self->{$key} = $copy->{ $key };
73             }
74             }
75             }
76              
77             sub dispatch_on_uri {
78             my $self = shift;
79             my $pipe = shift;
80              
81             my $store = $pipe->store();
82             my $request = $store->get('OpenFrame::Request');
83              
84             if (!$request) { return undef; }
85              
86             my $session = $self->get_session( $store );
87             delete $session->{ app };
88              
89             $self->_copy_app_from_namespace( $session );
90              
91             $self->request( $request ); ## set the request
92              
93             my @results = $self->_enter( $store );
94              
95             $self->request(''); ## clear the request
96              
97             my $namespace = $self->namespace || '';
98             my %hashcopy = %{$self};
99             $session->{app} = \%hashcopy;
100             $session->{application}->{ $namespace } = \%hashcopy;
101              
102             return @results;
103             }
104              
105             sub get_session {
106             my $self = shift;
107             my $store = shift;
108             my $session = $store->get('OpenFrame::AppKit::Session');
109             return $session;
110             }
111              
112             sub get_entry {
113             my $self = shift;
114             my $store = shift;
115              
116             my $args = $store->get('OpenFrame::Request')->arguments();
117             my $epnt = $self->_get_entry_points();
118              
119             my $dispatch = "get_entry_" . ref($epnt);
120             my $method;
121             eval {
122             $method = $self->$dispatch( $args, $epnt );
123             };
124             if ($@) {
125             $self->emit("could not call $dispatch");
126             }
127              
128             return $method || 'default';
129             }
130              
131             sub get_entry_HASH {
132             my $self = shift;
133             my $args = shift;
134             my $epnt = shift;
135             my $method;
136             foreach my $point ( keys %$epnt ) {
137             ## we have a hash
138             if ($self->_match_hash_arguments( $args, $epnt->{ $point } )) {
139             $method = $point;
140             last;
141             }
142             }
143             return $method;
144             }
145              
146             sub _enter {
147             my $self = shift;
148             my $store = shift;
149              
150             my $method = $self->get_entry( $store );
151              
152             my $sub = $self->can($method);
153             if ($sub) {
154             return ($sub->($self, $store));
155             } else {
156             ## can't do anything
157             }
158             }
159              
160             sub default {}
161              
162             sub _match_hash_arguments {
163             my $self = shift;
164             my $args = shift;
165             my $against = shift;
166              
167             my $count = scalar(@$against);
168             my $match = 0;
169             foreach my $wanted (@$against) {
170             if (ref $wanted eq 'ARRAY') {
171             my $wantarg = $wanted->[0];
172             my $lnot = 0;
173             if (substr($wantarg,0,1) eq '!') {
174             $wantarg = substr($wantarg, 1);
175             $lnot = 1;
176             }
177             if (exists $args->{ $wantarg }) {
178             my $argvalue = $args->{ $wantarg };
179             foreach my $wantvalue (@$wanted[1..$#$wanted]) {
180             if (ref $wantvalue eq 'Regexp') {
181             if ($lnot) {
182             $match--, last if $argvalue =~ $wantvalue;
183             } else {
184             $match++, last if $argvalue =~ $wantvalue;
185             }
186             }
187             else {
188             if ($lnot) {
189             $match--, last if $argvalue eq $wantvalue;
190             } else {
191             $match++, last if $argvalue eq $wantvalue;
192             }
193             }
194             }
195             }
196             }
197             elsif ( substr($wanted,0,1) eq '!') {
198             my $realwanted = substr($wanted, 1);
199             if (exists $args->{ $realwanted }) {
200             return 0;
201             } else {
202             $match++;
203             }
204             } elsif (exists $args->{ $wanted }) {
205             $match++;
206             } else {
207             ## skip
208             }
209             }
210             return 1 if $match == $count;
211             return 0;
212             }
213              
214             sub _get_entry_points {
215             my $self = shift;
216             return $self->entry_points();
217             }
218              
219             sub entry_points {
220             my $self = shift;
221             my $class = ref($self);
222             {
223             no strict;
224             return $ {$class . '::epoints'};
225             }
226             }
227              
228             sub config {
229             my $self = shift;
230             my $conf = shift;
231             if (defined( $conf )) {
232             $self->{'::appkit'}{config} = $conf;
233             return $self;
234             } else {
235             return $self->{'::appkit'}->{config};
236             }
237             }
238              
239             1;
240              
241             =head1 NAME
242              
243             OpenFrame::AppKit::App - The OpenFrame AppKit application class
244              
245             =head1 SYNOPSIS
246              
247             package MyApplication;
248              
249             use strict;
250              
251             use OpenFrame::AppKit::App;
252             use base qw ( OpenFrame::AppKit::App );
253              
254             =head1 DESCRIPTION
255              
256             The C class is designed to be inherited from.
257             It provides all the basic functionality of a pipeline segment, as well
258             as basic functionality that applications will need to start running.
259              
260             To create an application, all you need to do to get started is
261             subclass OpenFrame::AppKit::App.
262              
263             package MyApplication;
264              
265             use strict;
266              
267             use OpenFrame::AppKit::App;
268             use base qw ( OpenFrame::AppKit::App );
269              
270             In your server code you can now instantiate your application:
271              
272             my $app = MyApplication->new();
273              
274             However, applications require a little more information to act in the
275             manner we have come to expect. Applications in common web
276             applications act when a url is requested that they listen to. Your
277             new application is capable of that, but you need to tell it which URIs
278             to match against. You do this by using the C method of that
279             OpenFrame::AppKit::App helpfully provides. If for instance you wanted
280             your application to execute whenever you went to '/myapp.html' URL
281             then simply use the URI method to specify a regular expression to
282             match:
283              
284             $app->uri( qr!/myapp\.html! );
285              
286             OpenFrame::AppKit::App uses the concept of namespaces to keep your
287             application's data seperate from other application's data in the
288             global session. You can specify the namespace of your application by
289             using the C method, that once again,
290             OpenFrame::AppKit::App provides:
291              
292             $app->namespace( 'myapplication' );
293              
294             As you have probably noticed, the work needed to set up your
295             applications initialization is performed through method calls to your
296             application. All methods that have been demonstrated here are capable
297             of being chained:
298              
299             my $app = MyApplication->new()
300             ->uri( qr!/myapp\.html! )
301             ->namespace( 'myapplication' );
302              
303             All this is very useful, but so far the application still does nothing
304             at all. This will change. C applications act
305             by default as state machines. These states are specified by
306             parameters sent to the OpenFrame server. In the case of an HTTP GET
307             message you can see them on the end of a URL:
308              
309             http://some.server.com/test.cgi?name=value
310              
311             In this case there is one parameter, C and one value C.
312             The application's state machine looks at the parameters, your
313             application acts on values. To set up your state machine you create a
314             method in your application called C. This method
315             should return a hash of arrays. In the hash, the keys represent
316             methods in your module, and then elements in the array represent
317             parameters that have to exist in order for your application to be run:
318              
319             sub entry_points {
320             return {
321             form_filled => [ 'name', 'age' ]
322             };
323             }
324              
325             Each of the keys in your hash is an entry point, and needs a
326             subroutine in your module to perform the work.
327              
328             sub form_filled {
329              
330             }
331              
332             In the case that you want a method to be called even if there are no
333             parameters matched, OpenFrame automatically calls a method called
334             C for you.
335              
336             Whenever C calls an entry point in your application
337             it calls it with two parameters. The first of the two parameters is
338             the Application object itself. The second is the Pipeline store (but
339             I'll talk about that in a little while, its not important right now).
340             A method that your application will use nearly every time it is in an
341             entry point is the C method. It returns the
342             C object that you can use to find out the exact
343             uri that has been called as well as the paramaters and values supplied
344             to it.
345              
346             sub form_filled {
347             my $self = shift;
348             my $store = shift;
349              
350             my $request = $self->request();
351             my $uri = $request->uri();
352             my $args = $request->arguments();
353             }
354              
355             For more information about the request object and what it does, you
356             can see the C documentation. For now we'll talk
357             only about the $args variable, which is a hash reference. Lets assume
358             that your application is up and running, and receiving requests. If
359             you were to receive a request that was represented in URI form as:
360              
361             http://some.server.com/myapp.html?name=Bob&age=34
362              
363             Then you could expect to find that your $args hash would look like:
364              
365             $args = {
366             name => 'Bob',
367             age => 34
368             }
369              
370             my $name = $args->{name};
371              
372             The $name variable would hold the value C.
373              
374             When you write an application any data that you want to provide to the
375             template writer (which may be yourself) should be placed inside the
376             $self object. $self is a hash, and provided you don't use the
377             C<::appkit> key you can place whatever you'd like in there.
378              
379             =head1 AUTHOR
380              
381             James A. Duncan
382              
383             =head1 COPYRIGHT
384              
385             Copyright 2002 Fotango Ltd.
386              
387             This code is released under the same terms as perl itself.
388              
389             http://opensource.fotango.com/
390              
391             =cut