package PODServer;
use Squatting;
package PODServer::Controllers;
use File::Basename;
use File::Find;
use Config;
# skip files we've already seen
my %already_seen;
# figure out where all(?) our pod is located
# (loosely based on zsh's _perl_basepods and _perl_modules)
our %perl_basepods = map {
my ($file, $path, $suffix) = fileparse($_, ".pod");
$already_seen{$_} = 1;
($file => $_);
} glob "$Config{installprivlib}/pod/*.pod";
our %perl_modules;
our @perl_modules;
sub scan {
for (@INC) {
next if $_ eq ".";
my $inc = $_;
my $pm_or_pod = sub {
my $m = $File::Find::name;
next if -d $m;
next unless /\.(pm|pod)$/;
next if $already_seen{$m};
$already_seen{$m} = 1;
$m =~ s/$inc//;
$m =~ s/\.\w*$//;
$m =~ s{^/}{};
$perl_modules{$m} = $File::Find::name;
};
find({ wanted => $pm_or_pod, follow_fast => 1 }, $_);
}
my %h = map { $_ => 1 } ( keys %perl_modules, keys %perl_basepods );
@perl_modules = sort keys %h;
}
scan;
%already_seen = ();
# *.pod takes precedence over *.pm
sub pod_for {
for ($_[0]) {
return $_ if /\.pod$/;
my $pod = $_;
$pod =~ s/\.pm$/\.pod/;
if (-e $pod) {
return $pod;
}
return $_;
}
}
our @C = (
C(
Home => [ '/' ],
get => sub {
my ($self) = @_;
$self->v->{title} = 'POD Server';
if ($self->input->{base}) {
$self->v->{base} = 'pod';
}
$self->render('home');
}
),
C(
Frames => [ '/@frames' ],
get => sub {
my ($self) = @_;
$self->v->{title} = 'POD Server';
$self->render('_frames');
}
),
# The job of this controller is to take $module
# and find the file that contains the POD for it.
# Then it asks the view to turn the POD into HTML.
C(
Pod => [ '/(.*)' ],
get => sub {
my ($self, $module) = @_;
my $v = $self->v;
my $pm = $module; $pm =~ s{/}{::}g;
$v->{path} = [ split('/', $module) ];
$v->{module} = $module;
if (exists $perl_modules{$module}) {
$v->{pod_file} = pod_for $perl_modules{$module};
$v->{title} = "POD Server - $pm";
$self->render('pod');
} elsif (exists $perl_basepods{$module}) {
$v->{pod_file} = pod_for $perl_basepods{$module};
$v->{title} = "POD Server - $pm";
$self->render('pod');
} else {
$v->{title} = "POD Server - $v->{module}";
$self->render('pod_not_found');
}
}
)
);
package PODServer::Views;
use Data::Dump 'pp';
use HTML::AsSubs;
use Pod::Simple;
use Pod::Simple::HTML;
$Pod::Simple::HTML::Perldoc_URL_Prefix = '/';
# the ~literal pseudo-element -- don't entity escape this content
sub x {
HTML::Element->new('~literal', text => $_[0])
}
our $JS;
our $HOME;
our @V = (
V(
'html',
layout => sub {
my ($self, $v, @content) = @_;
html(
head(
title($v->{title}),
style(x($self->_css)),
(
$v->{base}
? base({ target => $v->{base} })
: ()
),
),
body(
div({ id => 'menu' },
a({ href => R('Home')}, "Home"), ($self->_breadcrumbs($v))
),
div({ id => 'pod' }, @content),
),
)->as_HTML;
},
_breadcrumbs => sub {
my ($self, $v) = @_;
my @breadcrumb;
my @path;
for (@{$v->{path}}) {
push @path, $_;
push @breadcrumb, a({ href => R('Pod', join('/', @path)) }, " > $_ ");
}
@breadcrumb;
},
_css => sub {
qq|
body {
background: #112;
color: wheat;
font-family: 'Trebuchet MS', sans-serif;
font-size: 10pt;
}
h1, h2, h3, h4 {
margin-left: -1em;
}
pre {
font-size: 9pt;
background: #000;
color: #ccd;
}
code {
font-size: 9pt;
font-weight: bold;
color: #fff;
}
a {
color: #fc4;
text-decoration: none;
}
a:hover {
color: #fe8;
}
div#menu {
position: fixed;
top: 0;
left: 0;
width: 100%;
background: #000;
color: #fff;
opacity: 0.75;
}
ul#list {
margin-left: -6em;
list-style: none;
}
div#pod {
width: 580px;
margin: 2em 4em 2em 4em;
}
div#pod pre {
padding: 0.5em;
border: 1px solid #444;
-moz-border-radius-bottomleft: 7px;
-moz-border-radius-bottomright: 7px;
-moz-border-radius-topleft: 7px;
-moz-border-radius-topright: 7px;
}
div#pod h1 {
font-size: 24pt;
border-bottom: 2px solid #fe2;
}
div#pod p {
line-height: 1.4em;
}
|;
},
home => sub {
$HOME ||= div(
a({ href => R(Home), target => '_top' }, "no frames"),
em(" | "),
a({ href => R(Frames), target => '_top' }, "frames"),
ul({ id => 'list' },
map {
my $pm = $_;
$pm =~ s{/}{::}g;
li(
a({ href => R('Pod', $_) }, $pm )
)
} (sort @perl_modules)
)
);
},
_frames => sub {
my ($self, $v) = @_;
html(
head(
title($v->{title})
),
frameset({ cols => '*,340' },
frame({ name => 'pod', src => R('Pod', 'Squatting') }),
frame({ name => 'list', src => R('Home', { base => 'pod' }) }),
),
)->as_HTML;
},
pod => sub {
my ($self, $v) = @_;
my $out;
my $pod = Pod::Simple::HTML->new;
$pod->index(1);
$pod->output_string($out);
$pod->parse_file($v->{pod_file});
$out =~ s{%3A%3A}{/}g;
$out =~ s/^.*<!-- start doc -->//s;
$out =~ s/<!-- end doc -->.*$//s;
x($out), $self->_possibilities($v);
},
pod_not_found => sub {
my ($self, $v) = @_;
div(
p("POD for $v->{module} not found."),
$self->_possibilities($v)
)
},
_possibilities => sub {
my ($self, $v) = @_;
my @possibilities = grep { /^$v->{module}/ } @perl_modules;
my $colon = sub { my $x = shift; $x =~ s{/}{::}g; $x };
hr,
ul(
map {
li(
a({ href => R('Pod', $_) }, $colon->($_))
)
} @possibilities
);
}
)
);
1;