July 2019

Trail User Manual

Tim Nieradzik

sparse.tech

Abstract: Routing library for the Scala platform

Introduction

Trail is a routing library for Scala. It allows to define type-safe routes, generate URLs and perform pattern matching.

Installation

Add the following dependencies to your build configuration:

libraryDependencies += "tech.sparse" %%  "trail" % "0.2.1"  // Scala
libraryDependencies += "tech.sparse" %%% "trail" % "0.2.1"  // Scala.js, Scala Native

Usage

Create a route:

import trail._
import shapeless._

val details = Root / "details" / Arg[Int]
println(details)
Output:
ConcatRight(ConcatRight(Root,Static(details)),Dynamic(Arg()))

Create a string URL by filling the placeholders of the route:

println(details.url(1))  // Shorter: details(1)
Output:
/details/1

Parse an URL:

println(details.parse("/details/42"))
Output:
Some(42)

Define and parse a route with a query parameter:

val route = Root / "details" & Param[Boolean]("show")
println(route.parse("/details?show=false"))
Output:
Some(false)

A query parameter may be optional:

val routeParamsOpt = Root / "details" & Param[Int]("id") & Param[Option[Boolean]]("show")
println(routeParamsOpt.parse("/details?id=42"))
Output:
Some((42,None))

A fragment can be specified:

val routeFragment = Root $ Fragment[Int]
println(routeFragment.parse("/#42"))
Output:
Some(42)

Create a routing table:

val userInfo = Root / "user" / Arg[String] & Param[Boolean]("show")

val result = "/user/hello?show=false" match {
  case details (a)      => s"details: $a"
  case userInfo((u, s)) => s"user: $u, show: $s"
}
println(result)
Output:
user: hello, show: false

Note that you can populate the trail.Path() data structure yourself and use it in place of its string counterpart. This is useful if you are using a third-party routing library which already provides you with a deconstructed URL:

val result2 = trail.Path("/user/hello", List("show" -> "false")) match {
  case details (a)      => s"details: $a"
  case userInfo((u, s)) => s"user: $u, show: $s"
}
println(result2)
Output:
user: hello, show: false

Define a custom codec that can be used in arguments, parameters and fragments:

import scala.util.Try
implicit case object IntSetCodec extends Codec[Set[Int]] {
  override def encode(s: Set[Int]): Option[String] = Some(s.mkString(","))
  override def decode(s: Option[String]): Option[Set[Int]] =
    s.flatMap(value =>
      if (value.isEmpty) Some(Set())
      else Try(value.split(',').map(_.toInt).toSet).toOption)
}

val export = Root / "export" / Arg[Set[Int]]
println(export.url(Set(1, 2, 3)))
Output:
/export/1,2,3

Define a custom path element type:

case class Foo(bar: String)
implicit object FooElement extends StaticElement[Foo](_.bar)

println((Root / Foo("asdf")).url(()))
Output:
/asdf

Encode and decode URI values:

val encoded = URI.encode("äöü")
println(encoded)
println(URI.decode(encoded))
Output:
%C3%A4%C3%B6%C3%BC
äöü

Edit chapter ⤴

Development

Manual

Run the following command to generate the listings for the manual:

$ sbt manual/run

The manual was generated using Instructor. Follow its installation instructions, then run the following command:

instructor manual.toml
Edit chapter ⤴

Support

Bugs/feature requests

Please use our issue tracker to file bugs and feature requests.

Discussions

For discussions and general questions, please join our Gitter channel instead:

Edit chapter ⤴

Generated with Instructor v0.1-SNAPSHOT