On Mon, Jul 14, 2008 at 5:13 PM, Bob Hutchison <hutch-li...@recursive.ca>
wrote:
> Hi,
> Well, I've just found cl-terrace and djula. And I have to say the
> timing couldn't have been better. In fact, the 'just' is so 'just'
> that I've not had time to compile the project(s) yet.
> A little background. I've got a fairly significant Ruby application
> (based on Rails but that doesn't matter) that uses the liquid
> templating language. Liquid is a Ruby based templating language that
> has, I believe, some common ancestor with the Django templating
> language. That doesn't matter so much, what does matter is that they
> are syntactically identical with almost the same set of tags and
> filters.
> For some time now, there have been some very time consuming (in Ruby)
> and tricky things that I've been wanting to move to lisp. The only
> thing preventing me was not having a replacement for liquid. I was
> getting ready to write my own version of liquid in lisp, but now I
> don't have to :-)
Hello, Bob! Welcome aboard :-)
> However, I need to extend djula with a few tags and filters. These
> extensions are either standard in liquid or in extensions that I wrote
> for liquid. I don't know if they are generally useful or not (we can
> worry about this later), but for me to use djula I need to extend djula.
> Is there a way that you suggest to make these extensions that won't be
> interfering with the standard tags and filters? I see that you say you
> can do this but what are you recommending? Or do I just do the obvious?
right... firstly I must apologize for non-elegance of the way you extend
djula, it's not perfect. the most notable deficiency is the high risk of
namespace conflicts since there's currently one flat namespace being shared
between the names of the tags and internal tokens used by the compiler
the names of the functions aren't formally exported yet, so you have to
extend djula from within the djula package or use the double colon (::)
notation
FILTERS
making new filters is done with the macro DEF-FILTER. DEF-FILTER is like
DEFUN except that it takes the name of the filter as a keyword instead of
the symbol naming the function. the "function" created by DEF-FILTER should
take the value of a variable as a string and return the result of applying
the filter (as a string).
note: the incoming string will already be html-escaped if it's supposed to
be. there can also be an &optional second argument if the filter is supposed
to take an argument
so if we want to make a new filter called "reverse-it" that reverses the
value of the variable we would do
(in-package :djula)
(def-filter :reverse-it (x)
(reverse x))
the next step is to document it:
(def-filter-documentation :reverse-it '(x)
"this is the new reverse-it filter"
:from-django-p nil)
doing this makes the filter show up in the document created by
BUILD-HTML-DOCUMENTATION-FROM-SOURCE. this documentation is also available
to someone using the cl-terrace View Server
TEMPLATE-ERRORS
there is a macro WITH-TEMPLATE-ERROR that should be used when possible to
provide nice error messages that show up in the browser when
*CATCH-TEMPLATE-ERRORS-P* is non-NULL (which it is by default). It's like
PROGN except the first form is a recovery form that is run only if there is
an error in one of the other forms and *CATCH-TEMPLATE-ERRORS-P* is non-NULL
use the function TEMPLATE-ERROR-STRING to make your error messages
consistent with the rest of the djula template errors
CL-USER> (in-package :djula)
#<PACKAGE "DJULA">
DJULA> (template-error-string "there was a ~A error" 'foo)
"{# Error: there was a FOO error #}"
DJULA> (with-template-error (template-error-string "there was a ~A error"
'foo)
5)
5
DJULA> (with-template-error (template-error-string "there was a ~A error"
'foo)
(error "stuff"))
"{# Error: there was a FOO error #}"
DJULA> (setf *catch-template-errors-p* nil)
NIL
DJULA> (with-template-error (template-error-string "there was a ~A error"
'foo)
(error "stuff"))
<<<throws an error "stuff">>>
so to be safe the definition of "reverse-it" should probably be something
like:
(def-filter :reverse-it (x)
(with-template-error (template-error-string "There was a problem
reversing ~A" x)
(reverse x)))
TAGS
compared to filters, tags are a complex beast. in the simplest case, their
definitions similar to filter definitions: the macro DEF-TAG-COMPILER is
like DEF-FILTER except that it should return a function that, when called,
returns the output of the tag
(in-package :djula)
(def-tag-compiler :show-language ()
":SHOW-LANGUAGE tags are compiled into a function that just shows the
value of
*LANGUAGE*"
(lambda () *language*))
(def-tag-documentation :show-language '()
"prints the name of the language currently being used by the template"
:from-django-p nil)
if you want to do anything more complex however, you will have to peak into
the documentation on the top of "core.lisp" and "tag.lisp" and maybe check
out the definitions in "tag-definitions.lisp", because it starts to get more
complicated. feel free to ask questions about implementing specific types of
tags
take care and good luck
Nick