The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Leyland::Manual::Views - Using Leyland view classes

RESOURCES AND REPRESENTATIONS

RESTful applications are all about resources and representations. Resources are what your application provides to the users. Representations are the way/format in which these resources are provided to the users. If your application is a blogging platform, then (some of) your resources are blog posts/articles. These articles can be represented in different ways, mostly HTML when the clients are web browsers, but also RSS/Atom when your clients are feed readers and maybe even JSON if your platform has some cool AJAX admin interface to it.

DATA SERIALIZATION

Before we approach the subject of views, you should know about Leyland's serialization features. Serialization is the process of taking a data structure (a Perl data structure in this case) and converting it into a textual format such as XML or JSON. The reverse process is called deserialization.

Leyland takes care of both processes by itself. If you have resources for which you wish to generate JSON/XML representations, all you need to do is define the routes that return these representations as returning those formats (with returns 'applications/json' for example, see "ROUTES" in Leyland::Manual::Controllers for more information), and just return the data structure as-is from the route. If the client accepts JSON/XML, Leyland will recognize it and automatically serialize the data structure to the proper format and return that to the client.

When a client sends your application JSON/XML data, Leyland can (and will, unless you explicitly tell it not to) deserialize the data into a Perl data structure, which will be available for you via $c->data.

If you need finer control over JSON/XML representations, you can still use views, which are explained in the following section.

VIEWS

Views, or templates if you will, are used to build representations. With Leyland, you mostly only need to concern yourself with HTML representations, though you can create views for XML or JSON or pretty much anything else if you so wish.

So a view is, basically, a "blank" representation of a certain resource family or type, minus the actual content. When sending a specific resource to a client, say a blog article, the content of the article is rendered into the view/template, and the output is returned to the client.

Views in a Leyland application reside in the views/ directory, located in the top level of the application (unless configured otherwise in the setup() method - see "CONFIGURING LEYLAND APPLICATIONS" in Leyland::Manual::Applications for more information). In this directory, you might have files like article.html, comment.html, etc.

The code that actually performs the rendering of a representation is the template engine, such as Tenjin, Template::Toolkit, HTML::Mason, etc. Your view files/templates will be written in the languages of these templates, so you should familiarize yourself with the template engine of your choice (which is a lie, since Leyland currently only supports Tenjin, so you should familiarize yourself with Tenjin; this will change in the future, however).

If you're experienced with views and templates, you'd know that different resources are almost always rendered inside the same wrapping layout view. Layout views are stored in the views/layouts/ subdirectory. By default, when rendering HTML templates, the main.html layout will be used.

Let's look at a simple example:

        # views/index.html
        <h1>Hello World</h1>

        # views/layouts/main.html
        <html>
                <head><title>Hi there</title></head>
                <body>[== $_content =]</body>
        </html>

When a controller renders index.html for output, index.html's content will replace [== $_content =] inside layouts/main.html (this is Tenjin specific, other template engines will be different), and the final output will be:

        <html>
                <head><title>Hi there</title></head>
                <body><h1>Hello World</h1></body>
        </html>

RENDERING VIEWS

Rendering views with Leyland is easy. If a certain route only returns HTML, you can call the template() method at the end of your route:

        get '^/$' {
                $c->template('index.html');
        }

template() takes the name of a template to render, performs the rendering and returns the resulting HTML. since the call to template() above is the last thing performed in the route, its output will be returned from the route. Of course, most templates won't be static like the above example, but dynamic like the following example:

        <h1>Hello [== $name =]</h1>

In order to render this template, you need to provide the $name variable. There are two ways to pass variables to templates: direct passing, and stash passing. The stash is a hash-ref that resides in the context object. You can add items to it whenever you wish (before rendering):

        $c->stash->{name} = "Michael";
        $c->template('index.html');

This is very similar to Catalyst. However, Leyland also gives you the ability to pass variables like in Dancer, directly in the call to the template() method, like so:

        $c->template('index.html', { name => "Michael" });

You can also combine the two:

        $c->stash->{email} = 'michael at gmail dot com';
        $c->template('index.html', { name => 'Michael' });

Both $email and $name will be available inside index.html. If, however, you pass the same variable twice via both methods, the direct method takes precedence:

        $c->stash->{name} = 'Michael';
        $c->template('index.html', { name => 'Johnny' });

In this case, $name will be "Johnny" in index.html.

THE LAYOUT TEMPLATE

As written before, by default, Leyland renders views into the main.html layout template. If you don't want Leyland to do that, pass 0 (zero) as the third argument to the template() method:

        $c->template('index.html', { name => 'Michael' }, 0);

If you're not passing any variables and still need to pass the 0 flag, do this:

        $c->template('index.html', {}, 0);

If you do want to render into a layout template, but not into the default layout template, there are several ways to do that which are very specific to Tenjin. I will only detail one of them (which I intend to make consistent with other template engines when they become available in Leyland): just pass the name of the layout template as the third argument to the template() method (where we passed 0 just now):

        $c->template('index.html', {}, 'layouts/my_layout.html');

MULTIFORMAT ROUTES

What should you do when a route of yours returns both HTML and other formats like JSON? You can't just use the template() method, as this will cause the clients to receive HTML even when they expect JSON. You can go the if route and check what the client wants like this:

        if ($c->want eq 'text/html') {
                return $c->template('index.html', { article => $article });
        } elsif ($c->want eq 'application/json') {
                return $article; # will be automatically serialized
        }

But Leyland also gives you another option which is easier: you can return an array reference with the following structure:

        return [ \%mimes, \%context, $use_layout ];

\%mimes will be a hash-ref with mime types as keys, and the names of templates for every mime type as values. \%context holds the variables to pass to the templates (or the data structure to serialize). $use_layout is optional and is exactly the same as the third argument to $c->template().

For example:

        return [{ 'text/html' => 'index.html' }, $article, 'layouts/my_layout.html'];

When the client wants HTML, Leyland will render the index.html template, with the $article hash-ref as the template's context, and into the layouts/my_layout.html layout template. If the client wants JSON or XML, Leyland will automatically serialize $article to that format.

If, for some reason, you want to render a template of another format, like JSON perhaps, add an application/json option to the \%mimes hash-ref.

OBJECTS AND NON-JSON/XML FORMATS

When your routes return Perl data structures, they are expected to be hash or array references and should not contain blessed values (i.e. objects), though JSON representations will support objects, assuming they are of classes that provide a TO_JSON() method.

If a route returns a format that Leyland doesn't natively support, like YAML for example, then it is your responsibility to serialize to it, as Leyland will not do it for you. Please do so, as Leyland will stubbornly try to encode it to JSON otherwise.

WHAT'S NEXT?

Read Leyland::Manual::Models now to learn how to use and create model classes or return to the table of contents.

AUTHOR

Ido Perlmuter, <ido at ido50.net>

BUGS

Please report any bugs or feature requests to bug-Leyland at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Leyland. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

        perldoc Leyland::Manual::Views

You can also look for information at:

LICENSE AND COPYRIGHT

Copyright 2010-2014 Ido Perlmuter.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.