From d365b7e335534a2161ce429220f1bc98e973c83b Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Fri, 15 Feb 2013 11:53:40 -0600 Subject: no reason routes themselves should be public --- src/main/scala/router.scala | 206 ++++++++++++++++++++++---------------------- src/test/scala/basic.scala | 6 -- 2 files changed, 103 insertions(+), 109 deletions(-) diff --git a/src/main/scala/router.scala b/src/main/scala/router.scala index 9df7a2a..5779256 100644 --- a/src/main/scala/router.scala +++ b/src/main/scala/router.scala @@ -4,8 +4,6 @@ import scala.collection.mutable.ArrayBuffer import scala.util.matching.Regex class Router[T] { - val routes = new ArrayBuffer[Route[T]]() - def addRoute ( path: String, target: T, @@ -31,7 +29,7 @@ class Router[T] { def route (path: String): Option[Match[T]] = { def testRoutes ( components: Seq[String], - routes: List[Route[T]] + routes: List[Route] ): Option[Match[T]] = routes match { case r :: rs => r.route(components) match { case Some(found) => Some(found) @@ -42,18 +40,6 @@ class Router[T] { testRoutes(path.split("/"), routes.toList) } - private class AmbiguousRouteMapping( - mapping: Map[String, String], - paths: Seq[String] - ) extends RuntimeException { - override def getMessage (): String = { - "Ambiguous path descriptor (specified keys " + - mapping.keys.mkString(", ") + - "): could match paths " + - paths.mkString(", ") - } - } - def uriFor (mapping: Map[String, String]): Option[String] = { // first remove all routes that can't possibly match // - if the route requires a variable component that doesn't exist in the @@ -89,118 +75,132 @@ class Router[T] { } } } -} -class Route[T] ( - val path: String, - val defaults: Map[String, String], - val validations: Map[String, Regex], - val target: T -) { - import Route._ - - lazy val variables = components.flatMap(getVariableName) - - def route( - parts: Seq[String], - components: Seq[String] = components, - mapping: Map[String, String] = defaults - ): Option[Match[T]] = { - if (components.filter(!isOptional(_)).length == 0 && parts.length == 0) { - Some(new Match[T](path, mapping, target)) - } - else if (components.length == 0 || parts.length == 0) { - None - } - else { - components.head match { - case Optional(name) => { - if (validate(name, parts.head)) { - route(parts.tail, components.tail, mapping + (name -> parts.head)) - } - else { - route(parts, components.tail, mapping) + private val routes = new ArrayBuffer[Route]() + + private class Route ( + val path: String, + val defaults: Map[String, String], + val validations: Map[String, Regex], + val target: T + ) { + import Route._ + + lazy val variables = components.flatMap(getVariableName) + + def route( + parts: Seq[String], + components: Seq[String] = components, + mapping: Map[String, String] = defaults + ): Option[Match[T]] = { + if (components.filter(!isOptional(_)).length == 0 && parts.length == 0) { + Some(new Match[T](path, mapping, target)) + } + else if (components.length == 0 || parts.length == 0) { + None + } + else { + components.head match { + case Optional(name) => { + if (validate(name, parts.head)) { + route(parts.tail, components.tail, mapping + (name -> parts.head)) + } + else { + route(parts, components.tail, mapping) + } } - } - case Variable(name) => { - if (validate(name, parts.head)) { - route(parts.tail, components.tail, mapping + (name -> parts.head)) + case Variable(name) => { + if (validate(name, parts.head)) { + route(parts.tail, components.tail, mapping + (name -> parts.head)) + } + else { + None + } } - else { - None + case literal => parts.head match { + case `literal` => route(parts.tail, components.tail, mapping) + case _ => None } } - case literal => parts.head match { - case `literal` => route(parts.tail, components.tail, mapping) - case _ => None - } } } - } - override def toString = path + override def toString = path - def pathWithMapping (mapping: Map[String, String]): Option[String] = { - val requiredDefaults = defaults.keys.filter { k => - mapping.isDefinedAt(k) && !variables.contains(k) - } - if (requiredDefaults.forall(k => defaults(k) == mapping(k)) && - requiredVariables.forall(mapping.isDefinedAt)) { - val boundComponents = components.flatMap { - case Optional(v) => { - val component = (mapping get v).flatMap(validComponentValue(v, _)) - defaults get v match { - case Some(default) => { - component.flatMap { - case `default` => None - case c => Some(c) + def pathWithMapping (mapping: Map[String, String]): Option[String] = { + val requiredDefaults = defaults.keys.filter { k => + mapping.isDefinedAt(k) && !variables.contains(k) + } + if (requiredDefaults.forall(k => defaults(k) == mapping(k)) && + requiredVariables.forall(mapping.isDefinedAt)) { + val boundComponents = components.flatMap { + case Optional(v) => { + val component = (mapping get v).flatMap(validComponentValue(v, _)) + defaults get v match { + case Some(default) => { + component.flatMap { + case `default` => None + case c => Some(c) + } } + case None => component } - case None => component } + case Variable(v) => validComponentValue(v, (mapping(v))) + case literal => Some(literal) } - case Variable(v) => validComponentValue(v, (mapping(v))) - case literal => Some(literal) + Some(boundComponents.mkString("/")) + } + else { + None } - Some(boundComponents.mkString("/")) - } - else { - None } - } - - private lazy val components = - path.split("/").filter(_.length > 0) - private lazy val requiredVariables = - components.filter(!isOptional(_)).flatMap(getVariableName) + private lazy val components = + path.split("/").filter(_.length > 0) - private lazy val hasVariable = variables.toSet + private lazy val requiredVariables = + components.filter(!isOptional(_)).flatMap(getVariableName) - private def isOptional (component: String) = - Optional.findFirstIn(component).nonEmpty + private lazy val hasVariable = variables.toSet - private def isVariable (component: String) = - Variable.findFirstIn(component).nonEmpty + private def isOptional (component: String) = + Optional.findFirstIn(component).nonEmpty - private def getVariableName (component: String) = component match { - case Variable(name) => Some(name) - case _ => None - } + private def isVariable (component: String) = + Variable.findFirstIn(component).nonEmpty - private def validate (name: String, component: String) = - validations get name match { - case Some(rx) => rx.findFirstIn(component).nonEmpty - case None => true + private def getVariableName (component: String) = component match { + case Variable(name) => Some(name) + case _ => None } - private def validComponentValue (name: String, component: String) = - if (validate(name, component)) { Some(component) } else { None } -} + private def validate (name: String, component: String) = + validations get name match { + case Some(rx) => rx.findFirstIn(component).nonEmpty + case None => true + } + + private def validComponentValue (name: String, component: String) = + if (validate(name, component)) { Some(component) } else { None } + } -object Route { - private val Optional = """^\?:(.*)$""".r - private val Variable = """^\??:(.*)$""".r + private object Route { + private val Optional = """^\?:(.*)$""".r + private val Variable = """^\??:(.*)$""".r + } + + private class AmbiguousRouteMapping( + mapping: Map[String, String], + paths: Seq[String] + ) extends RuntimeException { + override def getMessage (): String = { + "Ambiguous path descriptor (specified keys " + + mapping.keys.mkString(", ") + + "): could match paths " + + paths.mkString(", ") + } + } } class Match[T] ( diff --git a/src/test/scala/basic.scala b/src/test/scala/basic.scala index 686a313..71aa370 100644 --- a/src/test/scala/basic.scala +++ b/src/test/scala/basic.scala @@ -65,12 +65,6 @@ class Basic extends FunSuite { assert(m.target === true) } - test ("routes are created in the correct order") { - assert(router.routes(0).path === "blog") - assert(router.routes(2).path === "blog/:action/:id") - assert(router.routes(3).path === "test/?:x/?:y") - } - test ("routes match properly") { testRoute( router, "blog", Map( -- cgit v1.2.3