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!
December 12th, 2010 at 1:37 pm
lvalue accessors are not a good idea, for the reason Yuval gives in his review of the module you linked to.
December 12th, 2010 at 4:20 pm
Cool, so M2 is still coming along?
December 12th, 2010 at 4:58 pm
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.December 12th, 2010 at 5:02 pm
Robert: Yes, have finally found time in the last few months. You can follow progress at https://github.com/jonswar/perl-mason
December 12th, 2010 at 9:49 pm
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?
December 13th, 2010 at 7:53 am
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.
December 15th, 2010 at 6:52 am
If components are classes, will they still auto-update when the component source file is modified (like in Mason 1)?
December 15th, 2010 at 4:15 pm
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.
December 16th, 2010 at 4:51 pm
I just wanted to say thanks for finding the time for Mason 2! I like the perl6 syntax too!
February 28th, 2012 at 5:25 am
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?
March 4th, 2012 at 6:35 pm
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.