diff --git a/core/src/main/scala/org/scalatra/UrlGenerator.scala b/core/src/main/scala/org/scalatra/UrlGenerator.scala index 929eb5b56..93d73601d 100644 --- a/core/src/main/scala/org/scalatra/UrlGenerator.scala +++ b/core/src/main/scala/org/scalatra/UrlGenerator.scala @@ -5,13 +5,16 @@ trait UrlGenerator { def generate(route: Route, params: Pair[String, String]*): String = generate(route, params.toMap) + def generate(route: Route, splat: String, moreSplats: String*): String = + generate(route, Map[String, String](), splat +: moreSplats) + def generate( route: Route, params: Map[String, String] = Map(), splats: Iterable[String] = Seq() ): String = route.reversibleMatcher match { - case Some(matcher: ReversibleRouteMatcher) => matcher.reverse(params, splats) + case Some(matcher: ReversibleRouteMatcher) => matcher.reverse(params, splats.toList) case None => throw new Exception("Route \"%s\" is not reversible" format (route)) } diff --git a/core/src/main/scala/org/scalatra/routeMatcher.scala b/core/src/main/scala/org/scalatra/routeMatcher.scala index d4fab033f..901e8d27b 100644 --- a/core/src/main/scala/org/scalatra/routeMatcher.scala +++ b/core/src/main/scala/org/scalatra/routeMatcher.scala @@ -10,7 +10,7 @@ trait RouteMatcher trait ReversibleRouteMatcher { - def reverse(params: Map[String, String], splats: Iterable[String]): String + def reverse(params: Map[String, String], splats: List[String]): String } final class SinatraRouteMatcher(path: String, requestPath: => String) @@ -18,18 +18,21 @@ final class SinatraRouteMatcher(path: String, requestPath: => String) { def apply() = SinatraPathPatternParser(path)(requestPath) - def reverse(params: Map[String, String], splats: Iterable[String]): String = - replaceSplat(replaceNamed(params), splats) + def reverse(params: Map[String, String], splats: List[String]): String = + replaceSplats(replaceNamedParams(params), splats) - private def replaceNamed(params: Map[String, String]) = + private def replaceNamedParams(params: Map[String, String]) = """:[^/?#\.]+""".r replaceAllIn (path, s => params.get(s.toString.tail) match { case Some(value) => value case None => throw new Exception("The url \"%s\" requires param \"%s\"" format (path, s)) }) - private def replaceSplat(slug: String, splats: Iterable[String]): String = - slug + private def replaceSplats(slug: String, splats: List[String]): String = + splats match { + case Nil => slug + case s :: rest => replaceSplats("""\*""".r replaceFirstIn (slug, s), rest) + } override def toString = path } diff --git a/core/src/test/scala/org/scalatra/UrlGeneratorTest.scala b/core/src/test/scala/org/scalatra/UrlGeneratorTest.scala index 374509f81..b75cd4e2d 100644 --- a/core/src/test/scala/org/scalatra/UrlGeneratorTest.scala +++ b/core/src/test/scala/org/scalatra/UrlGeneratorTest.scala @@ -37,4 +37,16 @@ class UrlGeneratorTest extends ScalatraFunSuite { test("Unexpected parameters are just ignored at the moment") { generate(singleNamed, "bar" -> "pepper", "unexpected" -> "surprise") should equal ("/foo/pepper") } + + test("One splat parameter gets replaced") { + generate(singleSplat, "malt") should equal ("/single-splat/malt") + } + + test("Many splat parameters get replaced") { + generate(multipleSplats, "made", "in", "japan") should equal ("/mixing-multiple-splats/made/foo/in/japan") + } + + test("Mix named and splat") { + generate(mixNamedAndSplat, Map("foo" -> "deep"), Seq("purple")) should equal ("/mix-named-and-splat-params/deep/purple") + } }