O'Reilly logo

Embedding Perl in HTML with Mason by Ken Williams, Dave Rolsky

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Component Arguments

Most components will expect to receive named arguments, and these can be passed in one of two ways. Components can receive arguments as the result of external requests, such as those via HTTP, or they can receive arguments when they are called from another component. These arguments are available in the called component via several mechanisms. But from a component’s perspective, how it is called is largely irrelevant.

<%args> Block Revisited

Since we are talking about arguments, it is worth revisiting the <%args> block discussed previously. This block is used to declare the arguments that a component expects. In addition, it can also be used to specify a default value if none is given when the component is called.

The block we used earlier was:

<%args>
 $color
 $size => 20
 @items => ( 1, 2, 'something else' )
 %pairs => ( key1 => 1, key2 => 'value' )
</%args>

This says, in English, that this component expects two scalars, one named color, which is mandatory, and one named size, which is not mandatory and defaults to 20. It also expects an array named items, which defaults to (1, 2, ' something else' ) and a hash named pairs, which defaults to ( key1 => 1, key2 => 'value' ). Neither of these latter two arguments is mandatory.

These arguments are all available in your component as lexically scoped variables. For example, your component will have a lexically scoped $color variable available. You do not need to declare it anywhere but in the <%args> block.

If a mandatory argument (one with no default) is not provided in the call to the component, an exception is thrown. If an argument with a default is not given a value, the default is transparently assigned to the variable. Just to be clear, we will explicitly note that undef is a valid value for an argument. It is the absence of an argument that causes the exception.

%ARGS

In addition to any lexically scoped variables created via their declaration in an <%args> block, each component body also has a lexically scoped hash called %ARGS. This hash contains all of the arguments with which the component was called.

One point of confusion for those new to Mason is the difference between %ARGS and the <%args> block. The %ARGS hash contains the arguments exactly as they were passed to a component, whether or not they are declared in the <%args> block. The keys of the %ARGS hash do not contain the Perl sigils ($, @, or %). An argument declared as $color in the <%args> block would therefore be available via $ARGS{color}. Any assignment of defaults by the <%args> block is not visible in %ARGS; the values are given exactly as they were passed.

In addition, the %ARGS hash is always present,[9] but the <%args> block is optional.

If you are expecting input with a large number of similarly named items, such as input1, input2, and so on through input20, declaring all of them in an <%args> block may be a bit unwieldy. In this case, the %ARGS hash can be quite handy.

%ARGS is also useful if you expect arguments with names that cannot be used for Perl variables. For example, when submitting a web form by clicking on an image named submit, the browser will generate two additional form values, called submit.x and submit.y . You cannot have a Perl variable named $submit.x, so the only way to get at this argument is to check $ARGS{'submit.x'}.

There are other ways to retrieve the arguments passed to a component, which are discussed in Chapter 4.

%ARGS Versus @_

The Mason tradition has always been to use named arguments. However, for simple components, you may prefer to use @_ to access the arguments, just as in Perl subroutines. There are several caveats here. If your component contains an <%args> section, Mason expects it to receive an even number of arguments in @_ so that it can assign @_ to %ARGS. If it receives an odd number of arguments, a fatal error will occur. But regardless of how arguments are passed, @_ is always available in components.

So the following pieces of code are near-identical when a component receives an even number of arguments:

% foreach (sort %ARGS) {
  <% $_ %>
% }

% foreach (sort @_) {
  <% $_ %>
% }

Argument Examples

Let’s take a look at a number of scenarios involving argument passing, first via an HTTP URL query string and then via an internal component call. Then we will see how this interacts with the component’s <%args> block and the %ARGS hash.

Arguments submitted via POST and GET requests are treated in exactly the same way, and if both are present they are merged together before the component is called.

Let’s assume that the component being called contains this <%args> block:

<%args>
 $colors
 @colors
 %colors
</%args>

For each example, we show you two ways to call that component. The first is via an HTTP query string, which is how a component is called to generate a web page. The second is via a component call tag, as a component would be called from another Mason component.

/some/component?colors=blue<& /some/component, colors => 'blue' &>

In both cases, $colors is the string “blue” and @colors is a single-element array containing ('blue'). In addition, $ARGS{colors} would be the string “blue” as well.

This component will die when it is called, however, because Mason does not allow you to assign an odd number of elements to a hash, so the assignment to %colors is fatal.

/some/component?colors=blue&colors=red&colors=green<& /some/component, colors => [ 'blue', 'red', 'green' ] &>

Again the URL and internal example give the same result. The $colors variables contains a reference to a three-element array, ['blue', 'red', 'green']. This time, $ARGS{colors} contains the same three-element array reference as $colors and the @colors array contains a three-element array with those same elements.

Again, assigning an odd number of elements to the %colors hash causes a fatal error.

/some/component?colors=blue&colors=cyan&colors=green&colors=mint <& /some/component, colors => [ 'blue', 'cyan', 'green', 'mint' ] &>

Now, $colors contains a reference to a four-element array, and the @colors array has four elements as well. Finally, the assignment to %colors works without an error and will result in a hash containing ( 'blue'=>'cyan','green'=>'mint' ). $ARGS{colors} contains the same array reference as $colors.

<& /some/component, colors => { blue => 'cyan', green => 'mint' } &>

This set of arguments isn’t representable with a query string, because there’s no way to indicate that the arguments are structured in a hash via a web request.

In this call, $colors contains a reference to a hash, not an array, though the @colors array contains four elements, just as in the previous example. The %colors hash is likewise the same as the previous example. Now, the $ARGS{colors} hash entry contains a hash reference.

This discrepancy in how hash assignments are treated, depending on the way a call is made, is probably not too important because Mason simply does the right thing based on the contents of %args. You declare %colors as an argument, and as long as an even number of colors elements are passed in, you get a hash.

Arguments via Component Calls

When calling another component that expects named arguments, it is important to remember that arrays and hashes need to be passed as references. For example, a component named /display with an <%args> block like this:

<%args>
 @elements
 %labels
</%args>

Should be called like this:

<& /display, elements => \@some_data, labels => \%data_labels &>

Mason will do the right thing and translate the references back into an array and a hash in the /display component.

Arguments via HTTP Requests

When using Mason to make a web application, you must understand the details of how external HTTP requests are converted into component calls. Specifically, we are interested in how query string and POST parameters are converted into arguments.

These requests are expected to be in the standard name/value pair scheme used by most web interfaces. If a parameter is given only once (i.e., component?foo=1&bar=2), it will be present in the %ARGS hash as a simple scalar, regardless of how it is declared in the <%args> section.

If a parameter is declared as a scalar ($foo) but given multiple values (i.e., component?foo=1&foo=2), the $foo parameter will end up containing a reference to an array, as will $ARGS{foo}. Future versions of Mason may provide the ability to coerce these arguments into specific data structures.

If a parameter is declared as an array (@foo), it will contain zero or more values depending on what is in the query string and/or POST data. A hash is treated more or less like an array, except that giving a parameter declared as a hash an odd number of values will cause a fatal error.

One caution: the key/value associations in a declared hash are determined by the order of the input. Let’s assume we have a component with this <%args> block:

<%args>
 %foo
</%args>

A request for component?foo=1&foo=2 will result in a different hash from component?foo=2&foo=1. This isn’t generally a problem because you can usually control the order of the arguments by their position in an HTML form. However, neither the HTTP or HTML specifications specify that a client needs to respect this ordering when submitting the form, and, even if it were, some browsers would probably screw it up eventually.[10] It’s not a great idea, therefore, to use hashes as arguments in a top-level component that may be called via an HTTP request generated by a form. When you can control the query string yourself, this is not a problem.



[9] Unless the component is called with an odd number of arguments. See the next section for details on this exception.

[10] We know of no browsers that actually screw it up, but surely there must be some out there. Browsers have a history of simply making up their own unique behaviors, even when there is a specification.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required