~pepe/trolley

8cf1afddf3246ade7a6ab1e7c060071ff94c2ca4 — Josef Pospíšil 4 years ago 7f066cc
Path resolver (#2)

* Cleanup code

* Add path resolver
3 files changed, 44 insertions(+), 10 deletions(-)

M README.md
M test/trolley.janet
M trolley.janet
M README.md => README.md +20 -6
@@ 5,8 5,9 @@ action to which it resolves with params as defined in routes.

## Routes

We define routes as a struct with keys which contain path and params definition and
values which contain actions to which the path resolves.
We define routes as a struct with keys which contain path with optional  params
definition and values which contain actions to which the path resolves.

Example: 

```


@@ 36,17 37,30 @@ Id of `core/peg` differs on your machine.

## Router

This function creates routing function from routes definition, which then can be
This function returns routing function from routes definition, which then can be
used to match the path and returns action and table with parameters.

Example:

```
(import trolley)
(def router 
(def match 
  (trolley/router {"/" root "/home" home "/people/:id" people})) 
(router "/people/3")
(match "/people/3")
=> (people @{:id "3"})
```

Creation of the `router` function is the main usage pattern for this library.
## Resolver 

This function returns resolving function from routes definition, which then can
be used to resolve url path for the action and parameters.

Example:

```
(import trolley)
(def path-for 
  (trolley/resolver {"/" root "/home" home "/people/:id" people})) 
(path-for people @{:id "3"})
=> "/people/3"
```

M test/trolley.janet => test/trolley.janet +11 -1
@@ 2,7 2,8 @@
(import ../trolley :as trolley)

(def config "Routes' config for all tests"
  {"/" :root "/home/:id" :home "/real-thing.json" :real-thing})
  {"/" :root "/home/:id" :home "/real-thing.json" :real-thing
    "/involved/:id/example/:example-id/detail/:detail-id" :involved})

(deftest "Compile routes"
  (let [compiled-routes (trolley/compile-routes config)


@@ 40,3 41,12 @@
          (deep= (router "/real-thing.json") [:real-thing @{}]))
    (test "not found"
          (empty? (router "home")))))

(deftest "Path for"
  (let [path-for (trolley/resolver config)]
    (test "root"
          (= (path-for :root) "/"))
    (test "home"
          (= (path-for :home @{:id 3}) "/home/3"))
    (test "involved"
          (= (path-for :involved @{:id 1 :example-id 2 :detail-id 3}) "/involved/1/example/2/detail/3"))))

M trolley.janet => trolley.janet +13 -3
@@ 15,7 15,7 @@
     :main '(some (* :sep 
                     (+ (if :param (group (* (constant :param) :pref :capture-path)))
                        (if :path (group (* (constant :path) :capture-path)))
                        (if -1 (group (* (constant :root) (constant -1))) ))))}))
                        (if -1 (group (* (constant :root) (constant -1)))))))}))

(defn- compile-route
  "Compiles custom grammar for one route"


@@ 25,8 25,8 @@
             :root (tuple '* sep p)
             :path (tuple '* sep p) 
             :param (tuple '* sep  
                      ~(group (* (constant ,(keyword p))
                         (<- (some ,content)))))))
                      ~(group (* (constant ,(keyword p)) 
                                 (<- (some ,content)))))))
      (array/insert 0 '*)
      (array/push -1)
      splice


@@ 61,4 61,14 @@
  (def compiled-routes (compile-routes routes))
  (fn [path] (lookup compiled-routes path)))

(defn resolver 
  "Creates a simple route compiler from routes"
  [routes]
  (def inverted-routes (invert routes))
  (fn [action &opt params] 
    (def template (get inverted-routes action))
    (if params 
      (let [params-grammar (seq [[k v] :pairs params] ~(/ (<- (* ":" ,(string k))) ,(string v)))]
        (first (peg/match ~(% (any (+ ,;params-grammar (<- 1)))) template)))
      template)))