Created
July 2, 2014 07:50
-
-
Save jroper/f98de7f01740bd555a74 to your computer and use it in GitHub Desktop.
Simple Play routing DSL with string interpolation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.regex.Pattern | |
import play.core.Routes | |
import play.api.mvc._ | |
object Router extends Routes { | |
def routes = { | |
// Static paths | |
case Route("GET", p"") => controllers.Application.index | |
case Route("GET", p"/items") => controllers.Items.list | |
// A simple parameter | |
case Route("GET", p"/item/$id") => controllers.Items.get(id) | |
case Route("DELETE", p"/item/$id") => controllers.Items.delete(id) | |
// Non string parameters? | |
case Route("GET", p"/item/$id/child/${Id(child)}") => controllers.Items.getChild(id, child: Long) | |
// Regexes | |
case Route("GET", p"/item/$id/part/$part<[A-Z]>") => controllers.Items.getPart(id, part) | |
// multiple path part parameters | |
case Route("GET", p"/assets/$file*") => controllers.Assets.versioned(path = "/public", file: Asset) | |
} | |
// The magic is implemented here... | |
// A path extracting String interpolator | |
implicit class PathContext(sc: StringContext) { | |
val p = { | |
// "parse" the path | |
sc.parts.tail.map { part => | |
if (part.startsWith("*")) { | |
// It's a .* matcher | |
"(.*)" + Pattern.quote(part.drop(1)) | |
} else if (part.startsWith("<") && part.contains(">")) { | |
// It's a regex matcher | |
val splitted = part.split(">", 2) | |
val regex = splitted(0).drop(1) | |
"(" + regex + ")" + Pattern.quote(splitted(1)) | |
} else { | |
// It's an ordinary path part matcher | |
"([^/]*)" + Pattern.quote(part) | |
} | |
}.mkString(Pattern.quote(sc.parts.head), "", "/?").r | |
} | |
} | |
// Extractor for routes | |
object Route { | |
def unapply(rh: RequestHeader) = { | |
if (rh.path.startsWith(prefix) { | |
Some(rh.method -> rh.path.drop(prefix.length)) | |
} else None | |
} | |
} | |
// Extractor for Long ids | |
object Id { | |
def unapply(s: String) = try { | |
Some(s.toLong) | |
} catch { | |
case e: NumberFormatException => None | |
} | |
} | |
// routes boiler plate fluff | |
private var _prefix = "" | |
def prefix = _prefix | |
def setPrefix(prefix: String) = _prefix = prefix | |
def documentation = Nil | |
} |
That routing reminds me of Finagle's. I like that you can pass in params to the actions.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@adriaanm it feel a bit overkill to use meta-programming when you could actually avoid it :-)