Catalyst-View-Template-Pure
view release on metacpan or search on metacpan
lib/Catalyst/View/Template/Pure.pm view on Meta::CPAN
use strict;
use warnings;
package Catalyst::View::Template::Pure;
use Scalar::Util qw/blessed refaddr weaken/;
use Catalyst::Utils;
use HTTP::Status ();
use File::Spec;
use Mojo::DOM58;
use Template::Pure::ParseUtils;
use Template::Pure::DataContext;
use base 'Catalyst::View';
our $VERSION = '0.017';
sub COMPONENT {
my ($class, $app, $args) = @_;
$args = $class->merge_config_hashes($class->config, $args);
$args = $class->modify_init_args($app, $args) if $class->can('modify_init_args');
$class->inject_http_status_helpers($args);
$class->load_auto_template($app, $args);
$class->find_fields;
return bless $args, $class;
}
my @fields;
sub find_fields {
my $class = shift;
for ($class->meta->get_all_attributes) {
next unless $_->has_init_arg;
push @fields, $_->init_arg;
}
}
sub load_auto_template {
my ($class, $app, $args) = @_;
my @parts = split("::", $class);
my $filename = lc(pop @parts);
if(delete $args->{auto_template_src}) {
my $file = $app->path_to('lib', @parts, $filename.'.html');
my $contents = $file->slurp;
my $dom = Mojo::DOM58->new($contents);
if(my $node = $dom->at('pure-component')) {
if(my $script_node = $node->at('script')) {
$class->config(script => "$script_node");
$script_node->remove('script');
}
if(my $style_node = $node->at('style')) {
$class->config(style => "$style_node");
$style_node->remove('style');
}
$contents = $node->content;
}
$class->config(template => $contents);
}
if(delete $args->{auto_script_src}) {
my $file = $app->path_to('lib', @parts, $filename.'.js');
$class->config(script => $file->slurp);
}
if(delete $args->{auto_style_src}) {
my $file = $app->path_to('lib', @parts, $filename.'.css');
$class->config(style => $file->slurp);
}
}
sub inject_http_status_helpers {
my ($class, $args) = @_;
return unless $args->{returns_status};
foreach my $helper( grep { $_=~/^http/i} @HTTP::Status::EXPORT_OK) {
my $subname = lc $helper;
my $code = HTTP::Status->$helper;
my $codename = "http_".$code;
if(grep { $code == $_ } @{ $args->{returns_status}||[]}) {
eval "sub ${\$class}::${\$subname} { return shift->response(HTTP::Status::$helper,\@_) }";
eval "sub ${\$class}::${\$codename} { return shift->response(HTTP::Status::$helper,\@_) }";
}
}
}
sub ACCEPT_CONTEXT {
my ($self, $c, @args) = @_;
die "Can't call in Application context" unless blessed $c;
my $proto = (scalar(@args) % 2) ? shift(@args) : undef;
my %args = @args;
my $key = blessed($self) ? refaddr($self) : $self;
my $stash_key = "__Pure_${key}";
delete $c->stash->{$stash_key} if delete($args{clear_stash});
weaken $c;
$c->stash->{$stash_key} ||= do {
if($proto) {
foreach my $field (@fields) {
if(ref $proto eq 'HASH') {
$args{$field} = $proto->{$field} if exists $proto->{$field};
} else {
if(my $cb = $proto->can($field)) {
$args{$field} = $proto->$field;
}
}
}
}
my $args = $self->merge_config_hashes($self->config, \%args);
$args = $self->modify_context_args($c, $args) if $self->can('modify_context_args');
$self->handle_request($c, %$args) if $self->can('handle_request');
my $template;
if(exists($args->{template})) {
$template = delete ($args->{template});
} elsif(exists($args->{template_src})) {
$template = (delete $args->{template_src})->slurp;
}
my $directives = delete $args->{directives};
my $filters = delete $args->{filters};
my $pure_class = exists($args->{pure_class}) ?
delete($args->{pure_class}) :
'Template::Pure';
Catalyst::Utils::ensure_class_loaded($pure_class);
my $view = ref($self)->new(
%{$args},
%{$c->stash},
ctx => $c,
);
weaken(my $weak_view = $view);
my $pure = $pure_class->new(
template => $template,
directives => $directives,
filters => $filters,
components => $self->build_comp_hash($c, $view),
view => $weak_view,
%$args,
);
$view->{pure} = $pure;
$view;
};
return $c->stash->{$stash_key};
}
sub build_comp_hash {
my ($self, $c, $view) = @_;
return $self->{__components} if $self->{__components};
my %components = (
map {
my $v = $_;
my $key = lc($v);
$key =~s/::/-/g;
$key => sub {
my ($pure, %params) = @_;
my $data = Template::Pure::DataContext->new($view);
foreach $key (%{$params{node}->attr ||+{}}) {
next unless $key && $params{$key};
next unless my $proto = ($params{$key} =~m/^\$(.+)$/)[0];
my %spec = Template::Pure::ParseUtils::parse_data_spec($proto);
$params{$key} = $data->at(%spec)->value;
}
return $c->view($v, %params, clear_stash=>1);
}
} ($c->views),
);
$self->{__components} = \%components;
return \%components;
}
sub apply {
my $self = shift;
my @args = (@_,
template => $self->render,
%{$self->{ctx}->stash});
return $self->{ctx}->view(@args);
}
sub wrap {
my $self = shift;
my @args = (@_,
content => $self->render,
%{$self->{ctx}->stash});
return $self->{ctx}->view(@args);
}
sub response {
my ($self, $status, @proto) = @_;
die "You need a context to build a response" unless $self->{ctx};
( run in 1.871 second using v1.01-cache-2.11-cpan-39bf76dae61 )