* Dimitar Petrov <mita...@gmail.com> [2013-04-12 19:25]: > The Catalyst team is proud to announce that the latest version of > Catalyst (5.90030) is released on CPAN.
Oh frabjous day! > - make $app->uri_for and related methods return something sane, when > called as an application method, instead of a context method. Now > if you call MyApp::Web->uri_for(...) you will get a generic URI > object that you need to resolve manually. Calloo! Just to give you and idea of what this did for me, let me show here some sample code. Background: I recently split my MyApp into MyApp and MyPublicApp, both loaded within the same app.psgi, with different requests dispatched to each using Plack::App::URLMap. This cleaned up a previously very hairy and precarious dispatch situation wherein I had a few custom attributes to make sure that requests would dispatch only along certain chains in the app depending on the hostname they were for and whether or not they used SSL. It was a massive win. But I needed a way to generate URIs for MyApp from within MyPublicApp’s templates. At first I somewhat punted on the question, and just started with this: sub uri_for_fileticket { my $c = shift; my ( $file_id, $ticket_id, $filename ) = @_; sprintf 'https://%s/file/%d/ticket/%d/%s', ( $c->config->{'vhosts'}{'application'}, $file_id, $ticket_id, URI::Escape::uri_escape( $filename ), ); } And in some places I didn’t use uri_for at all, just reluctantly hard-coded the URIs. But soon I ran into needing to link to other actions with variable arguments, and I sure wasn’t going to write more of the above. Cue the first, appalling hack: package MyPublicApp; use MyApp (); use HTTP::Request (); use HTTP::Response (); use HTTP::Message::PSGI (); sub uri_for_app_action { # XXX terrible hack magick: # make a mock request to the backend app # for a special action which captures the context into the PSGI env # where we can then pick it up and use it to generate URIs my $c = shift; state $app = MyApp->psgi_app; my $env = HTTP::Request->new( GET => 'http://localhost/ctx' )->to_psgi; my $res = HTTP::Response->from_psgi( $app->psgi_app->( $env ) ); my $hostname = $c->config->{'vhosts'}{'application'}; my $c = delete $env->{'myapp.ctx'}; # break the ref cycle to avoid leaking memory $c->uri_for_action( @_ )->rel->abs( "https://$hostname/" ); } package MyApp::Controller::Root; sub ctx : Chained { my $self = shift; my ( $c ) = @_; $c->engine->env->{'myapp.ctx'} = $c; $c->res->status( 404 ); $c->res->body( '' ); } Yeughw… eughw. Yick. Yew. But it worked. Note that omitted here are various other bits elsewhere that make sure to skip auth, logging, etc. when dispatch goes to this `/ctx` action. At some point this action and its assorted supporting bits mutated into an `if` block within `around dispatch` within MyApp.pm, which at least removed some litter from the code. The terrible ugliness of this hack kept bugging me though. Finally I had enough and decided see whether Catalyst exposed enough enough public methods to reimplement the relevant-to-me bits of `uri_for` to do its job without depending on a context and without too much hassle. Well… see for yourself. sub uri_for_app_action { my $c = shift; my ( $path, $captures ) = @_; state $dispatcher = MyApp->dispatcher; my $action = $dispatcher->get_action_by_path( $path ); my @args = splice @$captures, $dispatcher->expand_action( $action )->number_of_captures; unshift @args, '' if @args; # ensure leading "/" if any args, omit if none return 'https://' . $c->config->{'vhosts'}{'application'} . $dispatcher->uri_for_action( $action, $captures ) . join '/', map { s/\?/%3F/g; $_ } my @copy = grep { defined } @args; } OK, could’ve been worse I suppose. And at least this did get me rid of the `/ctx` thing. Good thing I only use Chained dispatch, which meant there was lots of crud in `uri_for` that my emulation of it didn’t need to bother duplicating, otherwise this could not have been a win. And now? I just upgraded to 5.90030 and immediately took advantage: sub uri_for_app_action { my $c = shift; MyApp->uri_for_action( @_ ) ->abs( 'https://' . $c->config->{'vhosts'}{'application'} ); } Much better!! So, so, so much, much, much better! Just as it should have been (able to be) from the start, that is how it is now. > - Added cpanfile as a way to notice we are a dev checkout. Callay! (You don’t want to see the hack I had in Makefile.PL to make it quicker to skim and write the dep list. :-) Though that one was, of course, not anywhere near as bad as the above.) Thanks a whole bunch, -- Aristotle Pagaltzis // <http://plasmasturm.org/> _______________________________________________ List: Catalyst@lists.scsys.co.uk Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/ Dev site: http://dev.catalyst.perl.org/