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.
Related
Context
I have a function that operates on a data structure.
I have written a spec for the data structure this function operates on.
This function returns a reagent component that is rendered in a browser (PhantomJS)
The spec has some optional keys, and depending on whether or not those keys exist in the data when it's passed to the function, the output (component to be rendered in browser) of the aforementioned function will be affected.
I am looking to use clojure.test to compare values of the data structure passed to the function generating the component with values grabbed from the rendered element, so simple unit test or input->output comparison is not what I'm going for here.
Problem
Since calling generate or sample on the spec generator will sometimes include or omit the optional fields, I would like to iterate over a reasonably large set of data generated using sample and test each of the data structures, but I don't know the "right" or idiomatic way to do this.
I have used are in clojure.test before, which is great, but since I'm testing against the rendered component in the browser, and are tests input->output it doesn't seem like the right tool for the job.
Advice on a generally accepted practice here or language/clojure.test feature that would have me doing this in the most idiomatic way would be greatly appreciated.
compare values of the data structure passed to the function generating the component with values grabbed from the rendered element
If possible, I'd use s/fdef with :args, :ret, and :fn args to specify the relationship between your function's input and output, and then check the function. There's an example in the testing section of spec guide.
iterate over a reasonably large set of data generated using sample and test each of the data structures
This is essentially what check does.
As for clojure.test integration, you can check functions as part of a test suite like this:
(deftest foo-test
(is (not (:check-failed (st/summarize-results (st/check `foo))))))
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"?
There's a pattern I haven't figured out for Component yet:
I have some "live" configuration that requires disk IO (a component) on system-start, and has a dependency on a map of static config (.edn), and after this "live" configuration is instantiated, it won't change or side-effect anything anymore.
For ex: I need to set this up once, and it depends on static config:
(buddy.core.backends/jws
{:secret (buddy.core.keys/public-key
path-to-public-key-from-static-config)})
I would then reuse that backend ad-infinitum, (ex: in buddy.auth.middleware/wrap-authentication), and it doesn't change, nor side-effect.
Possible Solutions
I could make a component that stores this backend at system-start. But this gives up generality, because when I want to add similar "live config", it would have to be explicitly written into the component, and that gives up the spirit of generality that I think Component champions (eg Duct says components define side-effects, and create boundaries to access them)
I could pass a generic component a map of keys - [fn & args] and the fn+args get evaluated and stored in the component. But this feels like it offloads computation to my configuration .edn, and is an anti-pattern. For example:
(private-key priv-path-from-static
(slurp :password-path-from-static))
Should I encode the notion of slurping in my .edn config? I don't want to offload computation to a config file...
The backend and keys can be instantiated on a per-need basis within each component that requires them. IMO, that's too much of computing the exact same thing, when I'd rather it be stored in memory once.
I could have an atom component that holds a map of these "live" config objects, but then they get destructively added in, and my code has lost it's declarative nature.
TL;DR
What's the best way to create configuration at system-start, possibly needing dependencies, and then becoming available to other components as a component, while not giving up the generality which components should have?
In an ideal world, I think the component itself should describe what type of configuration data it needs. (This could be done with a protocol extending the component in question.). When the config component is started, it should look at all other components, get the list of config requirements and resolve it somehow (from a file, a database, etc.).
I've not seen such a library, but Aviso's config library comes close, maybe you can adapt it to your needs.
I'm building an application with re-frame and I'm wondering if reagent-form are meant to be used with re-frame or not, as reagent-form brings in its own way of handling state which is different than re-frame.
After experimenting a bit, I'd say it can be used, by completely ignoring the ratom that reagent-forms use for state and just using the callback, as in:
[bind-fields [form-template ...] default-values
(fn [_ _ doc]
(re-frame.core/dispatch [:update-data doc]))]
but to me, it didn't feel right, ignoring that ratom with state. That's why I created a new library called Free-form for creating forms with Reagent and optionally, with Re-frame.
Free-form doesn't store state, it just has a similar callback mechanism and it comes with a layer that makes it plugging it into Re-frame straightforward (but optional). On top of that, it makes no assumptions about the shape of your form, you are in total control of your HTML.
You can, but I think you shouldn't.
One thing you could do is just require twitter bootstrap and do some glueing to use everything in the re-frame way.
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..