Racket webserver/templates include-template нельзя использовать с переменной
Я пишу небольшой блог, используя веб-сервер Racket (требующий web-server/templates, web-server/servlet-env, web-server/servlet, web-server/dispatch
). Всякий раз, когда я хочу сделать шаблон, я делаю что-то такое:
(define (render-homeworks-overview-page)
(let
([dates
(sort
(get-all-homework-dates)
#:key my-date->string
string<?)])
(include-template "templates/homework-overview.html")))
Определение небольшой процедуры, чтобы обеспечить шаблон со всеми необходимыми значениями, в этом случае dates
, который затем используется внутри шаблона. Пока это хорошо работает, но я подумал, что, возможно, я смогу избавиться от let
во всех этих процедурах рендеринга, поместив его один раз в более абстрактный render-template
процедура, которая затем вызывается всеми процедурами рендеринга. В качестве альтернативы, вызовы этой более абстрактной процедуры могут стать настолько простыми, что мне больше не нужны все маленькие процедуры рендеринга. Я хочу предоставить значения в качестве аргументов ключевых слов, и до сих пор я получил следующий код:
(define render-template
(make-keyword-procedure
(lambda
(keywords keyword-args [content "<p>no content!</p>"] [template-path "template/base.html"])
(let
([content content])
(include-template template-path)))))
Это будет иметь значение по умолчанию для содержимого, отображаемого в шаблоне, и путь по умолчанию для шаблона, который будет отображаться и принимать произвольные аргументы ключевого слова, чтобы любая процедура рендеринга могла предоставить все, что требуется шаблону, указав его в качестве ключевого слова.
Однако я не могу запустить этот код, потому что есть ошибка:
include-at/relative-to/reader: not a pathname string, `file' form, or `lib' form for file
template-path
в вызове (include-template template-path)
подчеркнут красным, чтобы указать, что ошибка есть. Однако, когда я заменяю template-path
с обычной строкой примерно так:
(define render-template
(make-keyword-procedure
(lambda
(keywords keyword-args [content "<p>no content!</p>"] [template-path "template/base.html"])
(let
([content content])
(include-template "templates/base.html")))))
Ошибка не возникает. Кажется, что Racket как-то хочет убедиться, что существует правильный путь, данный include-template
, Но я хочу, чтобы это было значением данной процедуры. В противном случае я не могу написать процедуру, выполняющую эту работу.
Также я хочу, чтобы значения ключевых слов, представленных в процедуре, были видны шаблону. Я не уверен, если это происходит автоматически, или если мне нужно поставить let
какой-то вокруг include-template
вызов, потому что я не мог заставить код для запуска еще, чтобы проверить это.
Как я могу написать такую процедуру?
В качестве примера идеальной процедуры я хотел бы иметь:
- Jinja2-х
render_template
Я могу предоставить любой аргумент ключевого слова, который я хочу, и сделать любой шаблон, который я хочу отобразить. Я тоже не очень понимаю, почему в том числе что-то вроде "rm -rf /"
может повредить что угодно. Мне кажется, веб-сервер должен просто проверить, существует ли файл с таким именем. Очевидно, что он не будет существовать, поэтому выведите ошибку. Как это может привести к нежелательному ущербу? Следовательно, я не понимаю причин, ограничивающих то, что можно использовать в качестве пути к шаблону, к строкам (за исключением обходных путей). Тем не менее, это может быть слишком много для одного SO вопроса и, возможно, следует поставить другой вопрос о "почему" вещей.
1 ответ
Если вы хотите подать заявку include-template
с переменным аргументом пути вы можете определить процедуру рендеринга как:
(define (dynamic-include-template path)
(eval #`(include-template #,path)))
Процедура принимает любой путь шаблона в качестве аргумента и включает этот шаблон. Например, (dynamic-include-template "static.html")
будет оказывать static.html
,
Это может быть расширено, чтобы принять любое количество ключевых слов и сделать их доступными в отображаемом шаблоне, следующим образом:
(define render-template
(make-keyword-procedure
(lambda (kws kw-args
[path "templates/base.html"]
[content "<p>no content!</p>"])
(for ([i kws]
[j kw-args])
(namespace-set-variable-value!
(string->symbol (keyword->string i)) j))
(namespace-set-variable-value! 'content content)
(dynamic-include-template path))))
Здесь, внутри for
блок, значения ключевых слов с новыми идентификаторами задаются в среде верхнего уровня пространства имен с использованием namespace-set-variable-value!, такой что для ключевого слова и значения параметра вроде (render-template ... #:foo 'bar)
соответствующий идентификатор, который сделан доступным для шаблона, становится foo
(его @ Syntax
являющийся @foo
), и его значение становится bar
,
Например, чтобы отобразить шаблон обзора домашней работы, вы можете сделать:
(render-template "templates/homework-overview.html"
#:dates (sort (get-all-homework-dates) string<?))
затем внутри templates/homework-overview.html
вам придется:
...
@dates
...
Однако будьте осторожны при использовании eval
и рассмотрите следующее для соответствующих прочтений: