basic page rendering
Matt S Trout [Thu, 3 Feb 2011 23:16:16 +0000 (23:16 +0000)]
lib/SCSite.pm [new file with mode: 0644]
lib/SCSite/PageSet.pm
share/skin/layout.html [new file with mode: 0644]

diff --git a/lib/SCSite.pm b/lib/SCSite.pm
new file mode 100644 (file)
index 0000000..d0a9627
--- /dev/null
@@ -0,0 +1,118 @@
+package SCSite;
+
+use IO::All;
+use SCSite::PageSet;
+use Web::Simple;
+
+{
+  package SCSite::Link; use Moo; has $_ => (is => 'ro') for qw(title to);
+}
+
+has _topbar => (is => 'ro', default => sub { [
+  map SCSite::Link->new(title => $_->[0], to => $_->[1] ),
+    map [ /^(.*?)\s+(\S+)$/ ],
+      split("\n", <<'ENDBAR')
+Home /
+About /about
+News /news
+Catalyst /catalyst
+Servers /servers
+Professional Services /services
+Blogs /blog
+ENDBAR
+  ] }
+);
+
+has pages => (is => 'lazy');
+
+has _layout_zoom => (is => 'lazy');
+
+sub default_config {
+  (
+    pages_dir => 'share/content',
+    template_dir => 'share/skin',
+  )
+}
+
+sub _build_pages {
+  my ($self) = @_;
+  SCSite::PageSet->new(base_dir => $self->config->{pages_dir})
+}
+
+sub dispatch_request {
+  my $self = shift;
+  sub (/feed/**/) {
+    $self->_http_response(500 => 'text/plain' => 'Not implemented');
+  },
+  sub (/) {
+    $self->_page_http_response(200 => $self->_find_page('index'));
+  },
+  sub (/**) {
+    [ 302, [ 'Location' => "/$_[1]/" ], [] ]
+  },
+  sub (/**/) {
+    my ($code, $page) = map {
+      $_ ? (200, $_) : (404, $self->_error_page(404))
+    } $self->_find_page($_[1]);
+    $self->_page_http_response($code => $page);
+  }
+}
+
+sub _error_page {
+  my ($self, $code) = @_;
+  $self->_find_page("error_${code}");
+}
+
+sub _find_page {
+  my ($self, $path) = @_;
+  $self->pages->get({ path => $path });
+}
+
+sub _http_response {
+  my ($self, $code, $type, $content) = @_;
+  [ $code, [ 'Content-type' => $type ], [ $content ] ];
+}
+
+sub _page_http_response {
+  my ($self, $code, $page) = @_;
+  [ $code, [ 'Content-type' => 'text/html' ], $self->_render_page($page) ];
+}
+
+sub _render_page {
+  my ($self, $page) = @_;
+  my $zoom = $self->_layout_zoom;
+  $zoom->select('.title')->replace_content($page->title)
+       ->select('meta[name=description]')->replace_content($page->description)
+       ->select('meta[name=keywords]')->replace_content($page->keywords)
+       ->select('.topbar_entries')->repeat_content([
+         map { my $e = $_; sub {
+           $_->select('.entry')->replace_content($e->title)
+             ->then->set_attribute(href => $e->to)
+         } } @{$self->_topbar}
+       ])
+       ->select('.main')->replace_content(\$page->body)
+       ->to_fh
+}
+
+sub _build__layout_zoom {
+  my ($self) = @_;
+  HTML::Zoom->from_file(
+    io->dir($self->config->{template_dir})->catfile('layout.html')
+  )->memoize;
+}
+
+sub run_if_script {
+  return $_[0]->to_psgi_app if caller(1);
+  my $class = shift;
+  my @config_keys = keys %{{$class->default_config}};
+  require Getopt::Long;
+  my %config;
+  Getopt::Long::GetOptions(
+    map +("$_=s" => \$config{$_}), @config_keys
+  );
+  delete $config{$_} for grep !defined($config{$_}), keys %config;
+  my $new = $class->new(config => \%config);
+  $new->run(@_)
+}
+
+__PACKAGE__->run_if_script;
index d2d440a..559038b 100644 (file)
@@ -16,7 +16,7 @@ sub get {
   $spec->{path} or die "path is required to get";
   my ($dir, $file) = $spec->{path} =~ m{^(?:(.*)/)?([^/]+)$};
   my $type;
-  my @poss = io($self->base_dir)->${\sub {
+  my @poss = io->dir($self->base_dir)->${\sub {
     my $io = shift;
     defined($dir) ? $io->catdir($dir) : $io
   }}->filter(sub { $_->filename =~ /\Q${file}\E\.(html|md)/ and $type = $1 })
diff --git a/share/skin/layout.html b/share/skin/layout.html
new file mode 100644 (file)
index 0000000..e85c0de
--- /dev/null
@@ -0,0 +1,24 @@
+<html>
+  <head>
+    <title class="title">page title</title>
+    <meta name="description" />
+    <meta name="keywords" />
+  </head>
+  <body>
+    <div class="title">page title</div>
+    <div>
+      <ul class="topbar_entries">
+        <li><a class="entry"></a></li>
+      </ul>
+    <div>
+    <div>
+      <div class="sidebar_segment">
+        <h4 class="sidebar_title"></h4>
+        <ul class="sidebar_entries">
+          <li><a class="entry"></a></li>
+        </ul>
+      </div>
+    </div>
+    <div class="main" />
+  </body>
+</html>