Skip to content

Commit

Permalink
added support for template variants
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Mar 12, 2014
1 parent defc52f commit a385e46
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 21 deletions.
1 change: 1 addition & 0 deletions Changes
@@ -1,5 +1,6 @@

4.89 2014-03-12
- Added support for template variants.
- Fixed bug in Mojo::DOM::HTML where non-self-closing elements were not
handled correctly.
- Fixed bug in Mojo::DOM::HTML where <image> was not treated as an alias for
Expand Down
6 changes: 3 additions & 3 deletions lib/Mojolicious/Controller.pm
Expand Up @@ -21,7 +21,7 @@ has tx => sub { Mojo::Transaction::HTTP->new };
# Reserved stash values
my %RESERVED = map { $_ => 1 } (
qw(action app cb controller data extends format handler json layout),
qw(namespace partial path status template text)
qw(namespace partial path status template text variant)
);

sub AUTOLOAD {
Expand Down Expand Up @@ -884,8 +884,8 @@ wide default values can be set with L<Mojolicious/"defaults">. Some stash
values have a special meaning and are reserved, the full list is currently
C<action>, C<app>, C<cb>, C<controller>, C<data>, C<extends>, C<format>,
C<handler>, C<json>, C<layout>, C<namespace>, C<partial>, C<path>, C<status>,
C<template> and C<text>. Note that all stash values with a C<mojo.*> prefix
are reserved for internal use.
C<template>, C<text> and C<variant>. Note that all stash values with a
C<mojo.*> prefix are reserved for internal use.
# Remove value
my $foo = delete $c->stash->{foo};
Expand Down
10 changes: 10 additions & 0 deletions lib/Mojolicious/Guides/Rendering.pod
Expand Up @@ -198,6 +198,16 @@ alternatives.

$self->render_maybe('localized/baz') or $self->render('foo/bar/baz');

=head2 Template variants

To make your application look great on many different devices you can also
select a C<variant> for your templates, it only applies when a template with
the correct name actually exists and falls back to the generic one otherwise.

# foo.html+phone.ep
# foo.html.ep
$self->render('foo', format => 'html', variant => 'phone', handler => 'ep');

=head2 Rendering inline templates

Some renderers such as C<ep> allow templates to be passed inline.
Expand Down
51 changes: 34 additions & 17 deletions lib/Mojolicious/Renderer.pm
Expand Up @@ -84,7 +84,8 @@ sub render {
my $options = {
encoding => $self->encoding,
handler => $stash->{handler},
template => delete $stash->{template}
template => delete $stash->{template},
variant => $stash->{variant}
};
my $inline = $options->{inline} = delete $stash->{inline};
$options->{handler} //= $self->default_handler if defined $inline;
Expand Down Expand Up @@ -149,30 +150,28 @@ sub template_for {

sub template_handler {
my ($self, $options) = @_;

return undef unless my $file = $self->template_name($options);
unless ($self->{templates}) {

# Templates
s/\.(\w+)$// and $self->{templates}{$_} ||= $1
for map { sort @{Mojo::Home->new($_)->list_files} } @{$self->paths};

# DATA templates
my $loader = Mojo::Loader->new;
s/\.(\w+)$// and $self->{templates}{$_} ||= $1
for map { sort keys %{$loader->data($_)} } @{$self->classes};
}

return $self->{templates}{$file} if exists $self->{templates}{$file};
return $self->default_handler;
return $self->default_handler unless my $handlers = $self->_handlers($file);
return $handlers->[0];
}

sub template_name {
my ($self, $options) = @_;

return undef unless my $template = $options->{template};
return undef unless my $format = $options->{format};
$template .= ".$format";

# Variants
my $handler = $options->{handler};
return defined $handler ? "$template.$format.$handler" : "$template.$format";
if (defined(my $variant = $options->{variant})) {
$variant = "$template+$variant";
my $handlers = $self->_handlers($variant) // [];
$template = $variant
if @$handlers && !defined $handler || grep { $_ eq $handler } @$handlers;
}

return defined $handler ? "$template.$handler" : $template;
}

sub template_path {
Expand Down Expand Up @@ -206,6 +205,24 @@ sub _extends {
return delete $stash->{extends};
}

sub _handlers {
my ($self, $file) = @_;

unless ($self->{templates}) {

# Templates
s/\.(\w+)$// and push @{$self->{templates}{$_}}, $1
for map { sort @{Mojo::Home->new($_)->list_files} } @{$self->paths};

# DATA templates
my $loader = Mojo::Loader->new;
s/\.(\w+)$// and push @{$self->{templates}{$_}}, $1
for map { sort keys %{$loader->data($_)} } @{$self->classes};
}

return $self->{templates}{$file};
}

sub _render_template {
my ($self, $c, $output, $options) = @_;

Expand Down
3 changes: 2 additions & 1 deletion t/mojolicious/dispatch.t
Expand Up @@ -96,7 +96,8 @@ is $c->param(path => 'test')->param('path'), undef, 'value is reserved';
is $c->param(status => 'test')->param('status'), undef, 'value is reserved';
is $c->param(template => 'test')->param('template'), undef,
'value is reserved';
is $c->param(text => 'test')->param('text'), undef, 'value is reserved';
is $c->param(text => 'test')->param('text'), undef, 'value is reserved';
is $c->param(variant => 'test')->param('variant'), undef, 'value is reserved';
is_deeply [$c->param], [], 'values are hidden';

# Controller with application and routes
Expand Down
35 changes: 35 additions & 0 deletions t/mojolicious/layouted_lite_app.t
Expand Up @@ -106,6 +106,12 @@ get '/inline/again' => {inline => 0};

get '/data' => {data => 0};

get '/variants' => {layout => 'variants'} => sub {
my $self = shift;
$self->stash->{variant} = $self->param('device');
$self->render('variants');
};

my $t = Test::Mojo->new;

# "0" content reassignment
Expand Down Expand Up @@ -245,6 +251,23 @@ $t->get_ok('/inline/again')->status_is(200)
$t->get_ok('/data')->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
->content_is(0);

# Variants (desktop)
$t->get_ok('/variants')->status_is(200)
->header_is(Server => 'Mojolicious (Perl)')->content_is('Variant: Desktop!');

# Variants (tablet)
$t->get_ok('/variants?device=tablet')->status_is(200)
->header_is(Server => 'Mojolicious (Perl)')->content_is('Variant: Tablet!');

# Variants (desktop fallback)
$t->get_ok('/variants?device=phone')->status_is(200)
->header_is(Server => 'Mojolicious (Perl)')->content_is('Variant: Desktop!');

# Variants ("0")
$t->get_ok('/variants?device=0')->status_is(200)
->header_is(Server => 'Mojolicious (Perl)')
->content_is('Another variant: Desktop!');

done_testing();

__DATA__
Expand Down Expand Up @@ -375,3 +398,15 @@ seems
to
<%= content_for 'message' %>
work!
@@ layouts/variants.html.ep
Variant: <%= content %>\
@@ layouts/variants.html+0.ep
Another variant: <%= content %>\
@@ variants.html.ep
Desktop!\
@@ variants.html+tablet.epl
Tablet!\

0 comments on commit a385e46

Please sign in to comment.