Catalyst-Runtime

 view release on metacpan or  search on metacpan

lib/Catalyst.pm  view on Meta::CPAN

        for my $comp ( sort keys %{ $class->components } ) {
            my $type = ref $class->components->{$comp} ? 'instance' : 'class';
            $t->row( $comp, $type );
        }
        $class->log->debug( "Loaded components:\n" . $t->draw . "\n" )
          if ( keys %{ $class->components } );
    }

    # Add our self to components, since we are also a component
    if( $class->isa('Catalyst::Controller') ){
      $class->components->{$class} = $class;
    }

    $class->setup_actions;

    if ( $class->debug ) {
        my $name = $class->config->{name} || 'Application';
        $class->log->info("$name powered by Catalyst $Catalyst::VERSION");
    }

    if ($class->config->{case_sensitive}) {
        $class->log->warn($class . "->config->{case_sensitive} is set.");
        $class->log->warn("This setting is deprecated and planned to be removed in Catalyst 5.81.");
    }

    # call these so we pre setup the composed classes
    $class->composed_request_class;
    $class->composed_response_class;
    $class->composed_stats_class;

    $class->setup_finalize;

    # Flush the log for good measure (in case something turned off 'autoflush' early)
    $class->log->_flush() if $class->log->can('_flush');

    return $class || 1; # Just in case someone named their Application 0...
}

=head2 $app->setup_finalize

A hook to attach modifiers to. This method does not do anything except set the
C<setup_finished> accessor.

Applying method modifiers to the C<setup> method doesn't work, because of quirky things done for plugin setup.

Example:

    after setup_finalize => sub {
        my $app = shift;

        ## do stuff here..
    };

=cut

sub setup_finalize {
    my ($class) = @_;
    $class->setup_finished(1);
}

=head2 $c->uri_for( $path?, @args?, \%query_values?, \$fragment? )

=head2 $c->uri_for( $action, \@captures?, @args?, \%query_values?, \$fragment? )

=head2 $c->uri_for( $action, [@captures, @args], \%query_values?, \$fragment? )

Constructs an absolute L<URI> object based on the application root, the
provided path, and the additional arguments and query parameters provided.
When used as a string, provides a textual URI.  If you need more flexibility
than this (i.e. the option to provide relative URIs etc.) see
L<Catalyst::Plugin::SmartURI>.

If no arguments are provided, the URI for the current action is returned.
To return the current action and also provide @args, use
C<< $c->uri_for( $c->action, @args ) >>.

If the first argument is a string, it is taken as a public URI path relative
to C<< $c->namespace >> (if it doesn't begin with a forward slash) or
relative to the application root (if it does). It is then merged with
C<< $c->request->base >>; any C<@args> are appended as additional path
components; and any C<%query_values> are appended as C<?foo=bar> parameters.

B<NOTE> If you are using this 'stringy' first argument, we skip encoding and
allow you to declare something like:

    $c->uri_for('/foo/bar#baz')

Where 'baz' is a URI fragment.  We consider this first argument string to be
'expert' mode where you are expected to create a valid URL and we for the most
part just pass it through without a lot of internal effort to escape and encode.

If the first argument is a L<Catalyst::Action> it represents an action which
will have its path resolved using C<< $c->dispatcher->uri_for_action >>. The
optional C<\@captures> argument (an arrayref) allows passing the captured
variables that are needed to fill in the paths of Chained and Regex actions;
once the path is resolved, C<uri_for> continues as though a path was
provided, appending any arguments or parameters and creating an absolute
URI.

The captures for the current request can be found in
C<< $c->request->captures >>, and actions can be resolved using
C<< Catalyst::Controller->action_for($name) >>. If you have a private action
path, use C<< $c->uri_for_action >> instead.

  # Equivalent to $c->req->uri
  $c->uri_for($c->action, $c->req->captures,
      @{ $c->req->args }, $c->req->params);

  # For the Foo action in the Bar controller
  $c->uri_for($c->controller('Bar')->action_for('Foo'));

  # Path to a static resource
  $c->uri_for('/static/images/logo.png');

In general the scheme of the generated URI object will follow the incoming request
however if your targeted action or action chain has the Scheme attribute it will
use that instead.

Also, if the targeted Action or Action chain declares Args/CaptureArgs that have
type constraints, we will require that your proposed URL verify on those declared
constraints.

=cut

sub uri_for {
    my ( $c, $path, @args ) = @_;

    if ( $path->$_isa('Catalyst::Controller') ) {
        $path = $path->path_prefix;
        $path =~ s{/+\z}{};
        $path .= '/';
    }

    my $fragment =  ((scalar(@args) && ref($args[-1]) eq 'SCALAR') ? ${pop @args} : undef );

    unless(blessed $path) {
      if (defined($path) and $path =~ s/#(.+)$//)  {
        if(defined($1) and defined $fragment) {
          carp "Abiguious fragment declaration: You cannot define a fragment in '$path' and as an argument '$fragment'";
        }
        if(defined($1)) {
          $fragment = $1;
        }
      }
    }

    my $params =
      ( scalar @args && ref $args[$#args] eq 'HASH' ? pop @args : {} );

    undef($path) if (defined $path && $path eq '');

    carp "uri_for called with undef argument" if grep { ! defined $_ } @args;

    my $target_action = $path->$_isa('Catalyst::Action') ? $path : undef;
    if ( $path->$_isa('Catalyst::Action') ) { # action object
        s|/|%2F|g for @args;
        my $captures = [ map { s|/|%2F|g; $_; }
                        ( scalar @args && ref $args[0] eq 'ARRAY'
                         ? @{ shift(@args) }
                         : ()) ];

        my $action = $path;
        my $expanded_action = $c->dispatcher->expand_action( $action );
        my $num_captures = $expanded_action->number_of_captures;

        # ->uri_for( $action, \@captures_and_args, \%query_values? )
        if( !@args && $action->number_of_args && @$captures > $num_captures ) {
          unshift @args, splice @$captures, $num_captures;
        }

        if($num_captures) {
          unless($expanded_action->match_captures_constraints($c, $captures)) {
            $c->log->debug("captures [@{$captures}] do not match the type constraints in actionchain ending with '$expanded_action'")
                if $c->debug;
            return undef;
          }
        }

        $path = $c->dispatcher->uri_for_action($action, $captures);
        if (not defined $path) {
            $c->log->debug(qq/Can't find uri_for action '$action' @$captures/)
                if $c->debug;
            return undef;
        }
        $path = '/' if $path eq '';

        # At this point @encoded_args is the remaining Args (all captures removed).
        if($expanded_action->has_args_constraints) {
          unless($expanded_action->match_args($c,\@args)) {
             $c->log->debug("args [@args] do not match the type constraints in action '$expanded_action'")
                if $c->debug;
             return undef;
          }
        }
    }

    unshift(@args, $path);

    unless (defined $path && $path =~ s!^/!!) { # in-place strip
        my $namespace = $c->namespace;
        if (defined $path) { # cheesy hack to handle path '../foo'
           $namespace =~ s{(?:^|/)[^/]+$}{} while $args[0] =~ s{^\.\./}{};
        }
        unshift(@args, $namespace || '');
    }

    # join args with '/', or a blank string
    my $args = join('/', grep { defined($_) } @args);
    $args =~ s/\?/%3F/g; # STUPID STUPID SPECIAL CASE
    $args =~ s!^/+!!;

    my ($base, $class) = ('/', 'URI::_generic');
    if(blessed($c)) {
      $base = $c->req->base;
      if($target_action) {
        $target_action = $c->dispatcher->expand_action($target_action);
        if(my $s = $target_action->scheme) {
          $s = lc($s);
          $class = "URI::$s";
          $base->scheme($s);
        } else {
          $class = ref($base);
        }
      } else {
        $class = ref($base);
      }

      $base =~ s{(?<!/)$}{/};
    }

    my $query = '';
    if (my @keys = keys %$params) {
      # somewhat lifted from URI::_query's query_form
      $query = '?'.join('&', map {
          my $val = $params->{$_};
          my $key = encode_utf8($_);
          # using the URI::Escape pattern here so utf8 chars survive
          $key =~ s/([^A-Za-z0-9\-_.!~*'() ])/$URI::Escape::escapes{$1}/go;
          $key =~ s/ /+/g;

          $val = '' unless defined $val;
          (map {
              my $param = encode_utf8($_);
              # using the URI::Escape pattern here so utf8 chars survive
              $param =~ s/([^A-Za-z0-9\-_.!~*'() ])/$URI::Escape::escapes{$1}/go;
              $param =~ s/ /+/g;

              "${key}=$param";
          } ( ref $val eq 'ARRAY' ? @$val : $val ));
      } @keys);
    }

    $base = encode_utf8 $base;
    $base =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
    $args = encode_utf8 $args;
    $args =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;

    if(defined $fragment) {
      if(blessed $path) {
        $fragment = encode_utf8($fragment);
        $fragment =~ s/([^A-Za-z0-9\-_.!~*'() ])/$URI::Escape::escapes{$1}/go;
        $fragment =~ s/ /+/g;
      }
      $query .= "#$fragment";
    }

    my $res = bless(\"${base}${args}${query}", $class);
    $res;
}

=head2 $c->uri_for_action( $path, \@captures_and_args?, @args?, \%query_values? )

=head2 $c->uri_for_action( $action, \@captures_and_args?, @args?, \%query_values? )

=over

=item $path

A private path to the Catalyst action you want to create a URI for.

This is a shortcut for calling C<< $c->dispatcher->get_action_by_path($path)
>> and passing the resulting C<$action> and the remaining arguments to C<<
$c->uri_for >>.

You can also pass in a Catalyst::Action object, in which case it is passed to
C<< $c->uri_for >>.

Note that although the path looks like a URI that dispatches to the wanted action, it is not a URI, but an internal path to that action.

For example, if the action looks like:

 package MyApp::Controller::Users;

 sub lst : Path('the-list') {}

You can use:

 $c->uri_for_action('/users/lst')

and it will create the URI /users/the-list.

=item \@captures_and_args?

Optional array reference of Captures (i.e. C<CaptureArgs> or C<< $c->req->captures >>)
and arguments to the request. Usually used with L<Catalyst::DispatchType::Chained>
to interpolate all the parameters in the URI.

=item @args?

Optional list of extra arguments - can be supplied in the
C<< \@captures_and_args? >> array ref, or here - whichever is easier for your
code.

Your action can have zero, a fixed or a variable number of args (e.g.
C<< Args(1) >> for a fixed number or C<< Args() >> for a variable number)..

=item \%query_values?

Optional array reference of query parameters to append. E.g.

  { foo => 'bar' }

will generate



( run in 1.495 second using v1.01-cache-2.11-cpan-364913b4093 )