Docendo discimus

$self->explain

  • Calendar

    November 2017
    M T W T F S S
    « Sep    
     12345
    6789101112
    13141516171819
    20212223242526
    27282930  
  • Archives

  • Recent Posts

  • Bling

Posts Tagged ‘roles’

Generating HTML with Moose, part 2

Posted by brunorc on November 18, 2008

Previous part wasn’t an example of Good Desing (TM). According to my promises, this will be sorted out a little bit.

Instead of using the inheritance in all cases, I’ll switch to Roles. Role is somehow similar to what one can know as interface in Java. In the case of HTML generation it would be nice to use role “Renderizable”:

#!/usr/bin/env perl

use strict;
use warnings;
use feature 'say';

{
    package Renderizable;

    use Moose::Role;

    requires 'get_content';

    sub render {
        my $self = shift;
        return '<div>' . $self->get_content . '</div>';
    }
}

This role puts the requirement for the method get_content – it means every class which wants to use this role must implement (or inherit) this method.

Since there’s no need to use inheritance chain, I put classes on the same level:

{
    package Container::Section;

    use Moose;

    with 'Renderizable';

    has 'content' => (
        is => 'rw',
        isa => 'Str',
        default => 'I love Moose!',
    );

    sub get_content {
        return shift->content;
    }
}

{
    package Container::Page;

    use Moose;

    with 'Renderizable';

    has 'title' => (
        is => 'rw',
        isa => 'Str',
        default => 'Moose rulez!'
    );

    has 'body' => (
        is => 'rw',
        isa => 'ArrayRef[Container::Section]',
        auto_deref => '1'
    );

    sub get_content {
        my $self = shift;
        print join '', map { $_->render . "\n" } $self->body;
    }

    before 'render' => sub {
        my $self = shift;
        say '<html><head><title>' . $self->title
            . '</title></head><body>';
    };

    after 'render' => sub {
        say '</body></html>';
    };
}

Now we can see it in action:

my $block = Container::Section->new(
    { content => 'This is an example of the Moose power!' }
);
my $defblock = Container::Section->new;

my $page = Container::Page->new;
$page->body( [ $block, $defblock ] );

$page->render;

This is nice, but sometimes one may want to put some dynamically generated content. It can be some piece of Perl code (we leave the security apart for a moment…).

{
    package Container::Section::Evaluable;

    use Moose;

    extends 'Container::Section';

    has '+content' => (
        default => 'localtime()'
    );

    sub get_content {
        my $self = shift;

        return eval $self->content;
    }
}

Here the attribute content is specialized (with the ‘+’ sign) to have some default value. Specialization can only restrict things (e.g. one cannot specialize isa => ‘Int’ to ‘Num’).

But let’s say there is some value that doesn’t need to be recalculated on every request. Even more, we are happy if it’s calculated only once. We can benefit from lazy attribute, which in turn depends on default value or builder method.

{
    package Container::Section::Evaluable::Once;

    use Moose;

    extends 'Container::Section::Evaluable';

    has 'lazy_content' => (
        isa => 'Str',
        is => 'rw',
        lazy => 1,
        builder => '_builder',
    );

    sub _builder {
        return eval shift->content;
    }

    sub get_content {
        return shift->lazy_content;
    }
}

The lazy_attribute will be initialized with the first call to get_content. And since it has value, there’s no need to burden the server with heavy calls to localtime.

Now we can check if it works properly…

my $block = Container::Section->new(
    { content => 'This is an example of the Moose power!' }
);
my $defblock = Container::Section->new;
my $eval = Container::Section::Evaluable->new;
my $evalonce = Container::Section::Evaluable::Once->new(
    { content => 'localtime()' }
);

my $page = Container::Page->new;
$page->body( [ $block, $defblock, $eval, $evalonce ] );

$page->render;

say "\n" . ( '-' x 24 );
sleep 5;

$page->render;

I would be glad to read all your comments, rants and corrections!

Advertisements

Posted in Moose | Tagged: , , , , , | Leave a Comment »