mmontone / djula

Common Lisp port of the Django templating language
http://mmontone.github.io/djula/djula
MIT License
152 stars 21 forks source link

Adding default template arguments #33

Closed eudoxia0 closed 8 years ago

eudoxia0 commented 8 years ago

Is there some mechanism for adding a key/value pair to Djula so I don't have to pass it every time I call render-template*? I'm currently writing a wiki where every view has to explicitly pass an argument with the wiki's name, which is rather cumbersome.

mmontone commented 8 years ago

There is nothing like that at the moment, but we could think how to solve that.

In a project of mine I implement a custom {% config my-var %} tag that simply outputs the value of my-var (in my case it fetches it from the app config). If you are interested I can share that code with you if you think it could be useful for your usecase.

Actually, here it is:

(djula::def-tag-compiler config (path)
  (let* ((path (split-sequence:split-sequence #\. (string-downcase (string path))))
         (section (first path))
         (option (second path)))
    (lambda (stream)
      (princ (calendar.config:get-option *config* section option)
             stream))))

Maybe something like that is good for your use case.

mmontone commented 8 years ago

Ah, maybe you could also put your variable in djula::template-arguments

Untested:

(setf (getf djula::*template-arguments :wiki) "my-wiki")

Djula variables are extracted from that dynamic variable

eudoxia0 commented 8 years ago

Thanks for the prompt reply. I think the second one can work.

eudoxia0 commented 8 years ago

Hmm, there is one thing though. The wiki name can be set at startup time, but the user object needs to be extracted dynamically on each request.

I think I should use the tag approach.

mmontone commented 8 years ago

I have some utils for extracting variables in tags implementations:

(defun resolve-template-var (var)
  (if (symbolp var)
      (djula::resolve-variable-phrase
       (djula::parse-variable-phrase (string var)))
      var))

(defmacro with-template-vars (vars &body body)
  "Macro for writing Djula template tags"
  `(let ,(loop for var in vars
            collect `(,var (resolve-template-var ,var)))
     ,@body))

Example usage:

(djula::def-tag-compiler :object-link (object) (lambda (stream)
  (with-template-vars (object) (render-object-link object stream))))
eudoxia0 commented 8 years ago

I'm not sure I understand the code. Are you adding the variable to the template arguments within the scope of that tag?

eudoxia0 commented 8 years ago

I got it working, the code is along these lines:

(djula:def-tag-compiler wiki-get-user ()
  (lambda (stream)
    (declare (ignore stream))
    (setf (getf djula::*template-arguments* :user)
          (lucerne-auth:get-userid))))
vindarel commented 3 years ago

I tried

(setf (getf djula::*template-arguments* :wiki) "my-wiki")

then are we supposed to access {{ wiki }} in the templates? Nothing shows up for me. Thanks.

mmontone commented 3 years ago

I'm not sure you can globally bind template arguments like that. I'd have to revise the code to tell you exactly why.

mmontone commented 3 years ago

Without having looked at code, this works:

(with-output-to-string (s)
           (let ((djula::*template-arguments* (list :wiki "lalal")))
           (funcall (djula::compile-string "This is the wiki: {{wiki}}")
                    s)))

So I guess you could wrap djula:render-template in your own my-render-template function, like this:

(defun my-render-template (&rest args)
    (let ((djula::*template-arguments* (list :wiki "my-wiki")))
         (apply #'djula:render-template* args)))

Also, better, without messing with the internals:

(defun my-render-template (template stream &rest args)
   (apply #'djula:render-template* template stream (list* :wiki "my-wiki" args)))
mmontone commented 3 years ago

Let me know if that works.

vindarel commented 3 years ago

This works very nicely, thank you. And it isn't even a macro.

Don't you think there could be a *default-template-arguments* list that would be used by render-template*? It would answer this issue's use case, which I also faced, with a Djula built-in. Or maybe it's a bad idea.

mmontone commented 3 years ago

Don't you think there could be a default-template-arguments list that would be used by render-template*? It would answer this issue's use case, which I also faced, with a Djula built-in. Or maybe it's a bad idea.

Could be a good idea, but after coding it properly, and adding tests. But: I remember having template-arguments unbound helped me to avoid confusion when implementing Djula (found unbound variable errors better than having nil, as when I got nil I didn't know if it was because I forgot to bind, or because of something else ).

mmontone commented 3 years ago

Now that I think about it, having a default-template-arguments would be good idea, yes, and do (let ((template-arguments default-templat-arguments)) ..)

I like the idea. I'll have a look later today perhaps.

mmontone commented 3 years ago

@vindarel I've just pushed an implementation of default template arguments

vindarel commented 3 years ago

Wo-ah. https://github.com/mmontone/djula/commit/1880a08ca819e0ff81b39815f11b1c084b9ef065 +1. Well done and thanks!

Will this variable appear on the documentation?

I want to write a blog post to advertise this and show Djula's activity.

mmontone commented 3 years ago

Didn't add to docs. But I should. You are welcome to do it if you want. Otherwise, I'll do that later. (I'm lazy to update docs because there's some git branch involvements? Haven't updated docs for quite long now.)

vindarel commented 3 years ago

there's some git branch involvements? Haven't updated docs for quite long now.

We add the doc in doc/source on master, but we must build it and push it into gh-pages. You did it this April :)