Skip to content

Commit

Permalink
update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
anuragsoni committed Jan 20, 2020
1 parent f9f39dc commit d3bef18
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 8 deletions.
1 change: 1 addition & 0 deletions doc/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(documentation)
83 changes: 83 additions & 0 deletions doc/index.mld
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{0 Routes}

{1 Introduction}

Routes is a routing library for OCaml that allows defining type safe routes, to dispatch a request to a
matching handler, based on path parameters in the input URI target.
Type safe in this context, refers to processing the input URI in a manner that assigns
concrete types to the values extracted from the path parameters.

The library has no external dependencies aside from the OCaml standard library, and it
can be used in both native (via ocamlopt) and javascript usecases (via js_of_ocaml).
It isn't tied to any particular framework, with the intention for frameworks to provide
a higher level wrapper around it.

{2 Installation}

Routes is published on the opam repository. If using opam, install it via

stable version:
{[ opam install routes ]}

development version:
{[ opam pin add routes.dev git+https://github.com/anuragsoni/routes.git ]}

If using esy, add the dependency [@opam/routes] to [package.json/esy.json].
Or you can use [esy add @opam/routes] to add it to the manifest file automatically.

{2 Usage }

{[

let greet_user (name : string) (id : int) =
Printf.sprintf "Hello, %s [%d]" name id

let add_user (name : string) (id : int) (is_admin : bool) =
Printf.sprintf "Added user %s with id %d. IsAdmin? %b" name id is_admin

let greet_user_route () = Routes.(s "user" / str / int /? nil)
let add_user_route () = Routes.(s "user" / str / int / bool / s "add" /? nil)

let router = Routes.one_of [ Some `GET, greet_user_route () @--> greet_user
; Some `POST, add_user_route () @--> add_user ]
]}


Routes ships with patterns that match the following types: int, int32, int64, bool, string,
but it is possible to define custom patterns that can be used
to extract path parameters that can be parsed into a user defined type.

{[
type shape =
| Square
| Circle

let shape_of_string = function
| "square" -> Some Square
| "circle" -> Some Circle
| _ -> None

let shape_to_string = function
| Square -> "square"
| Circle -> "circle"

let shape = Routes.pattern shape_to_string shape_of_string

(* Now the shape pattern can be used just like any
of the built in patterns like int, bool etc *)
let route () = s "shape" / shape / s "create" /? nil
]}

{1 Support}

Routes' git repository is located on {{: https://github.com/anuragsoni/routes} Github}. Use the repository's {{: https://github.com/anuragsoni/routes/issues} issue tracker} to file bug reports and feature requests.

{1 License }

Routes is distributed under the BSD-3-clause license.

{1 API documentation}
{!modules:
Routes
}

107 changes: 99 additions & 8 deletions src/routes.mli
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,116 @@ module Method : sig
end

type ('a, 'b) path
(** [path] represents a sequence of path parameter patterns that are expected in a route. *)

type 'b route
type 'b router
(** [route] is a combination of a path sequence, with a function that will be
called on a successful match. When a path sequence matches, the patterns
that are extracted are forwarded to said function with the types that the user
defined.
val pattern
: ('c -> string)
-> (string -> 'c option)
-> ('a, 'b) path
-> ('c -> 'a, 'b) path
Example:
{[
let route () = Routes.(s "foo" / str / int /? nil @-->
(fun (a : string) (b : int) ->
Printf.sprintf "%s %d" a b))
]}
*)

type 'b router
(** [router] is a collection of multiple routes. It transforms a list of routes
into a trie like structure, that is then used for matching an input target url.
It works for routes that are grouped by an HTTP verb and for standalone routes
that have no HTTP verb attached to it. *)

val int : ('a, 'b) path -> (int -> 'a, 'b) path
(** [int] matches a path segment if it can be successfully coerced into an integer. *)

val int32 : ('a, 'b) path -> (int32 -> 'a, 'b) path
(** [int32] matches a path segment if it can be successfully coerced into a 32 bit integer. *)

val int64 : ('a, 'b) path -> (int64 -> 'a, 'b) path
(** [int64] matches a path segment if it can be successfully coerced into a 64 bit integer. *)

val str : ('a, 'b) path -> (string -> 'a, 'b) path
(** [str] matches any path segment and forwards it as a string. *)

val bool : ('a, 'b) path -> (bool -> 'a, 'b) path
(** [bool] matches a path segment if it can be successfully coerced into a boolean. *)

val s : string -> ('a, 'b) path -> ('a, 'b) path
(** [s word] matches a path segment if it exactly matches [word]. The matched path param is then discarded. *)

val nil : ('a, 'a) path

val pattern
: ('c -> string)
-> (string -> 'c option)
-> ('a, 'b) path
-> ('c -> 'a, 'b) path
(** [pattern] accepts two functions, one for converting a user provided type to
a string representation, and another to potentially convert a string to the said type.
With these two functions, it creates a pattern that can be used for matching a path segment.
This is useful when there is a need for types that aren't provided out of the box
by the library.
Example:
{[
type shape =
| Square
| Circle
let shape_of_string = function
| "square" -> Some Square
| "circle" -> Some Circle
| _ -> None
let shape_to_string = function
| Square -> "square"
| Circle -> "circle"
let shape = Routes.pattern shape_to_string shape_of_string
(* Now the shape pattern can be used just like any
of the built in patterns like int, bool etc *)
let route () = s "shape" / shape / s "create" /? nil
]}
*)

val ( / ) : (('a, 'b) path -> 'c) -> ('d -> ('a, 'b) path) -> 'd -> 'c
(** [l / r] joins two path match patterns [l] and [r] into a pattern sequence, parse l followed by parse r.
Example: If we want to define a route that matches a string followd by
a constant "foo" and then an integer, we'd use the [/] operator like below:
{[
let route () = Routes.(str / s "foo" / int /? nil)
]} *)

val ( /? ) : (('a, 'b) path -> 'c) -> ('a, 'b) path -> 'c
(** [l /? r] is used to express the sequence of, parse l followed by parse r and then stop parsing.
This is used at the end of the route pattern to define how a route should end. The right hand parameter
[r] should be a pattern definition that cannot be used in further chains joined by [/] (One such operator is [nil]). *)

val ( @--> ) : (unit -> ('a, 'b) path) -> 'a -> 'b route
(** [r @--> h] is used to connect a route pattern [r] to a function [h] that gets called
if this pattern is successfully matched.*)

val one_of : (Method.t option * 'b route) list -> 'b router
(** [one_of] accepts a list of tuples comprised of an optional HTTP verb and a route definition
of type ['b route] where 'b is the type that a successful route match will return.
It transforms the input list of routes into a trie like structure that can later be used
to perform route matches. *)

val match' : ?meth:Method.t -> 'a router -> target:string -> 'a option
(** [match'] accepts an optional HTTP verb, a router and the target url to match.
if the HTTP verb is provided, it tries to look for a matching route that was defined
with the specific HTTP verb provided as input. Otherwise it looks for a route
that is not associated to any HTTP verb.
*)

val sprintf : (unit -> ('a, string) path) -> 'a
val nil : ('a, 'a) path
val one_of : (Method.t option * 'b route) list -> 'b router
(** [sprintf] takes a route pattern as an input, and returns a string with the result of formatting the pattern into a URI path. *)

0 comments on commit d3bef18

Please sign in to comment.