Mojolicious-Plugin-PlainRoutes

 view release on metacpan or  search on metacpan

lib/Mojolicious/Plugin/PlainRoutes.pm  view on Meta::CPAN

use 5.014;
package Mojolicious::Plugin::PlainRoutes;
# ABSTRACT: Plaintext route definitions for Mojolicious
$Mojolicious::Plugin::PlainRoutes::VERSION = '0.07';
use Mojo::Base 'Mojolicious::Plugin';
use Mojo::Util qw/decamelize/;

has autoname => 0;

sub register {
	my ($self, $app, $conf) = @_;

	$self->autoname($conf->{autoname});

	$conf->{file} //= $app->home->rel_file("lib/".$app->moniker.".routes");

	open my $fh, '<:encoding(UTF-8)', $conf->{file};
	my $tree = $self->tokenise($fh);
	close $fh;

	$self->process($app->routes, $tree);
}

sub tokenise {
	my ($self, $input) = @_;

	if (ref $input eq 'GLOB') {
		$input = do { local $/; <$input> };
	} elsif (ref $input) {
		Carp::carp "Non-filehandle reference passed to tokenise";
		return [];
	}

	return $self->_tokenise($input);
}

sub _tokenise {
	my ($self, $input) = @_;

	$input =~ s/\r\n/\n/g;
	$input =~ s/\n\r/\n/g;
	$input =~ s/\r/\n/g;

	my %grammar = (
		comment    => qr{ \# [^\n]* }x,
		verb       => qr{ ANY | DELETE | GET | PATCH | POST | PUT }x,
		path       => qr{ / [^#\s]* }x,
		arrow      => qr{ -> }x,
		scope      => qr( { | } )x,
		action     => qr{ [\w\-:]* \. \w* }x,
		name       => qr{ \( [^)]+ \) }x,
		eol        => qr{ \n }x,
		space      => qr{ [^\S\n]+ }x,
	);

	my @words = grep { defined && length }
	              split m{( $grammar{comment}
	                      | $grammar{verb}
	                      | $grammar{path}
	                      | $grammar{arrow}
	                      | $grammar{scope}
	                      | $grammar{action}
	                      | $grammar{name}
	                      | $grammar{eol}
	                      | $grammar{space}
	                      )}x, $input;

	# Include the lexical category with the word, e.g., map:
	#   "/foo" -> { text => "/foo", category => "path" }
	my @annotated_words;
	for my $word (@words) {
		my @cats = grep { $word =~ /^$grammar{$_}$/ } keys %grammar;

		if (@cats > 1) {
			warn "$word has multiple lexical categories: @cats";
		}

		push @annotated_words, { text => $word, category => $cats[0] // '' };
	}

	# Add special EOF word to act as a clause terminator if necessary
	push @annotated_words, { text => '', category => 'eof' };

	# Initialise
	my $root    = [];
	my @nodes   = ($root);
	my %clause  = ();
	my $context = 'default';

	# Track for helpful error messages
	my $col = 1;
	my $line = 1;
	my $error = 0;

	# Define outside the loop scope so that the closure can access it
	my %word;

	# Called whenever a syntax error is encountered.
	my $syntax_error = sub {
		$error = 1;
		my $_col = $col - length $word{text};
		print STDERR qq{Syntax error in routes on line $line, col $_col: }
		          .  qq{"$word{text}" (expected a @_)\n};
	};

	for (@annotated_words) {
		%word = %$_;
		$col += length $word{text};
		if ($word{category} eq 'eol') {
			$line += 1;
			$col = 1;
		}

		# While in comment context, the parser checks for newlines and
		# otherwise does nothing.
		if ($context eq 'comment') {
			if ($word{category} eq 'eol') {



( run in 1.522 second using v1.01-cache-2.11-cpan-71847e10f99 )