Catalyst-View-Markdown
view release on metacpan or search on metacpan
lib/Catalyst/View/Markdown.pm view on Meta::CPAN
package Catalyst::View::Markdown;
use strict;
use warnings;
use base qw/Catalyst::View/;
use Text::Markdown;
use File::Find;
use MRO::Compat;
use Scalar::Util qw/blessed weaken/;
our $VERSION = '0.01';
$VERSION = eval $VERSION;
__PACKAGE__->mk_accessors('markdown_filename');
__PACKAGE__->mk_accessors('include_path');
*paths = \&include_path;
=head1 NAME
Catalyst::View::Markdown - Markdown View Class
=head1 SYNOPSIS
# use the helper to create your View
myapp_create.pl view MD Markdown
# add custom configration in View/MD.pm
__PACKAGE__->config(
# any Markdown configuration items go here
FILENAME_EXTENSION => '.md',
empty_element_suffix => '/>',
tab_width => 4,
trust_list_start_value => 1,
);
# add include path configuration in MyApp.pm
__PACKAGE__->config(
'View::MD' => {
INCLUDE_PATH => [
__PACKAGE__->path_to( 'root', 'src' ),
__PACKAGE__->path_to( 'root', 'lib' ),
],
},
);
# render view from lib/MyApp.pm or lib/MyApp::Controller::SomeController.pm
sub message : Global {
my ( $self, $c ) = @_;
$c->stash->{markdown_filename} = 'message';
$c->forward( $c->view('MD') );
}
=cut
sub _coerce_paths {
my ( $paths, $dlim ) = shift;
return () if ( !$paths );
return @{$paths} if ( ref $paths eq 'ARRAY' );
# tweak delim to ignore C:/
unless ( defined $dlim ) {
$dlim = ( $^O eq 'MSWin32' ) ? ':(?!\\/)' : ':';
}
return split( /$dlim/, $paths );
}
sub new {
my ( $class, $c, $arguments ) = @_;
my $config = {
FILENAME_EXTENSION => '',
CLASS => 'Text::Markdown',
%{ $class->config },
%{$arguments},
};
if ( ! (ref $config->{INCLUDE_PATH} eq 'ARRAY') ) {
my $delim = $config->{DELIMITER};
my @include_path
= _coerce_paths( $config->{INCLUDE_PATH}, $delim );
if ( !@include_path ) {
my $root = $c->config->{root};
my $base = Path::Class::dir( $root, 'base' );
@include_path = ( "$root", "$base" );
}
$config->{INCLUDE_PATH} = \@include_path;
}
if ( $c->debug && $config->{DUMP_CONFIG} ) {
$c->log->debug( "Markdown Config: ", CORE::dump($config) );
}
my $self = $class->next::method(
$c, { %$config },
);
# Set base include paths. Local'd in render if needed
$self->include_path($config->{INCLUDE_PATH});
$self->config($config);
# Creation of template outside of call to new so that we can pass [ $self ]
# as INCLUDE_PATH config item, which then gets ->paths() called to get list
# of include paths to search for templates.
# Use a weakend copy of self so we dont have loops preventing GC from working
my $copy = $self;
Scalar::Util::weaken($copy);
$config->{INCLUDE_PATH} = [ sub { $copy->paths } ];
$self->{markdown} =
$config->{CLASS}->new($config) || do {
my $error = $config->{CLASS}->error();
$c->log->error($error);
$c->error($error);
return undef;
};
return $self;
}
sub process {
my ( $self, $c ) = @_;
my $mdfile = $c->stash->{markdown_filename}
|| $c->action . $self->config->{FILENAME_EXTENSION};
unless (defined $mdfile) {
$c->log->debug('No Markdown file specified for rendering') if $c->debug;
return 0;
}
local $@;
my $output = eval { $self->render($c, $mdfile) };
unless ( $c->response->content_type ) {
$c->response->content_type('text/html; charset=utf-8');
}
$c->response->body($output);
return 1;
}
sub render {
my ($self, $c, $mdfile, $args) = @_;
$c->log->debug(qq/Rendering Markdown file "$mdfile"/) if $c && $c->debug;
my $mdtext;
if ( ref $mdfile eq 'SCALAR' ) {
$mdtext = $$mdfile;
}
else {
my $filename;
# Find the first readable file with the right filename under the include paths
find(
sub { $filename = $File::Find::name if !$filename && $_ eq $mdfile && -r $File::Find::name},
@{$self->include_path}
);
# If we found a match...
if ($filename) {
open my $in, '<', $filename;
$mdtext = join '', do { local $/; <$in> }; # slurp!
close $in;
( run in 2.278 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )