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 ‘OOP’

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 »

Generating HTML with Moose, part 1

Posted by brunorc on November 17, 2008

It will be rather simple story, as my relationship with Moose is still far from familiarity. But wishing to show the already learnt part, I decided to put it on the blog as a short tutorial.

#!/usr/bin/env perl

use strict;
use warnings;
use feature 'say'; # use 5.10
{
    package Container;
    use Moose;
    has 'content' => (
        is => 'rw',
        isa => 'Str',
        default => 'Moose is great'
    );
    sub render {
        my $self = shift;
        say '<div>' . $self->content . '</div>';
    }
}

The class is very straightforward. It has the content attribute and the render method, used to “generate” the HTML-ized content. In case the user didn’t put any content, I provided some default value.

We can say that page is just a big container, but somehow different. It will have a header and a footer, and in lieu of simple text attribute it will rather have a collection of other containers:

{
    package Container::Page;

    use Moose;

    extends 'Container'; # we inherit from Container

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

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

    sub render {
        my $self = shift;
        map { $_->render . "\n" } @{ $self->body };
    }
}

Now I have the stub of the class. Still no header, though. But before I take care about this, I’d like to note the sugar which comes with Moose. As we can see, body is an array reference. Thus, it will be dereferenced a lot. Instead of writing @{ $self->body } every now and then, I can ask Moose to dereference it for me:

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

    sub render {
        my $self = shift;
        map { $_->render . "\n" } $self->body;
    }

Now to the header. Of course I can put the additional HTML chunks in the render method, but I can also use method modifiers. Let’s see:

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

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

Note that I inherit the content attribute from Container, but later I don’t use it. It’s bad, because someone may want to place the static text in this attribute, thus being rather disappointed when it doesn’t appear. I promise to be neater in the second part. But before this we can see the result of our work:

my $block = Container->new(
    { content => "This is an example of the Moose power!" }
);
my $defblock = Container->new;
my $page = Container::Page->new(
    { body => [ $block, $defblock ] }
);
$page->render;

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