I am writing a little blog using the Racket webserver (requiring web-server/templates, web-server/servlet-env, web-server/servlet, web-server/dispatch). Whenever I want to render a template, I do something this:
(define (render-homeworks-overview-page)
(let
([dates
(sort
(get-all-homework-dates)
#:key my-date->string
string<?)])
(include-template "templates/homework-overview.html")))
Defining a little procedure, to provide the template with all the necessary values, in this case dates, which is then used inside the template. This works well so far, but I thought maybe I could get rid of the let in all those render procedures, by putting it once into a more abstract render-template procedure, which is then called by all the render procedures. Alternatively, the calls to this more abstract procedure could become so simple, that I don't need all the little render procedures anymore. I want to supply the values as keyword arguments and so far I got the following code:
(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)))))
This would have a default value for the content displayed in the template and a default path for the template to render and take arbitrary keyword arguments, so that any render procedure could supply whatever is needed by a template by giving it as a keyword.
However, I cannot run this code, because there is an error:
include-at/relative-to/reader: not a pathname string, `file' form, or `lib' form for file
The template-path in the call (include-template template-path) is underlined red, to indicate that the error is there. However when I replace the template-path with an ordinary string like so:
(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")))))
The error does not occur. It seems that Racket somehow wants to ensure, that there is a valid path given to include-template. But I want that to be a value given to the procedure. Otherwise I cannot write a procedure doing this job.
Also I want the values of the keywords provided to the procedure to be visible to the template. I am not sure, if that is automatically the case, or if I need to put a let of some kind around the include-template call, because I could not get the code to run yet, to test this.
How can I write such procedure?
As an example of an ideal procedure I'd like to have:
Jinja2's render_template
I can supply any keyword argument I wish and render any template I wish to render. I also do not really understand, why including something like "rm -rf /" could damage anything. To me it seems the webserver should simply check if a file exists with that name. Obviously it will not exist, so throw an error. How should this ever lead to any unwanted damage? In consequence I do not understand the reasoning behind limiting what can be used as a path to a template to strings (except for workarounds). However, this might be too much for one SO question and should maybe put into another question about the "why" of things.
If you want to apply include-template with a variable path argument, you can define a render procedure as:
(define (dynamic-include-template path)
(eval #`(include-template #,path)))
The procedure takes in any template path as argument and includes that template. For example, (dynamic-include-template "static.html") would render static.html.
This can be extended to accept any number of keywords and make them available within the template being rendered, as follows:
(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))))
Here, inside the for block, keyword values with new identifiers are being set in the top-level environment of namespace using namespace-set-variable-value!, such that for a keyword and value parameter like (render-template ... #:foo 'bar), the corresponding identifier that is made available to the template becomes foo (its # Syntax being #foo), and its value becomes bar.
For example, to render the homework-overview template, you can do:
(render-template "templates/homework-overview.html"
#:dates (sort (get-all-homework-dates) string<?))
then inside templates/homework-overview.html you would have:
...
#dates
...
Take caution, however, when using eval, and consider the following for relevant reads:
[racket] Dynamic Templates?
How do I use templates "dynamically"?
Related
This may actually be a bit of an XY-problem, so I'll try to explain what the goal is first.
I'm building a ClojureScript application which is composed of a set of Reagent components. It provides a user interface where you can dynamically add or remove UI elements. These UI elements (components) have a certain type. For example a Markdown component is-a Text component. Whenever the user is presented with the option to add Text we list all the components that match the type+descendants (in this case Markdown, there could be others).
The way I've coded it up is as follows.
Each component is in its own namespace, this namespace contains a builder function that returns the new component. At the root of the namespace it also calls (derive ::type ::parent)
now in some different namespace we require and enumerate all of these components in a map like:
(ns app.components
(:require
[app.gui.markdown :as markdown]
[app.gui.study-list :as study-list]))
(def all
{markdown/t markdown/builder
study-list/t study-list/builder})
Where the /t refers to the namespace-qualified keyword which was used to define the hierarchy. We use the all map to provide the data for the menu's that face the user (which components can be added, filtered by type).
Now, as you can imagine, this isn't pretty. Since it must now maintain such a (potentially) long list of all the types in the hierarchy manually.
Rather I would do something like (def all (components-of (descendants ::root))) but I'm unsure how to tackle this, since I think it would require finding vars by name (not supported in ClojureScript).
So my question is: how do you maintain a map or list of namespaces + vars (dynamically) in ClojureScript?
You cannot do this dynamically, but as far as I can tell for your problem there isn't much need. ClojureScript macros can reflect back into the compiler - you could easily use the cljs.analyzer.api namespace to figure out which vars you need (through var metadata or whatever) and automatically emit the runtime info map you want. This is in fact very similar to how cljs.test/run-tests works. It uses the compiler to filter out all vars in all namespaces with :test metadata attached and it generates the code to test each one. It's worth examining cljs.test in detail to see how this can be done.
I've got the following problem:
I do understand that the concept of functional programming languages do not allow immutable variables, so classic variable assignment and usage like
var foo = bar();
after that in another function...
var baz = funnything(foo);
is not possible (correct me if it is, I'm still trying to get the whole idea and concepts behind functional programming).
I'm trying to play around a bit with Clojure and come to a point where I stuck:
I want to get a webpage and save the DOM into a variable for later usage.
The program starts, a prompt is spawning and the user can enter a URL which he want to fetch.
After the DOM of the website is fetched he can navigate through the DOM by for example getting the title, or all links or whatever.
The Problem:
Because I can't use a construct like
; More or less pseudo-code
(def page nil)
(get-url [url]
(page (browser/get url)))
(get-title
(println (html/select page [:title])))
I have to use the following construct:
(get-title [url]
(println (html/select (browser/get url) [:title])))
Due to this construct everytime I want to access an element of the site via get-title, get-links or whatever I have to download the whole webpage whenever the user is entering one of those methods on the command prompt.
Do I miss something here? Are there classical assigments in Clojure? Am I doing the complete thing wrong and should study a whole lot more functional programming concepts before getting hands-on and learning-by-doing?
You are misunderstanding assignment in clojure.
(page (browser/get url))
is not equivalent to: page = browser/get(url), it is equivalent to page(browser/get(url)).
I think you don't really need a global variable, just composable functions. Your example could look something like this:
(defn get-title [url]
(let [page (browser/get url)]
(println (html/select page [:title]))))
That and using memoize, like Joost mentioned, if you want to cache the return values of broser/get.
You can easily cache the value of (browser/get url) so you don't have to refetch the page every time you want to examine a property of it. See clojure.core/memoize. But that will only solve part of the problem.
In your particular case, your program requires some sort of global (or session scoped), mutable, concept of "the current page" or at least, "the current url", since that is what you're expecting your user to enter and modify. Clojure provides multiple constructs for managing mutable state.
Since you're only dealing with one page at a time, this is probably best modeled by using atoms. See http://clojure.org/atoms and http://clojuredocs.org/clojure_core/clojure.core/swap!
How do I use a type-safe url with setMessage?
I want to change
...
setMessage [shamlet|<span .warning>Warning! See Help.|]
...
to a message that contains a link.
From what I could gather thus far, it ought to work somehow like this
...
renderer <- getUrlRender
let html = [hamlet|<span .warning>Warning! See #
<a href=#{HelpR}> Help!|]
setMessage $ toHtml $ html renderer
...
but that code just gives me confusing error messages all over the file.
I did read the printed Yesod Book Chapter on Shakespearian Templates, but I found that it is not very explicit on the involved types. For instance what type does [hamlet|...|]| produce? Without URL-Interpolation, ghci reports t -> Markup but with URL-Interpolation inside, I just get errors.
I am further confused by all the type synonyms involved, e.g. [shamlet|...|] delivers something of type Html, while setMessage expects a Html (). I do not know how to look these up easily: Hoogle often finds nothing on the topic, while Google always finds possibly outdated versions (with examples that no longer work) - sure I get to the newest version eventually, but is there a place where I get an easy overview over these? (Can ghci list all synonyms for a type?)
Note that I actually want to produce the message in a purely functional code fragment, which is later on used by a handler. So that is why I would like to separate the URL rendering from where the hamlet is specified. Thanks for any pointer in the right direction!
I think you want to use getUrlRenderParams. Strangely enough, a related discussion came up on IRC today. Hamlet templates take a URL rendering function as their first argument, and that function must take two parameters: a type-safe URL, and a list of query string parameters. getUrlRender returns a function that doesn't take the query string parameters, which is why you need getUrlRenderParams instead.
I'm trying to understand the namespacing model in clojurescript. I understand that javascript doesn't come built in with namespace support, so its been an add on via the google closure library. However, I don't understand how clojurescript deals with these differences. Can someone please explain?
Lets say for example I want to use the google charts api and whenever possible would like to use advanced compilation. What do I use in the closure/build call, how would the ns form look like and what's the best way to refer to the api from the code? I've looked at https://github.com/sritchie/contour but that example doesn't use advanced compilation, and I don't see the options referring to the google api in the project.clj file.
The namespace declaration is the same regardless of whether you are using simple or advanced mode compilation. The difference is that libraries that don't participate in google dependency management via goog.require('') and goog.provide('') need to be explictly referenced via an externs.js file. This file, which you pass to the cljs compiler, provides stubs of all the vars and associated methods. A typical externs.js would look something like:
// contents of externs.js
var externalLibrary = {}
var externalLibrary.method = function() {}
So to reference a google closure aware library your namespace declaration looks like this:
(ns my.namespace
(:require [goog.events.KeyCodes :as key-codes])
And that emits the following javascript:
goog.provide("my.namespace");
goog.require("goog.events.keycodes");
Note that if you want to call your closurescript from another javascript file then you need to tag the method as exported so that the optimizing compiler knows to preserve the name. (e.g. you might have a script tag on a page from where you want to call a javascript function that has been generated from clojurescript)
That looks like this:
(defn ^:export externallyCalled [])
The other thing to be aware of is that you have to treat macros differently in clojurescript, this is because the ability to process them 'on the fly' in the reader is not present in the same way as it is in clojure.
you do that as follows (note that you are obliged to bind the macros with :as in this case)
(ns my.namespace
(:require-macros [my.macros :as my]))
If you are calling code that didn't participate in google's dependency management you access it via the js namespace and the javascript interop...
(. js/nonGoogle (method args)) ; a method access
(. js/nonGoogle -p) ; a property access
all the usual . variants work too..
I have created a record like this:
(defrecord User [user-id email])
:but when I try to access it from another name-space I get the error:
(User. "name" "email")
java.lang.IllegalArgumentException: Unable to resolve classname: User
It works when I do:
(oe.model.modelcore.User. "name" "email")
: I know I will need to import the Java class, but is there any way for clojure to do this automatically when I do:
(use 'oe.model.modelcore :reload)
Technically you must require it (don't necessarily need to use it) so that the file containing the record definition is compiled and the class is created. Then you must import it so that it is available to construct as a Java class. If you create a constructor function in the first ns like
(defn new-user [id email]
(User. id email))
then you will not need to import it in the calling ns.
I wrote this up a while back here:
http://tech.puredanger.com/2010/06/30/using-records-from-a-different-namespace-in-clojure/
It could be really tricky if you have - (dash) in your name space.
As it turns out there were two errors:
– Importing defrecord from another namespace is not just :use. I had to first :require the namespace, then import the defrecord. This was a trivial problem to solve and I figured it out quickly. Only this did not work in my case
– Dashes “-” and Underscores “_” are a nuisance since we are mixing Lisp with Java. While the file system uses underscores Clojure converts things to dashes. Brilliant.
So to fix the second error I use the follow in the ns block
(ns adder.core
(:require building-blocks.activity)
(:import [building_blocks.activity Activity]))
https://cyrax.wordpress.com/2013/07/22/clojure-importrequireuse-defrecord-from-another-namespace/
In your question you are creating a record, then invoking the constructor for the class generated as a side effect. To do so you need to import that class as mentioned in another answer.
However, imho the preferred path (since Clojure 1.4) is to use the constructor functions generated by defrecord (here they'll be named ->User and map->User). These functions allow you to avoid the interop forms and just refer in the constructor functions like any other function. By avoiding interop, this is a more portable less-hosty solution:
(ns some-ns
(:require [oe.model.modelcore :refer (->User)]))
(def user (->User "name" "email"))
You have to import the record like this:
(ns some-ns
(:import [oe.model.modelcore User]))