Commit 5e9ac242 authored by Vladimir Sedach's avatar Vladimir Sedach

Added documentation, changed license to LLGPLv3, exported uri-encode and uri-decode.

parent 261a69a3
This diff is collapsed.
......@@ -8,6 +8,59 @@ the 02 and later drafts of the URI Template standard, it does allow
arbitrary Lisp expressions in template placeholders, which gives
simpler and more powerful templates.
Example use:
Template interpolation:
(let ((foo 1))
=> ""
Template destructuring:
(uri-template-bind (#U{part}/{number})
(list part (parse-integer number) %uri-host))
=> ("widget" 1 "")
Template desctructuring also binds several handy standard components
to anaphoric variables:
(uri-template-bind (#U{uri})
(list %uri-scheme %uri-authority %uri-user %uri-host %uri-port
%uri-path %uri-directory %uri-file %uri-query %uri-fragment
%uri-head %uri-tail))
=> ("http"
uri-templates can be enabled by calling enable-uri-template-syntax, or
by merging or using the uri-template:uri-template readtable via the
named-readtables library (
uri-template works with Parenscript to provide template interpolation
for JavaScript. To enable this functionality, load Parenscript before
compiling uri-template (you might need to re-compile if you have
previously loaded uri-template without first loading
Parenscript). Note that destructuring is currently unsupported for
For more information about uri-template, see the project homepage:
......@@ -18,5 +71,6 @@
The author, Vladimir Sedach, can be contacted at
For license and copyright information (BSD), see the file COPYING
included with the distribution.
The code is licensed under the LLGPL (see the file COPYING included
with the distribution for details), except for the unit tests, which
are in the public domain.
(in-package #:uri-template)
(defvar uri-decode? t)
(defvar uri-decode? t
"Controls whether URI decoding/unescaping is done on the templated value when destructuring.
True by default.")
(defun uri-decode (str)
"Decodes URI encoded/escaped characters in the given string."
(regex-replace-all "%[\\d|a-f|A-F]{2}" str (lambda (match) (string (code-char (parse-integer match :start 1 :radix 16)))) :simple-calls t))
(defmacro weak-register-groups-bind (vars regex str &body body)
......@@ -15,10 +18,25 @@
(defmacro uri-template-bind ((template) uri &body body)
"Binds URI template placeholders (which must be symbols) in given
URI, as well as attempting to bind a set of standard uri
components (anaphoric variables %uri-protocol, %uri-host, etc.) to
their respective parts of the given URI. Body executes only if all URI
template placeholders can be bound."
URI, as well as attempting to bind a set of standard URI components to
their respective parts of the given URI. Body executes only if all
explicitly specified URI template placeholders can be bound.
Given the example URI
The standard URI components look like:
%uri-scheme http
%uri-user user
%uri-port 8080
%uri-path /dir/abc
%uri-directory /dir/
%uri-file abc
%uri-query bar=baz&xyz=1
%uri-fragment hash
%uri-tail /dir/abc?bar=baz&xyz=1#hash"
(let* ((template (cdr template)) ;; template is expected to look like output of #U: '(uri-template &rest args)
(template-vars (mapcar (lambda (x) (car (last (second x)))) (remove-if #'stringp template)))
(uri-var (gensym)))
......@@ -10,10 +10,12 @@
;; destructuring
;; RFC 2396 standard URI components
;; Copying and distribution of this file, with or without
;; modification, are permitted in any medium without royalty provided
;; this notice is preserved. This file is offered as-is, without any
;; warranty.
(in-package #:uri-template.test)
(in-readtable uri-template.test)
......@@ -35,9 +40,13 @@
(list %uri-scheme %uri-authority %uri-path %uri-directory %uri-file %uri-query %uri-fragment))
(assert (equal (uri-template-bind (#U{uri})
(list %uri-scheme %uri-host %uri-path %uri-directory %uri-file %uri-query %uri-fragment %uri-head %uri-tail))
'("http" "" "/abc" "/" "abc" "bar=baz&xyz=1" nil "" "/abc?bar=baz&xyz=1")))
'("http" "" "/abc" "/" "abc" "bar=baz&xyz=1" "hash" "" "/abc?bar=baz&xyz=1#hash")))
(assert (equal (uri-template-bind (#U{uri})
(list %uri-scheme %uri-authority %uri-user %uri-host %uri-port %uri-path %uri-directory %uri-file %uri-query %uri-fragment %uri-head %uri-tail))
'("http" "" "user" "" "8080" "/dir/abc" "/dir/" "abc" "bar=baz&xyz=1" "hash" "" "/dir/abc?bar=baz&xyz=1#hash")))
(assert (equal (uri-template-bind (#U{uri})
(list %uri-scheme %uri-host %uri-path %uri-directory %uri-file %uri-query %uri-fragment))
(in-package #:uri-template)
(defvar uri-encode? t)
(defvar uri-encode? t
"Controls whether URI encoding/escaping is done on the templated value.
True by default.")
(defun uri-encode (str)
"URI encodes/escapes the given string."
(regex-replace-all '(:alternation #\Space #\! #\* #\' #\( #\) #\; #\: #\@ #\& #\= #\+ #\$ #\, #\/ #\? #\# #\[ #\])
(lambda (match) (format nil "%~16R" (char-code (elt match 0))))
:simple-calls t))
(defun read-uri-template (stream &optional recursive-p)
"A function suitable for inserting into the readtable so you can
read URI templates from your own dispatch character."
(let ((*readtable* (copy-readtable))
(template-accumulator ())
(string-accumulator ())
......@@ -33,6 +38,11 @@
(if uri-encode? `(encode-u-r-i-component ,x) x))
(defun uri-template (&rest template-args)
"The car of the list that the URI template reader produces. A
function or macro.
This symbol also names the named-readtables readtable that provides
the #U dispatch macro."
(format nil "~{~A~}" template-args))
#+parenscript (parenscript:defpsmacro uri-template (&rest template-args)
......@@ -43,5 +53,6 @@
`(uri-template ,@(read-uri-template stream t)))
(defun enable-uri-template-syntax ()
"Binds the #U dispatch character to read a URI template."
(set-dispatch-macro-character #\# #\U 'uri-template-reader)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment