Reputation: 13
I have a scenario where the route contains alternating segments of key/value pairs. The number of pairs is undefined, what is the effective way to collect those pairs. For example, the route may look like
/key1/value1/key2/value2/key3/value3
and I would like get a linkedmap with
(key1, value1) -> (key2, value2) -> (key3, value3)
I know that we can get a list of path segments and convert them as above. But what I am looking for is something that navigates the path down like
class EntitiesSpec extends WordSpec with Matchers with ScalatestRouteTest with Directives {
def lastSegment:Directive1[String] = path(Segment)
def intermediate: Directive1[String] = <processing the intermediate>
val navigate = (intermediate & navigate) ~ lastSegment
val route = navigate { (param1:String) =>
complete(param1)
}
"this should call the route" should {
"invoke a route /hello" in {
Get("/hello/world/hello1/world1/hello3/world3") ~> route ~> check {
println(responseAs[String])
}
}
}
}
Another good one would be, for example, if you have a path like
/12/1/5/6/8
How to define a directive that can add the numbers recursively? Any help is appreciated.
Upvotes: 1
Views: 99
Reputation: 17933
Don't Use Path for Key/Value Pairs
The methodology described in the question is an anti-pattern. Key/value pairing can be accomplished much easier in the query string. By using the parameterMap
Directive it becomes trivial to convert the key/value pairs into a list of String
tuples:
//matches a uri of the form: /?key1=value1&key2=value2
val queryStringRoute =
parameterMap { params =>
val linkedPairList : List[(String, String)] = params.toList
}
Don't Use Recursion on Directives
A Recursive Directive is also ill-advised. A Route
is declared as a simple Function1
:
type Route = (RequestContext) ⇒ Future[RouteResult]
Because the return type is a Future
then any recursion would have to be an operation on the original Future. Therefore, the original calling stack cannot be ignored and the recursion will never be tail optimized. With a long enough Path it is possible to cause a stackoverflow of the recursive function.
If You Have to Use the Path
When you have no other choice but to use the Path
then you can just do some String
parsing:
val pathToKeyValuePairs : Uri.Path => List[(String, String)] =
(_ : Uri.Path)
.toString
.split('/')
.grouped(2)
.map(arr => arr(0) -> arr(1))
.toList
This parser can now be used inside of a Route
:
val unadvisedRoute =
extractRequest { request =>
val linkedPairList : List[(String, String)] =
pathToKeyValuePairs(request.uri.path)
}
Upvotes: 2