my $method_registry = {};
my $method_by_name = {};
+my $method_path_lookup = {};
our $AUTOLOAD; # it's a package global
my $match_re = [];
my $match_name = [];
+ my $errprefix;
+
+ my $method;
+ if ($info->{subclass}) {
+ $errprefix = "register subclass $info->{subclass} at ${self}/$info->{path} -";
+ $method = 'SUBCLASS';
+ } else {
+ $errprefix = "register method ${self}/$info->{path} -";
+ $info->{method} = 'GET' if !$info->{method};
+ $method = $info->{method};
+ }
+
+ $method_path_lookup->{$self} = {} if !defined($method_path_lookup->{$self});
+ my $path_lookup = $method_path_lookup->{$self};
+
+ die "$errprefix no path" if !defined($info->{path});
+
foreach my $comp (split(/\/+/, $info->{path})) {
- die "path compoment has zero length" if $comp eq '';
+ die "$errprefix path compoment has zero length\n" if $comp eq '';
+ my ($name, $regex);
if ($comp =~ m/^\{(\w+)(:(.*))?\}$/) {
- my $name = $1;
- push @$match_re, $3 ? $3 : '\S+';
- push @$match_name, $1;
+ $name = $1;
+ $regex = $3 ? $3 : '\S+';
+ push @$match_re, $regex;
+ push @$match_name, $name;
+ } else {
+ $name = $comp;
+ push @$match_re, $name;
+ push @$match_name, undef;
+ }
+
+ if ($regex) {
+ $path_lookup->{regex} = {} if !defined($path_lookup->{regex});
+
+ my $old_name = $path_lookup->{regex}->{match_name};
+ die "$errprefix found changed regex match name\n"
+ if defined($old_name) && ($old_name ne $name);
+ my $old_re = $path_lookup->{regex}->{match_re};
+ die "$errprefix found changed regex\n"
+ if defined($old_re) && ($old_re ne $regex);
+ $path_lookup->{regex}->{match_name} = $name;
+ $path_lookup->{regex}->{match_re} = $regex;
+
+ die "$errprefix path match error - regex and fixed items\n"
+ if defined($path_lookup->{folders});
+
+ $path_lookup = $path_lookup->{regex};
+
} else {
- push @$match_re, $comp;
- push @$match_name, undef;
+ $path_lookup->{folders}->{$name} = {} if !defined($path_lookup->{folders}->{$name});
+
+ die "$errprefix path match error - regex and fixed items\n"
+ if defined($path_lookup->{regex});
+
+ $path_lookup = $path_lookup->{folders}->{$name};
}
}
+ die "$errprefix duplicate method definition\n"
+ if defined($path_lookup->{$method});
+
+ $path_lookup->{$method} = $info;
+
$info->{match_re} = $match_re;
$info->{match_name} = $match_name;
$method_by_name->{$self} = {} if !defined($method_by_name->{$self});
if ($info->{name}) {
- die "method '${self}::$info->{name}' already defined\n"
+ die "$errprefix method name already defined\n"
if defined($method_by_name->{$self}->{$info->{name}});
$method_by_name->{$self}->{$info->{name}} = $info;
return $info;
}
-sub map_method {
- my ($self, $stack, $method, $uri_param) = @_;
+sub map_path_to_methods {
+ my ($class, $stack, $uri_param) = @_;
- my $ma = $method_registry->{$self};
+ my $path_lookup = $method_path_lookup->{$class};
- my $stacklen = scalar(@$stack);
+ while (defined(my $comp = shift @$stack)) {
+ return undef if !$path_lookup; # not registerd?
+ if ($path_lookup->{regex}) {
+ my $name = $path_lookup->{regex}->{match_name};
+ my $regex = $path_lookup->{regex}->{match_re};
- #syslog ('info', "MAPTEST:$method:$self: " . join ('/', @$stack));
-
- foreach my $info (@$ma) {
- #syslog ('info', "TEST0 " . Dumper($info));
- next if !($info->{subclass} || ($info->{method} eq $method));
- my $regexlen = scalar(@{$info->{match_re}});
- if ($info->{subclass}) {
- next if $stacklen < $regexlen;
+ return undef if $comp !~ m/^($regex)$/;
+ $uri_param->{$name} = $1;
+ $path_lookup = $path_lookup->{regex};
+ } elsif ($path_lookup->{folders}) {
+ $path_lookup = $path_lookup->{folders}->{$comp};
} else {
- next if $stacklen != $regexlen;
- }
-
- #syslog ('info', "TEST1 " . Dumper($info));
-
- my $param = {};
- my $i = 0;
- for (; $i < $regexlen; $i++) {
- my $comp = $stack->[$i];
- my $re = $info->{match_re}->[$i];
- #print "COMPARE $comp $info->{match_re}->[$i]\n";
- my ($match) = $stack->[$i] =~ m/^($re)$/;
- last if !defined($match);
- if (my $name = $info->{match_name}->[$i]) {
- $param->{$name} = $match;
- }
+ die "internal error";
}
+
+ return undef if !$path_lookup;
- next if $i != $regexlen;
-
- #print "MATCH $info->{name}\n";
-
- foreach my $p (keys %$param) {
- $uri_param->{$p} = $param->{$p};
- }
-
- return $info;
- }
-}
-
-sub __find_handler_full {
- my ($class, $method, $stack, $uri_param, $pathmatchref) = @_;
-
- my $info;
- eval {
- $info = $class->map_method($stack, $method, $uri_param);
- };
- syslog('err', $@) if $@;
-
- return undef if !$info;
-
- $$pathmatchref .= '/' . $info->{path};
+ if (my $info = $path_lookup->{SUBCLASS}) {
+ $class = $info->{subclass};
- if (my $subh = $info->{subclass}) {
+ my $fd = $info->{fragmentDelimiter};
- my $matchlen = scalar(@{$info->{match_re}});
+ if (defined($fd)) {
+ # we only support the empty string '' (match whole URI)
+ die "unsupported fragmentDelimiter '$fd'"
+ if $fd ne '';
- for (my $i = 0; $i < $matchlen; $i++) {
- shift @$stack; # pop from stack
+ $stack = [ join ('/', @$stack) ] if scalar(@$stack) > 1;
+ }
+ $path_lookup = $method_path_lookup->{$class};
}
+ }
- my $fd = $info->{fragmentDelimiter};
+ return undef if !$path_lookup;
- if (defined($fd)) {
+ return ($class, $path_lookup);
+}
- # we only support the empty string '' (match whole URI)
- die "unsupported fragmentDelimiter '$fd'"
- if $fd ne '';
+sub find_handler {
+ my ($class, $method, $path, $uri_param) = @_;
- $stack = [ join ('/', @$stack) ] if scalar(@$stack) > 1;
- }
+ my $stack = [ grep { length($_) > 0 } split('\/+' , $path)]; # skip empty fragments
- return $subh->__find_handler_full($method, $stack, $uri_param, $pathmatchref);
- }
+ my ($handler_class, $path_info);
+ eval {
+ ($handler_class, $path_info) = $class->map_path_to_methods($stack, $uri_param);
+ };
+ my $err = $@;
+ syslog('err', $err) if $err;
- return ($class, $info, $$pathmatchref);
-};
+ return undef if !($handler_class && $path_info);
-sub find_handler {
- my ($class, $method, $path, $uri_param) = @_;
+ my $method_info = $path_info->{$method};
- my $stack = [ grep { length($_) > 0 } split('\/+' , $path)]; # skip empty fragments
+ return undef if !$method_info;
- my $pathmatch = '';
- return $class->__find_handler_full($method, $stack, $uri_param, \$pathmatch);
+ return ($handler_class, $method_info);
}
sub handle {