Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation

Re: Links between Mason components?

by FloydATC (Hermit)
on May 17, 2012 at 20:48 UTC ( #971172=note: print w/ replies, xml ) Need Help??

in reply to Links between Mason components?

Only you know how to link to stuff in your web application, so no size will ever fit all; a generalized solution will probably never meet all requirements.

Here's what I usually do: I write my own modules (classes really) that represent the different entities that the web application deals with, such as users, articles, hosts or whatever. Each of those modules (classes) include a link() method which "knows" what an URL for that particular entity should look like. This method is passed with a hash argument where I pass on all the arguments that the current Mason component was called with. Example:

<TABLE class="items"> % foreach my $item (@items) { <TR><TD><% $item->link( %ARGS ) %></TD></TR> % } </TABLE>
The corresponding module might contain something like this:
sub link { my $self = shift; my %args = @_; $args{'foo'} ||= "default"; $args{'bar'} ||= "values"; return '<A href="/path/to/item.html?id='.$self->id.'&foo='.$args{'fo +o'}.'&bar='.$args{'bar'}.'>'.$self->name.'</A>'; }

There might be other elegant solutions but this approach has worked brilliantly for me over the years.

-- Time flies when you don't know what you're doing

Comment on Re: Links between Mason components?
Select or Download Code
Re^2: Links between Mason components? (requirements)
by tye (Cardinal) on May 18, 2012 at 03:54 UTC

    Thanks for the examples.

    this approach has worked brilliantly for me over the years

    I guess you should count yourself lucky that you have never, in years, run into a value that contains a '+' character or a $self->name() that contains a contraction or a quote (or tons of other cases that your code brilliantly fails in the face of). Though, as I noted, I've found that these types of bugs usually take a long time to actually bite you. "Years" isn't out of the question. Heck, my latest discovery of this class of bug is in code where the problem hasn't been noticed for years.

    Perhaps there is code elsewhere that guarantees that only URL-safe characters are ever allowed in $self->name(). My experience is that such an assumption is so often correct (and usually due mostly to chance) that "nobody" notices the lack of proper encoding / escaping. Which leads to "everybody" forgetting about encoding and escaping and then to problems (sometimes just annoying, sometimes serious) when the case where that assumption doesn't hold finally crops up.

    Writing "$page?foo=$foo" while thinking "I can do that because I know that $foo never contains anything but letters" just leads to people (sometimes even the original author) copying that form of code in a situation where it isn't actually safe. If you have to think that phrase, then you should be recording it in a comment. Or, better yet, just properly encode $foo even though you "know" you don't have to.

    Only you know how to link to stuff in your web application, so no size will ever fit all; a generalized solution will probably never meet all requirements.

    The only requirements I was expecting to find a generalized solution for were the requirements of the standards that define how CGI parameters are put into a URL. But you seem blissfully unaware of those. Not that you aren't "in good company" on that front, in my experience.

    In my environment, there are no classes that represent objects to be displayed that have any business knowing how to link to pages. The objects that the browser-friendly pages display are the same objects that the REST API deals with and that the XMLRPC API deals with (and that the cron jobs deal with, etc.). So teaching those objects how to produce links to browser-friendly pages wouldn't solve the problem of providing URLs for the REST API (the XMLRPC API doesn't use a concept of 'links'). But that problem really gets bad when the new, non-Mason front end components start getting added in.

    So, I'd like the Mason-specific quirks of linking to be facilitated by the Mason. In particular, my examples showed how I was trying to use details about a specific Mason component to better abstract the types of links used by just that component (which then used the module I wrote to correctly construct a URL and then wrap that correctly into an HTML link).

    We try to put as little code in the Mason as is practical. But code that is specific to one Mason component seems a bit silly to put someplace else. But if I don't learn some significant improvements in how to do that, then moving the code out of Mason will likely be the solution.

    In reading further after I posted, I did find $m->print(...) which answered another fundamental question I had: How do I write Perl code that 'returns' content from a submodule (or method). Which means I could rewrite the URL-composing Mason code like this:

    <%def .link><%perl> my( $title, $page, %args ) = @_; my $i = $m->interp(); $m->print( "<a href='$page" ); for my $key ( sort keys %args ) { $m->print( join '=', map $i->apply_escapes($_,'u'), $key, $args{$key} ); } $m->print( "'>" . $i->apply_escapes($title,'h') . "</a>" ); </%perl></%def>

    which is much closer to correct, if still quite ugly. (One mistake is that it doesn't HTML-escape the URL. That shouldn't usually be a problem here since I'm URL-encoding most of the values and using ';' separators not '&' separators. But I can still think of ways to make it fail.)

    So, as usual, taking the time to try to carefully compose the question did lead to finding some answers. But I'm still hoping for some revelations of tricks I'm missing about doing this type of abstraction nicely in Mason.

    Thanks, again, for your reply.

    Update: Better example of proper URL building in MASON:

    <%def .link><%perl> my( $title, $page, %args ) = @_; my $i = $m->interp(); $page .= '?' . join ';', map { join '=', map $i->apply_escapes($_,'u'), $key, $args{$key} ); } sort keys %args if %args; $page = $i->apply_escapes( $page, 'h' ); $title = $i->apply_escapes( $title, 'h' ); $m->print( "<a href='$page'>$title</a>" ); </%perl></%def>

    - tye        

      The example is ofcourse very simplified to illustrate a concept. If there are user strings involved then proper care is required when building the URLs.

      If my approach doesn't suit your needs then it just goes to prove my point; one size does not fit all :-)

      -- Time flies when you don't know what you're doing

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://971172]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (8)
As of 2014-09-21 02:46 GMT
Find Nodes?
    Voting Booth?

    How do you remember the number of days in each month?

    Results (166 votes), past polls