Catalyst-Plugin-CachedUriForAction

 view release on metacpan or  search on metacpan

lib/Catalyst/Plugin/CachedUriForAction.pm  view on Meta::CPAN


		my $n_args = $xa->number_of_args; # might be undef to mean "any number"
		my $tmpl = $c->uri_for( $action, [ ("\0\0\0\0") x $n_caps ], ("\0\0\0\0") x ( $n_args || 0 ) );
		my ( $prefix, @part ) = split /%00%00%00%00/, $tmpl, -1;
		$prefix =~ s!\A/!!;
		$cache->{ '/' . $action->reverse } = [ $n_caps, $n_args, \@part, $prefix ];
	}
}

sub uri_for_action {
	my $c = shift;

	my $dispatcher = $c->dispatcher;
	my $cache = $dispatcher && $dispatcher->{(CACHE_KEY)}
		or return $c->next::method( @_ ); # fall back if called too early

	my $action   = shift;
	my $captures = @_ && 'ARRAY'  eq ref $_[0]  ? shift : [];
	my $fragment = @_ && 'SCALAR' eq ref $_[-1] ? pop   : undef;
	my $params   = @_ && 'HASH'   eq ref $_[-1] ? pop   : undef;

	$action = '/' . $dispatcher->get_action_by_path( $action )->reverse
		if ref $action
		and do { local $@; eval { $action->isa( 'Catalyst::Action' ) } };

	my $info = $cache->{ $action }
		or Carp::croak "Can't find action for path '$action' in uri_for_action";

	my ( $uri, $base ) = '';
	if ( ref $c ) {
		$base = $c->request->base;
		$uri = '/' if $$base !~ m!/\z!;
	} else { # fallback if called as class method
		$base = bless \( my $tmp = '' ), 'URI::_generic';
		$uri = '/';
	}

	my ( $n_caps, $n_args, $extra_parts ) = @$info;
	$uri .= $info->[-1];

	# this is not very sensical but it has to be like this because it is what Catalyst does:
	# the :Args() case (i.e. any number of args) is grouped with the :Args(0) case (i.e. no args)
	# instead of being grouped with with the :Args(N) case (i.e. a fixed non-zero number of args)
	if ( $n_args ) {
		Carp::croak "Not enough captures for path '$action' (need $n_caps) in uri_for_action"
			if @$captures < $n_caps;
	} else {
		Carp::croak "Wrong number of captures for path '$action' (need $n_caps) in uri_for_action"
			if @$captures != $n_caps;
	}

	# the following is carefully written to
	# - loop over every input array exactly once
	# - avoid any conditionals inside each loop body
	# - use only simple loop forms that are specially optimised by the perl interpreter
	my $i = -1;
	if ( defined $n_args ) { # the non-slurpy case
		Carp::croak "Wrong number of args+captures for path '$action' (need ".@$extra_parts.") in uri_for_action"
			if ( @$captures + @_ ) != @$extra_parts;
		# and now since @$extra_parts is exactly the same length as @$captures and @_ combined
		# iterate over those arrays and use a cursor into @$extra_parts to interleave its elements
		for ( @$captures ) { ( $uri .= uri_encode_utf8 $_ ) .= $extra_parts->[ ++$i ] }
		for ( @_ )         { ( $uri .= uri_encode_utf8 $_ ) .= $extra_parts->[ ++$i ] }
	} else {
		# in the slurpy case, the size of @$extra_parts is determined by $n_caps alone since $n_args was undef
		# and as we checked above @$captures alone has at least length $n_caps
		# so we will need all of @$captures to cover @$extra_parts, and may then still have some of it left over
		# so iterate over @$extra_parts and use a cursor into @$captures to interleave its elements
		for ( @$extra_parts )       { ( $uri .= uri_encode_utf8 $captures->[ ++$i ] ) .= $_ }
		# and then append the rest of @$captures, and then everything from @_ after that
		for ( ++$i .. $#$captures ) { ( $uri .= '/' ) .= uri_encode_utf8 $captures->[ $_ ] }
		for ( @_ )                  { ( $uri .= '/' ) .= uri_encode_utf8 $_ }
	}

	$uri =~ s/%2B/+/g;
	substr $uri, 0, 0, $$base;

	if ( defined $params ) {
		my $query = '';
		my $delim = $URI::DEFAULT_QUERY_FORM_DELIMITER || '&';
		my ( $v, $enc_key );
		for my $key ( sort keys %$params ) {
			$v = $params->{ $key };
			if ( 'ARRAY' ne ref $v ) {
				( $query .= $delim ) .= uri_encode_utf8 $key;
				( $query .= '=' ) .= uri_encode_utf8 $v if defined $v;
			} elsif ( @$v ) {
				$enc_key = $delim . uri_encode_utf8 $key;
				for ( @$v ) {
					$query .= $enc_key;
					( $query .= '=' ) .= uri_encode_utf8 $_ if defined;
				}
			}
		}
		if ( '' ne $query ) {
			$query =~ s/%20/+/g;
			( $uri .= '?' ) .= substr $query, length $delim;
		}
	}

	if ( defined $fragment ) {
		( $uri .= '#' ) .= uri_encode_utf8 $$fragment;
	}

	bless \$uri, ref $base;
}

BEGIN { delete $Catalyst::Plugin::CachedUriForAction::{'uri_encode_utf8'} }

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Catalyst::Plugin::CachedUriForAction - drop-in supercharger for uri_for_action

=head1 SYNOPSIS

 use Catalyst qw( CachedUriForAction );

=head1 DESCRIPTION

This provides a (mostly) drop-in replacement version of C<uri_for_action>.



( run in 2.156 seconds using v1.01-cache-2.11-cpan-140bd7fdf52 )