File Coverage

blib/lib/Sledge/Engine.pm
Criterion Covered Total %
statement 6 6 100.0
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 8 8 100.0


line stmt bran cond sub pod time code
1             package Sledge::Engine;
2              
3 5     5   130421 use strict;
  5         88  
  5         196  
4 5     5   30 use base qw(Class::Data::Inheritable);
  5         13  
  5         6916  
5             use Scalar::Util qw(blessed);
6             use File::Basename ();
7             use Class::Inspector;
8             use UNIVERSAL::require;
9             use Module::Pluggable::Object;
10             use Carp ();
11             use String::CamelCase qw(camelize);
12             use Sledge::Utils;
13              
14              
15             our $VERSION = '0.04';
16             our $StaticExtension = '.html';
17              
18             sub import {
19             my $pkg = shift;
20              
21             return unless $pkg eq 'Sledge::Engine';
22              
23             my $caller = caller(0);
24             no strict 'refs';
25             my $engine = 'Sledge::Engine::CGI';
26             if ($ENV{MOD_PERL}) {
27             my($software, $version) =
28             $ENV{MOD_PERL} =~ /^(\S+)\/(\d+(?:[\.\_]\d+)+)/;
29             if ($version >= 1.24 && $version < 1.90) {
30             $engine = 'Sledge::Engine::Apache::MP13';
31             *handler = sub ($$) { shift->run(@_); };
32             }
33             else {
34             Carp::croak("Unsupported mod_perl version: $ENV{MOD_PERL}");
35             }
36             }
37             $engine->require;
38             push @{"$caller\::ISA"}, $engine;
39              
40             $caller->mk_classdata('ActionMap' => {});
41             $caller->mk_classdata('ActionMapKeys' => []);
42             $caller->mk_classdata('components' => []);
43              
44             }
45              
46             sub new {
47             my $class = shift;
48             my $self = bless {}, $class;
49             $self;
50             }
51              
52             sub setup {
53             my $pkg = shift;
54              
55             my $pages_class = join '::', $pkg, 'Pages';
56             $pages_class->use or die $@;
57             my $finder = Module::Pluggable::Object->new(
58             search_path => [$pages_class],
59             require => 1,
60             );
61             $pkg->components([$finder->plugins]);
62             for my $subclass(@{$pkg->components}) {
63             my $methods = Class::Inspector->methods($subclass, 'public');
64             for my $method(@{$methods}) {
65             if ($method =~ s/^dispatch_//) {
66             $pkg->register($subclass, $method);
67             }
68             }
69             }
70             $pkg->ActionMapKeys([
71             sort { length($a) <=> length($b) } keys %{$pkg->ActionMap}
72             ]);
73             }
74              
75             sub register {
76             my($pkg, $class, $page) = @_;
77             my $prefix = Sledge::Utils::class2prefix($class);
78             my $path = $prefix eq '/' ? "/$page" : "$prefix/$page";
79             $path =~ s{/index$}{/};
80             $pkg->ActionMap->{$path} = {
81             class => $class,
82             page => $page,
83             };
84             }
85              
86             sub lookup {
87             my($self, $path) = @_;
88             $path ||= '/';
89             $path =~ s{/index$}{/};
90             my $action;
91             if ($action = $self->ActionMap->{$path}) {
92             return $action;
93             }
94             elsif ($action = $self->lookup_static($path)) {
95             return $action;
96             }
97             # XXX handle arguments.
98             # my $match;
99             # for my $key(@{$self->ActionMapKeys}) {
100             # next unless index($path, $key) >= 0;
101             # if ($path =~ m{^$key}) {
102             # $match = $key;
103             # }
104             # }
105             # return unless $match;
106             # my %action = %{$self->ActionMap->{$match}};
107             # if (length($path) > length($match)) {
108             # my $args = $path;
109             # $args =~ s{^$match/?}{};
110             # $action{args} = [split '/', $args];
111             # }
112             # return \%action;
113             }
114              
115             sub lookup_static {
116             my($self, $path) = @_;
117             my($page, $dir, $suf) =
118             File::Basename::fileparse($path, $StaticExtension);
119             return if index($page, '.') >= 0;
120             $page ||= 'index';
121             my $class;
122             if ($dir eq '/') {
123             my $appname = ref $self;
124             for my $subclass(qw(Root Index)) {
125             $class = join '::', $appname, 'Pages', $subclass;
126             last if $class->require;
127             }
128             }
129             else {
130             $dir =~ s{^/}{};
131             $dir =~ s{/$}{};
132             $class = join '::',
133             ref($self), 'Pages', map { camelize($_) } split '/', $dir;
134             }
135             if ((Class::Inspector->loaded($class) || $class->require) &&
136             -e $class->guess_filename($page)) {
137             no strict 'refs';
138             *{"$class\::dispatch_$page"} = sub {}
139             unless $class->can("dispatch_$page");
140             my %action = (class => $class, page => $page);
141             $self->ActionMap->{$path} = \%action;
142             return \%action;
143             }
144             }
145              
146             sub run {
147             my $self = shift;
148             unless (blessed $self) {
149             $self = $self->new;
150             }
151             $self->handle_request(@_);
152             }
153              
154             sub handle_request {
155             die "ABSTRACT METHOD!";
156             }
157              
158             1;
159              
160             __END__