Git Product home page Git Product logo

furl's Introduction

Formatted Url

This library allows to create type-safe formatted urls in the spirit of the format type from the standard library. It is backed by re and uri

This is work in progress, if you have any remarks or proposition, please open an issue. The API will be changed until I'm happy with it. Contributions are welcome. Also, it may still be buggy.

To use this library, you can pin it:

opam pin add furl https://github.com/Drup/furl.git

Quick look

Let's imagine we want to build a REST service indexing Camelidaes.

let camlidae () = Furl.host "www.camlidae.ml"

We can query a Camelidae by name:

let by_name () =
  Furl.(~$camlidae / "name" /% String /? nil)
val by_name : unit -> (string -> 'a, 'a) Furl.furl

Notice how the type of the value describes the contained parameters.

Let's consider the type ('f,'r) Furl.furl. 'f is the type of a function corresponding to the parameters of the url. 'r is the return type, which could be anything at this point.

We can also query a list of camelidae by humps:

let by_humps () =
  Furl.(~$camlidae / "humps" /% Int /? nil)
val by_humps : unit -> (int -> 'a, 'a) Furl.furl

This is nice, but we want to refine the list by humps to only show non extinct camelidaes:

let by_humps () =
  Furl.(~$camlidae / "humps" /% Int /? ("extinct",Opt Bool) ** nil)
val by_humps : unit -> (int -> bool option -> 'a, 'a) Furl.furl

We can now build a handler answering to these endpoints:

let handle_camlidaes =
  Furl.match_url [
    Furl.(route ~$by_name) (fun n ->
      List.filter (fun c -> c.name = n) list_camlidaes
    ) ;
    Furl.(route ~$by_humps) (fun humps -> function
      | None ->
        List.filter (fun c -> c.humps = humps) list_camlidaes
      | Some b ->
        List.filter (fun c -> c.humps = humps && c.extinct = b) list_camlidaes
    );
  ]
    ~default:(fun _uri -> failwith "This is not a camlidae.")
val handle_camlidaes : Uri.t -> camlidae list

You can then give this handler to your favorite web server and Tada, a camelidae web API.

You can also expose the formatted urls for clients:

let query_by_humps = Furl.eval @@ by_humps ()
val query_by_humps : int -> bool option -> Uri.t

Then you can use your favorite http client to get the answer we all want:

fetch_http @@ query_by_hump 2 (Some false) ;;
["Bactrian camel"; "Wild camel"]

See camlidae.ml for the complete working example.

Principles

furl uses GADT in a similar manner than format in the standard library, but specialized for urls and regexps. The matching against multiple uris is done with re.

urls are separated into Furl.url which are not yet finalized and Furl.furl which are finalized. This separation is done to make Furl.url a pure datastructure that can be serialized, moved around (and sent to the client in the context of eliom). Furl.furl contains an url and the list of converters.

furl's People

Contributors

anuragsoni avatar drup avatar j0sh avatar reynir avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

reynir j0sh garrigue

furl's Issues

Include the http verb

It makes sens to bundle the http verb with a formatted url. It allows to expose endpoint to users with their verb, preventing some mistakes.

I'm not sure it should be directly at the base level or something on top.

Figure out atoms properly

Currently, atom has a phantom type to force List to be at top level.

  • Do we really want to force that ? List have a performance downside currently and from the semantic point of view, it's not clear to me at all what it means to have nested lists. A possible semantic would be "Non top level lists are exactly like *". Having a semantic of an element depends if it's at the top or not seems fishy, even if it looks "intuitive".
  • We could separate the repetition specification from the type specification.
    That would mean we remove lists from atom and we use a type component:
type 'a component =
  | Opt : 'a atom -> 'a option component
  | One : 'a atom -> 'a component
  | List : 'a atom -> 'a list component
  | List1 : 'a atom -> ('a * 'a list) component

The good point is that it gives a clearer semantic: only the components have special semantic for path and query, atoms are only regexps.
The meh point is that it adds another type layer. That could be avoided with the syntax extension and maybe mitigated with good choice of constructors (but value restriction ...).

Integrate tighter with uri

The construction of the regular expression is, atm, hacky and incorrect. If uri were to expose a function to query safe chars and to build a regular expressions for uri, we could use it.

Implement a format-like syntax extension.

Using ppx, it should be rather easy to do. Something of the form:

{furl|foo.org/bar/%i*?babar=%f?|furl}

This would allow to keep a readable syntax while avoiding the value restriction, since we would build the value directly (instead of using the combinators).

For matching, something along the lines of

match%furl uri with
  | {|....|} x y z -> body
  | _ -> default

Integers and float regexps

The regexp for float and ints accept arbitrary big numbers and then parses them. If it's too big, it will fail (and not gracefully, at the moment)

I'm not sure what would be a good solution.

  • Using a regexp that matches only 32 bit integers, while possible, is a terrible idea.
  • Using zarith is a bit overkill (most application really don't care). And there is the javascript issue.
  • Wrapping int and floats in an option is really not user friendly.

The only good solution I see is to

  • Return None for extract. It forces the return type of extract to be an option (which should be the case anyway, but the types are not obvious to me just yet)
  • Trigger the default handler for match_url.

Maybe the kind of error encountered should be exposed to the user.

Provide a client and a server sub-library

Three libraries could be provided:

  • A client library using cohttp
  • A client library using javascript
  • A server library using cohttp

All optionals, of course. The core is still independent of everything.

Implement a real decision tree

We encode the decision tree in a regexp at the moment. This is not great for multiple reason (it doesn't work except for urls ...).

It's also a blocker for #1 and #9

Redesign the API

New WIP API:

type 'a ty
val string : string ty
val int : int ty
val list : 'a ty -> 'a list ty
val option : 'a ty -> 'a option ty
val flag : string -> bool ty
val regex : Re.t -> string ty

type 'a key = ..

val meth : meth key
val hash : 'a ty -> 'a key
val header : string -> 'a ty -> 'a key

type 'a t

val return : 'a -> 'a t
(** Doesn't match anything *)

(** Lookup keys *)

val get : 'a key -> ('a -> 'b t) -> 'b t
val record : 'a key -> 'a t
val check : 'a key -> 'a -> unit t

(** Lookup path *)

val s : string -> unit t
val v : 'a ty -> 'a t
val (/) : 'a t -> 'b t -> ('a * 'b) t

(** Lookup queries *)

val query : string -> 'a ty -> 'a t

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.