Skip Menu |

This queue is for tickets about the Catalyst-Manual CPAN distribution.

Report information
The Basics
Id: 77722
Status: open
Priority: 0/
Queue: Catalyst-Manual

People
Owner: Nobody in particular
Requestors: john.deighan [...] gmail.com
Cc:
AdminCc:

Bug Information
Severity: Normal
Broken in: 5.9002
Fixed in: (no value)



Subject: problems with end() and auto()
I've run into 2 problems while going through the tutorial. They might just be misunderstandings on my part, but I believe that both point to needed improvements in the tutorial code. Both took me quite a bit of time to discover the problem and solution. The most recent was with an end() action in Root.pm. Reading over the tutorial, it stated that "end" was one of 5 "special, private" actions. So, if it's a private action, I should be able to add the ":Private" attribute, right? It already had ":ActionClass('RenderView')", which I left in place, but also added ":Private". Unfortunately, that caused the TT view to not be rendered, even though the 'template' key was set in the stash, and the output was completely empty. The other problem, now that I think of it, happened when I tried to follow a tutorial in one of the Catalyst books that I've purchased, not the online tutorial. I had an "auto" action which looked like this: sub auto :Private { my($self, $c) = @_; push(@{$c->stash->{matches}}, 'Root -> auto'); } Well, I believe that all functions should have a "return" statement. It's in the "Perl Best Practices" book, but more importantly, I believe in it. So, I added a final "return;". Unfortunately, that caused the entire response to be aborted - other actions should have been fired, but they were not. It took me a long time to find out that if I removed the "return;" (or replaced it with "return 1;"), everything started working again. Realizing that Perl does some things that I consider very bad, like functions that don't appear to return any values really do return values, and functions return the last expression evaluated even though you never asked for anything to be returned, here is what I think is happening: 1. the call to push() returns something (probably the thing being pushed, but perhaps the resulting list - who knows) 2. the call to auto() then returns that as the function return value, even though there is no explicit return statement. 3. if the call to auto returns a false value, the entire response is aborted. I'm sure that's documented somewhere, but I'm working on a beginner's tutorial. Point is - if the example code always returned a value explicitly when a return value is used in any way, newbies (not a Perl newbie, but a Catalyst newbie) wouldn't make these kinds of mistakes and spend a lot of time trying to track down what happened. Of course, if I entered the example code exactly as it is in the tutorial files, I wouldn't have had these problems either. But then, I want to understand what's going on and would rather enter code that meets my usual programming standards.
Subject: Re: [rt.cpan.org #77722] problems with end() and auto()
Date: Sat, 9 Jun 2012 08:47:05 +1000
To: bug-Catalyst-Manual [...] rt.cpan.org
From: Kieren Diment <diment [...] gmail.com>
On 09/06/2012, at 8:21 AM, John Deighan via RT wrote: Show quoted text
> Fri Jun 08 18:21:08 2012: Request 77722 was acted upon. > Transaction: Ticket created by jdeighan > Queue: Catalyst-Manual > Subject: problems with end() and auto() > Broken in: 5.9002 > Severity: Normal > Owner: Nobody > Requestors: john.deighan@gmail.com > Status: new > Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=77722 > > > > I've run into 2 problems while going through the tutorial. They might > just be misunderstandings on my part, but I believe that both point to > needed improvements in the tutorial code. Both took me quite a bit of > time to discover the problem and solution. > > The most recent was with an end() action in Root.pm. Reading over the > tutorial, it stated that "end" was one of 5 "special, private" actions. > So, if it's a private action, I should be able to add the ":Private" > attribute, right? It already had ":ActionClass('RenderView')", which I > left in place, but also added ":Private". Unfortunately, that caused the > TT view to not be rendered, even though the 'template' key was set in > the stash, and the output was completely empty.
End used to be a :Private action, you're right. These days it's almost like a private action except it also calls to the appropriate view unless otherwise told to do so (in old catalysts you'd have to do it explicitly). This should probably be fixed. Could you file a separate bug for this please (this bug report is not very granular). Show quoted text
> > The other problem, now that I think of it, happened when I tried to > follow a tutorial in one of the Catalyst books that I've purchased, not > the online tutorial. I had an "auto" action which looked like this: > > sub auto :Private { my($self, $c) = @_; > > push(@{$c->stash->{matches}}, 'Root -> auto'); > } >
Auto bails on the dispatch cycle if it returns undef. Push returns the created array (but is almost always called in void context). All subs return the value from the last statement if there's no explicit return value. The purpose of the example in the book was to demonstrate the dispatch, I suppose at the time I thought that an explicit return value was an unnecessary distraction Show quoted text
> Well, I believe that all functions should have a "return" statement. > It's in the "Perl Best Practices" book, but more importantly, I believe > in it. So, I added a final "return;".
A final 'return 1' would have worked fine. Did you try that? or even return (push(@{$c->stash->{matches}}, 'Root -> auto'));' Show quoted text
> Unfortunately, that caused the > entire response to be aborted - other actions should have been fired, > but they were not. It took me a long time to find out that if I removed > the "return;" (or replaced it with "return 1;"), everything started > working again. Realizing that Perl does some things that I consider very > bad, like functions that don't appear to return any values really do > return values, and functions return the last expression evaluated even > though you never asked for anything to be returned, here is what I think > is happening: > > 1. the call to push() returns something (probably the thing being > pushed, but perhaps the resulting list - who knows)
The 'who knows' here suggests that you should spend some time exploring in the perl debugger - with normal code and catalyst code. Show quoted text
> > 2. the call to auto() then returns that as the function return value, > even though there is no explicit return statement. > > 3. if the call to auto returns a false value, the entire response is > aborted. I'm sure that's documented somewhere, but I'm working on a > beginner's tutorial.
It is. It should be documented in the tutorial, if it isn't please identify the first mention of auto where it should be mentioned. Show quoted text
> > Point is - if the example code always returned a value explicitly when a > return value is used in any way, newbies (not a Perl newbie, but a > Catalyst newbie) wouldn't make these kinds of mistakes and spend a lot > of time trying to track down what happened. >
You may be getting the book code and the tutorial code confused. Of course it may be worth mentioning that implicit returns are possible, and while not encouraged sometimes make sense somewhere in the tutorial. Identifying where this passage could be would be useful. Show quoted text
> Of course, if I entered the example code exactly as it is in the > tutorial files, I wouldn't have had these problems either. But then, I > want to understand what's going on and would rather enter code that > meets my usual programming standards.
Yes, there was a design decision to avoid the explicit return in the dispatcher code chapter to avoid an unnecessary distraction. In this instance I feel the lack of an explicit return was fine, but I usually try to avoid them. Generally I will only use implicit returns for two line subs. i.e: sub something { my ($self, @params}; $self->do_something(@params); } rather than: sub something { my ($self, @params}; return $self->do_something(@params); }