Add template context processors ability. #180

Closed
wants to merge 2 commits into
from

Conversation

Projects
None yet
4 participants
Contributor

fdevibe commented Jan 15, 2013

This allows for arbitrary modules and funs to be called with given sets of arguments before rendering templates, where the funs return two-tuples of template variables and their values (e.g. {season, "winter"}). These variables will then be valid for all templates.
To enable, add

  {template_context_processors, L}

to boss.config, where L is a list of three-tuples, {Module, Fun, Arity} describing the context processor funs to be called. Arity can be either 0, 2 (in which case the fun will be called with Request and SessionID arguments) or 5 (in which case also Lang, AppInfo and Variables) will be added to the call.

Example:

In src/lib/user_lib.erl, define the fun

request_in_templates(Req, SessionID) ->
    {request, Req}.

Then, add

{template_context_processors, [{user_lib, request_in_templates, 2}]}

to boss.config. Now the request object will be available to all templates, in the request variable.

Fredrik de Vibe
Add template context processors ability.
This allows for arbitrary modules and funs to be called with
given sets of arguments before rendering templates, where the
funs return two-tuples of template variables and their values
(e.g. {season, "winter"}). These variables will then be valid
for all templates.
To enable, add
  {template_context_processors, L}
to boss.config, where L is a list of three-tuples,
{Module, Fun, Arity} of the context processor funs to be
called. Arity can be either 2 (in which case the fun will be
called with Request and SessionID arguments) or 5 (in which
case also Lang, AppInfo and Variables) will be added to the
call.
Contributor

evanmiller commented Jan 16, 2013

Hi. I like the idea, but I don't like the extra config option. Can we make a controller callback for this, similar to before_ and friends? Maybe "context_" or something. That way it will already have access to Request and SessionID. I'm open to ideas.

Contributor

fdevibe commented Jan 16, 2013

Having the request object in templates is only one alternative use of this mechanism. The mechanism is intended to be similar to Django's (https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TEMPLATE_CONTEXT_PROCESSORS), and as such a way of providing globally available template variables. If it is implemented as a controller callback, one would need to duplicate that functionality across controllers to achieve the same result (at least as far as I understand controller callbacks). There certainly are pros as well as cons, it would enable more fine-grained control, but it would lead to duplication in the Django use-case. It seems to me to be a slightly different mechanism, maybe both could be useful?

Regarding the config option, could you elaborate a bit on what you disapprove of? I agree there is added complexity by adding a new config option, and I also agree that the format is rather convoluted. I added the funs' arity as I don't see all use-cases, and for all I know, other variables might come in handy at some point. That might be a poor excuse for a feature, so would it be better if we used two-tuples with only module and fun, always expecting an arity of 2, namely request and session ID (Django's only get the request object, but then again, those request objects aren't identical to CB's)? I.e. tuples like

{user_lib, request_in_templates}

As an aside, the "correct" external fun specification in erlang is (to the best of my knowledge) fun module:name/arity, but I couldn't figure out how to use that in this case due to the way the config files are parsed (using file:consult).

My initial take on the problem (disregarding #173) was to put all template context processors in one directory and call a fun of a given pattern in all of the modules present there. Given my inexperience with both erlang and CB, that solution is somewhat harder for me to implement, so after some thought (and the attempt in #173), I ended up with this proposition. I personally kind of prefer the proposed solution (or a variation on it), though, as it is more explicit (although my familiarity with Django might give me a slight bias here).

Contributor

evanmiller commented Jan 16, 2013

Thanks for the elaboration. My thinking:

  1. I mainly prefer the controller callback approach because 1. it offers fine-grained control and 2. we aren't creating any problems that we didn't have before. I.e. people already want before_ filters applied to all controllers. I'm sure we'll figure out a solution to that, possibly related to -extends (look it up, it lets you get function "inheritance" from a base module). Anyway we'll work something out and I'd rather use an established pattern with known limitations than introduce a new one.
  2. The sin here is putting application logic into a config file. A config file should be for deployment configuration, like ports and passwords and directories and stuff. Maybe module names, but with very few exceptions individual function names don't belong in configuration.

If you just want the request object in templates, I'd be open to stuffing _req into the template variable list. Or even better, a serialized version of the request object that doesn't permit side effects. (And a config option is unnecessary, as CB has effectively declared all variables beginning with underscore to be reserved.)

Contributor

fdevibe commented Jan 16, 2013

Thanks for clarifying. I like the -extends solution, I noticed the duplication caused by specifying the same before_ filters across controllers. That would offer both fine-grained control as well as remove the duplication problem.

An alternative to specifying fun names could be to expect a pre-defined function name in specific template context processor modules, leading to something like e.g.

{template_context_processors, [request_in_templates, day_of_week, ...]}

and in those modules:

template_insert_(Req, SessionID) ->
    {variable_name, some_value}.

or thereabouts. I think I prefer the controller callback approach though, given that the duplication problem goes away.

Regarding simply adding the request object, I'm not so sure. That would solve my current use-case, but I kind of feel that it's a nice thing that the request object isn't present by default (due to separation of concerns), and I guess there's a reason why the Django devs have chosen not to add it (although there's a bundled context processor that does exactly that, which is simple to include in the Django config). The lack of generality was also my main issue with #173, and the reason I went for this solution instead, I believe having the option of extending the set of globally (or a subset thereof) available template variables is both powerful and useful in developing larger applications.

Contributor

evanmiller commented Jan 17, 2013

Ok, let's go with a controller callback design. I feel like people have asked for this functionality in some form before (though I can't find references at the moment). Why don't you propose an API and I'll tell you what I think? :D

Might be worth taking this discussion to the mailing list since others are likely to have an opinion.

@ghost ghost assigned zkessin Jan 7, 2014

Contributor

danikp commented Jan 7, 2014

Could you be kind to update your pull request to latest code base and according to coding standards
https://github.com/ChicagoBoss/ChicagoBoss/blob/master/CODING_STANDARDS.md
?

Contributor

fdevibe commented Jan 7, 2014

I'll have to look up the old mailing list posts and get back into the code. I'll get it done as soon as possible (though that might not be instantaneous :-)).

Contributor

zkessin commented Jan 23, 2014

Can you update this PR to be able to be merged into the current code base and standards?

@zkessin zkessin closed this Jan 23, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment