<commit_before>package Mojolicious::Routes::Route;
use Mojo::Base -base;

use Carp 'croak';
use Mojo::Util;
use Mojolicious::Routes::Pattern;
use Scalar::Util qw(blessed weaken);

has [qw(inline parent partial)];
has 'children' => sub { [] };
has pattern    => sub { Mojolicious::Routes::Pattern->new };

sub AUTOLOAD {
  my $self = shift;

  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
  croak "Undefined subroutine &${package}::$method called"
    unless blessed $self && $self->isa(__PACKAGE__);

  # Call shortcut with current route
  croak qq{Can't locate object method "$method" via package "$package"}
    unless my $shortcut = $self->root->shortcuts->{$method};
  return $self->$shortcut(@_);
}

sub add_child {
  my ($self, $route) = @_;
  weaken $route->remove->parent($self)->{parent};
  push @{$self->children}, $route;
  return $self;
}

sub any { shift->_generate_route(ref $_[0] eq 'ARRAY' ? shift : [], @_) }

sub bridge { shift->route(@_)->inline(1) }

sub delete { shift->_generate_route(DELETE => @_) }

sub detour { shift->partial(1)->to(@_) }

sub find {
  my ($self, $name) = @_;

  my @children = (@{$self->children});
  my $candidate;
  while (my $child = shift @children) {

    # Match
    $candidate = $child->has_custom_name ? return $child : $child
      if $child->name eq $name;

    # Search children too
    push @children, @{$child->children};
  }

  return $candidate;
}

sub get { shift->_generate_route(GET => @_) }

sub has_conditions {
  my $self = shift;
  return 1 if @{$self->over || []};
  return undef unless my $parent = $self->parent;
  return $parent->has_conditions;
}

sub has_custom_name { !!shift->{custom} }

sub has_websocket {
  my $self = shift;
  return 1 if $self->is_websocket;
  return undef unless my $parent = $self->parent;
  return $parent->is_websocket;
}

sub is_endpoint { $_[0]->inline ? undef : !@{$_[0]->children} }

sub is_websocket { !!shift->{websocket} }

sub name {
  my $self = shift;
  return $self->{name} unless @_;
  @$self{qw(name custom)} = (shift, 1);
  return $self;
}

sub new { shift->SUPER::new->parse(@_) }

sub options { shift->_generate_route(OPTIONS => @_) }

sub over {
  my $self = shift;

  # Routes with conditions can't be cached
  return $self->{over} unless @_;
  my $conditions = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
  return $self unless @$conditions;
  $self->{over} = $conditions;
  $self->root->cache(0);

  return $self;
}

sub parse {
  my $self = shift;
  $self->{name} = $self->pattern->parse(@_)->pattern // '';
  $self->{name} =~ s/\W+//g;
  return $self;
}

sub patch { shift->_generate_route(PATCH => @_) }
sub post  { shift->_generate_route(POST  => @_) }
sub put   { shift->_generate_route(PUT   => @_) }

sub remove {
  my $self = shift;
  return $self unless my $parent = $self->parent;
  @{$parent->children} = grep { $_ ne $self } @{$parent->children};
  return $self->parent(undef);
}

sub render {
  my ($self, $path, $values) = @_;

  # Render pattern (if necessary)
  my $endpoint = !@{$self->children};
  if (defined((my $pattern = $self->pattern)->pattern) || $endpoint) {
    my $prefix = $pattern->render($values, $endpoint);
    $path = "$prefix$path" unless $prefix eq '/';
  }

  # Let parent render
  return $path =~ m!^/! ? $path : "/$path" unless my $parent = $self->parent;
  return $parent->render($path, $values);
}

sub root {
  my $root = my $parent = shift;
  $root = $parent while $parent = $parent->parent;<commit_msg>the pattern does not return a bare slash anymore<commit_after>package Mojolicious::Routes::Route;
use Mojo::Base -base;

use Carp 'croak';
use Mojo::Util;
use Mojolicious::Routes::Pattern;
use Scalar::Util qw(blessed weaken);

has [qw(inline parent partial)];
has 'children' => sub { [] };
has pattern    => sub { Mojolicious::Routes::Pattern->new };

sub AUTOLOAD {
  my $self = shift;

  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
  croak "Undefined subroutine &${package}::$method called"
    unless blessed $self && $self->isa(__PACKAGE__);

  # Call shortcut with current route
  croak qq{Can't locate object method "$method" via package "$package"}
    unless my $shortcut = $self->root->shortcuts->{$method};
  return $self->$shortcut(@_);
}

sub add_child {
  my ($self, $route) = @_;
  weaken $route->remove->parent($self)->{parent};
  push @{$self->children}, $route;
  return $self;
}

sub any { shift->_generate_route(ref $_[0] eq 'ARRAY' ? shift : [], @_) }

sub bridge { shift->route(@_)->inline(1) }

sub delete { shift->_generate_route(DELETE => @_) }

sub detour { shift->partial(1)->to(@_) }

sub find {
  my ($self, $name) = @_;

  my @children = (@{$self->children});
  my $candidate;
  while (my $child = shift @children) {

    # Match
    $candidate = $child->has_custom_name ? return $child : $child
      if $child->name eq $name;

    # Search children too
    push @children, @{$child->children};
  }

  return $candidate;
}

sub get { shift->_generate_route(GET => @_) }

sub has_conditions {
  my $self = shift;
  return 1 if @{$self->over || []};
  return undef unless my $parent = $self->parent;
  return $parent->has_conditions;
}

sub has_custom_name { !!shift->{custom} }

sub has_websocket {
  my $self = shift;
  return 1 if $self->is_websocket;
  return undef unless my $parent = $self->parent;
  return $parent->is_websocket;
}

sub is_endpoint { $_[0]->inline ? undef : !@{$_[0]->children} }

sub is_websocket { !!shift->{websocket} }

sub name {
  my $self = shift;
  return $self->{name} unless @_;
  @$self{qw(name custom)} = (shift, 1);
  return $self;
}

sub new { shift->SUPER::new->parse(@_) }

sub options { shift->_generate_route(OPTIONS => @_) }

sub over {
  my $self = shift;

  # Routes with conditions can't be cached
  return $self->{over} unless @_;
  my $conditions = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
  return $self unless @$conditions;
  $self->{over} = $conditions;
  $self->root->cache(0);

  return $self;
}

sub parse {
  my $self = shift;
  $self->{name} = $self->pattern->parse(@_)->pattern // '';
  $self->{name} =~ s/\W+//g;
  return $self;
}

sub patch { shift->_generate_route(PATCH => @_) }
sub post  { shift->_generate_route(POST  => @_) }
sub put   { shift->_generate_route(PUT   => @_) }

sub remove {
  my $self = shift;
  return $self unless my $parent = $self->parent;
  @{$parent->children} = grep { $_ ne $self } @{$parent->children};
  return $self->parent(undef);
}

sub render {
  my ($self, $path, $values) = @_;

  # Render pattern (if necessary)
  my $endpoint = !@{$self->children};
  my $pattern  = $self->pattern;
  $path = $pattern->render($values, $endpoint) . $path
    if $endpoint || defined($pattern->pattern);

  # Let parent render
  return $path =~ m!^/! ? $path : "/$path" unless my $parent = $self->parent;
  return $parent->render($path, $values);
}

sub root {
  my $root = my $parent = shift;
  $root = $parent while $parent = $parent->parent;
{'fraction_non_alphanumeric': 0.19117010532390707, 'fraction_numerical': 0.0031741451450007212, 'mean_word_length': 3.837404047452896, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 135, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3689886', 'n_tokens_mistral': 2479, 'n_tokens_neox': 2422, 'n_words': 637}