PlackX-Framework

 view release on metacpan or  search on metacpan

lib/PlackX/Framework/Handler.pm  view on Meta::CPAN

use v5.26;
use warnings;
use experimental 'signatures';
package PlackX::Framework::Handler {
  use PXF::Util ();
  use Scalar::Util qw(blessed);
  use HTTP::Status qw(status_message);

  my  %globals;
  our $psgix_streaming; # memoized, but in an "our" var so tests can change it
  sub use_global_request_response    { } # Override in subclass to turn on
  sub global_request        ($class) { $globals{$class->app_namespace}->[0]            }
  sub global_response       ($class) { $globals{$class->app_namespace}->[1]            }
  sub error_response ($class, $code) { [$code, [], [status_message($code)." ($code)"]] } # Override for nicer message

  #
  # App assembly section
  #
  sub build_app ($class, %options) {
    my $app = prebuild_app($class, %options);
    if (my $wrapper = $class->app_namespace->can('apply_middleware')) {
      $app = $wrapper->($app);
    }
    return $app;
  }

  sub prebuild_app ($class, %options)  {
    # Freeze the router
    my $rt_engine = ($class->app_namespace . '::Router::Engine')->instance;
    $rt_engine->freeze;

    # Make app
    my $app = sub ($env) {
      psgi_response($class->handle_request($env, undef, $rt_engine))
    };

    # if app_base is specified, use URLMap, we don't need to cascade
    if (my $app_base = $class->app_base) {
      require Plack::App::URLMap;
      my $mapper = Plack::App::URLMap->new;
      $mapper->map($app_base => $app);
      #$mapper->map('/'       => $file_app) if $file_app;
      return $mapper->to_app;
    }

    return $app
  }

  sub app_base ($class) {
    my $base = eval { $class->app_namespace->app_base } || eval { $class->app_namespace->uri_prefix } || '';
    $base = '/'.$base if $base and length $base and substr($base,0,1) ne '/';
    return $base;
  }

  #
  # Request handling section
  #
  sub handle_request ($class, $env_or_req, $maybe_resp = undef, $maybe_rt_engine = undef) {
    my $app_namespace  = $class->app_namespace;

    # Get or create default request and response objects
    my $env      = $class->env_or_req_to_env($env_or_req);
    my $request  = $class->env_or_req_to_req($env_or_req);
    my $response = $maybe_resp || ($app_namespace . '::Response')->new->set_defaults;

    # Memoize server info and maybe set request/response globals
    $psgix_streaming = $env->{'psgi.streaming'} ? !!1 : !!0
      if !defined $psgix_streaming;
    $globals{$app_namespace} = [$request, $response]
      if $class->use_global_request_response;

    # Set up stash
    my $stash = ($request->stash or $response->stash or {});
    $request->stash($stash);
    $response->stash($stash);

    # Maybe set up Templating, if loaded
    if (PXF::Util::is_module_loaded($app_namespace . '::Template')) {
      eval {
        my $template = ($app_namespace . '::Template')->new($response);
        $template->set(STASH => $stash, REQUEST => $request, RESPONSE => $response);
        $response->template($template);
      } or do {
        warn "$app_namespace\::Template module loaded, but unable to set up template: $@"
        .    "  (Hint: Did you use/import from it or set up templating manually?)\n";
      };
    }

    # Clear flash if set, set response defaults, and route request
    $response->flash(undef) if $request->flash;
    return $class->route_request($request, $response, $maybe_rt_engine);
  }

  sub route_request ($class, $request, $response, $rt_engine = undef) {
    $rt_engine //= ($class->app_namespace . '::Router::Engine')->instance;
    if (my $match = $rt_engine->match($request)) {
      $request->route_base($match->{base}) if defined $match->{base};



( run in 4.324 seconds using v1.01-cache-2.11-cpan-64ef6c95b5d )