Dec 12

In Mason 1 you can declare component arguments and then refer to them as normal lexically scoped variables:

<%args>
$address
$city
$name
</%args>

Dear <% $name %>: We will come to your house at <% $address %> in the
fair city of <% $city> to deliver your prize!

In Mason 2, components are classes rather than subroutines, so we have attributes rather than arguments. When you call a component, you create an instance of the component class and pass attributes to the constructor.

Attributes have several advantages over arguments: you can refer to them in any of the component’s methods (unlike in Mason 1, where arguments have to be awkwardly passed to each method) and you can take advantage of Moose’s powerful attribute declarations.

Here’s how this looks without any special syntax:

<%class>
has 'address' => (is => 'ro');
has 'city'    => (is => 'ro');
has 'name'    => (is => 'ro');
</%class>

Dear <% $self->name %>: We will come to your house at <% $self->address %> in the fair city of <% $self->city> to deliver your prize!

Clearly, we’ll need to pour some sugar on this.

Declaring attributes

Mason 2 will provide an <%attr> section, analagous to Mason 1′s <%args>:

<%attr>
a
b => 'foo'
c => (isa => 'Str', default => 'something')
</%attr>

This illustrates the three kinds of <%attr> declarations:

  • A name by itself: required attribute.
  • A name followed by => and a non-paren: optional attribute with a default.
  • A name followed by => a paren: specifies Moose attribute options.

You can mix and match these attributes with standard has declarations. The above is equivalent to:

<%class>
has 'a' => (required => 1);
has 'b' => (default => 'foo');
has 'c' => (isa => 'Str', default => 'something');
</%class>

All attributes will be read-write by default, via MooseX::HasDefaults::RW. Although read-write attributes are sometimes discouraged, I believe they’re appropriate and convenient for these short-lived component objects that are not generally expected to be accessed outside of their class.

Accessing attributes

To make the frequent access of attributes more convenient, we resort to some limited source filtering evil and implement Perl 6ish $. notation. Specifically, all occurrances of

$.foo

within Perl sections will be replaced with

$self->foo

where foo is any valid attribute name ([A-Za-z_]w*).

Note that with read-write lvalue attributes, this could even be used for assignment:

$.foo = 5;

Very convenient for attributes which exist chiefly for sharing data between methods.

The $. substitution feature will be made easy to disable, for those who are against it.

Putting it all together

The first example above with attribute declaration and access sugar:

<%attr>
address
city
name
</%attr>

Dear <% $.name %>: We will come to your house at <% $.address %> in the
fair city of <% $.city> to deliver your prize!

11 Responses to “Mason 2: Attributes”

  1. hdp Says:

    lvalue accessors are not a good idea, for the reason Yuval gives in his review of the module you linked to.

  2. Robert Says:

    Cool, so M2 is still coming along?

  3. Jonathan Swartz Says:

    hdp: Not sure what you’re referring to? Ok – you mean that type constraints cannot be enforced? It’s worth noting, but does not seem like a big deal to me for short-lived attributes used to share data between component methods.

  4. Jonathan Swartz Says:

    Robert: Yes, have finally found time in the last few months. You can follow progress at https://github.com/jonswar/perl-mason

  5. hdp Says:

    I mean that nothing that normally happens in an accessor will happen, except for the most basic thing (storing the value) — no triggers, no type constraints (and thus no coercions), no method modifiers on the accessor, no weak_ref.

    Dismissing these problems because “it’s just for component methods” doesn’t make sense to me. lvalue accessors won’t scale to even moderately complex codebases, and in exchange for this you get very marginally sweeter syntax. And if you don’t care about scaling, why use Moose?

  6. Jonathan Swartz Says:

    hdp – ok, thanks for the explanation, didn’t occur to me that it would be more than just type constraints. In my own Moose classes I usually just update hash elements in the object directly but this will make me think about using setters instead, though I wish there were a way to make a setter private.

    But I don’t buy the “if you don’t take advantage of Moose setters, why use Moose at all”. I’ve never (or hardly) used setter features outside the constructor and still gotten quite a lot of utility from Moose’s other features.

  7. Arun Prasaad Says:

    If components are classes, will they still auto-update when the component source file is modified (like in Mason 1)?

  8. Jonathan Swartz Says:

    Arun: Yes, although this is definitely tricker to pull off with classes than with Mason 1′s hashes of anonymous subroutines. It is working right now except I haven’t figured out a good way to “clean” the class of any methods that no longer exist. Subject for another post.

  9. Ryan Says:

    I just wanted to say thanks for finding the time for Mason 2! I like the perl6 syntax too!

  10. Stephen S Says:

    Hi,

    Apologies for commenting on an old thread, but I was considering upgrading my Catalyst from HTML::Mason to Mason. In which I used to use the attributes section to pass values to the autoloader (so that I could dynamically add to a header that is wrapping the content). Although in this post you mention Mason 2 will provide the section it doesn’t seem to exist i the latest CPAN version. My Mason 1 work around is:

    http://stevesbraindumps.blogspot.com/2010/01/catalyst-mason-passing-variables-in.html

    I was wondering without the section in Mason2 what the best way to proceed would be. Indeed is it sensible to upgrade?

  11. Jonathan Swartz Says:

    Stephen: See http://search.cpan.org/perldoc?Mason::Manual::Components#ATTRIBUTES for details on declaring and accessing attributes. Basically, you just use Moose syntax inside a <%class> section:

    < %class>
    has ‘foo’;
    has ‘bar’ => (required => 1);
    has ‘baz’ => (isa => ‘Int’, default => 17);
    </%class>

    There is no explicit < %attr> block any more because it doesn’t save much over the regular Moose syntax (and with the latter, you get a lot more power/options). But I should make that clearer in the docs.

Leave a Reply

preload preload preload