Re: Extending djula (and a bit of an introduction)

18 views
Skip to first unread message

Bob Hutchison

unread,
Jul 14, 2008, 11:13:16 AM7/14/08
to cl-te...@googlegroups.com, Bob Hutchison

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 :-)

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?

Cheers,
Bob

----
Bob Hutchison -- tumblelog at http://www.recursive.ca/so/
Recursive Design Inc. -- weblog at http://www.recursive.ca/hutch
http://www.recursive.ca/ -- works on http://www.raconteur.info/cms-for-static-content/home/


Nick Allen

unread,
Jul 14, 2008, 1:11:27 PM7/14/08
to cl-te...@googlegroups.com
On Mon, Jul 14, 2008 at 5:13 PM, Bob Hutchison <hutch...@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





Reply all
Reply to author
Forward
0 new messages