Skip to content

Commit

Permalink
Add support for splats in sinatra-like url generation
Browse files Browse the repository at this point in the history
  • Loading branch information
ornicar committed Aug 20, 2011
1 parent 56a247e commit 2bb7209
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 7 deletions.
5 changes: 4 additions & 1 deletion core/src/main/scala/org/scalatra/UrlGenerator.scala
Expand Up @@ -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))
}
Expand Down
15 changes: 9 additions & 6 deletions core/src/main/scala/org/scalatra/routeMatcher.scala
Expand Up @@ -10,26 +10,29 @@ 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)
extends RouteMatcher with ReversibleRouteMatcher
{
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
}
Expand Down
12 changes: 12 additions & 0 deletions core/src/test/scala/org/scalatra/UrlGeneratorTest.scala
Expand Up @@ -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")
}
}

0 comments on commit 2bb7209

Please sign in to comment.