On Feb 3, 2015, at 9:52 AM, John Berquist <
jcber...@gmail.com> wrote:
> The lein script installs Leiningen to the user's home directory, so the tomcat user can't run it successfully in such cases. (This is easily fixed by making sure that the tomcat user has a home directory, but it did trip me up at first.)
In that case you could specify the full path to the lein script as an argument to the cfmljure constructor (it’s actually what we do at World Singles since our lein script is under version control so we can carefully control which version we’re using on our servers).
> I also like to turn on the setting to preserve dot notation casing in the admin for Railo/Lucee because I like to be able to use dot notation with structs without having serializeJSON() returning all upper case keys. Railo/Lucee still lets you reference struct keys with any casing (it doesn't have to match the original case), so you are not forced to be consistent in your key casing. But this is not the case for hash maps :D. This resulted in the task example not running correctly at first because
task.name doesn't exist. I realized that the to-struct function in task.core.clj was upper casing the keys (makes complete sense given the default behavior of cfml).
Ah, yes. You’d be able to skip the upper-casing in your environment and just convert keywords to strings (or have your Clojure code create hashmaps with string keys instead).
> I have a couple of questions as well, resulting, I am sure, from my ignorance of clojure. Passing in a cfml struct to clojure seems to create an unusual situation. I am not sure how one would get values out of it in clojure?
The best way I’ve found is to do this:
var core = clj.clojure.core;
var structClj = core.into( core.hash_map(), structCFML );
That produces a Clojure-compatible struct with the same keys (strings) that your CFML struct had.
The underlying problem here is that the struct (in CFML) is not "associative" in the Clojure world. I could probably fix that by defining extending the struct types to implement the associative protocol - something that’s possible in Clojure and very powerful (you can extend any type to have additional methods that implement protocols - interfaces - making it easier to reuse code). The problem would be that ColdFusion, Railo, and possibly Lucee, would have different types and it would mean introducing some Clojure code (as a library) that you’d need to add to your project.clj file etc etc. In other words, the pain probably isn’t worth it.
> Also (here is a real newbie question!) how do you recommend reloading clojure in a joint cfml clojure project. Specifically, when testing passing cfml variables to clojure functions, I want to be able to change a clojure function, and reload it, to test the changes. What is the correct/recommended way to do this, short of restarting tomcat?
You can require the namespace with the :reload keyword to force Clojure to re-read it from disk and re-compile it on the fly. Here’s the code we use (variables.ns is the list of namespaces we pass to cfmljure's constructor):
public void function reload( string namespaces ) {
var core = this.clojure.core
if ( namespaces == "all" ) {
var nsList = variables.ns.filter( function( ns ) { return ns.startsWith( "worldsingles" ) } )
var reload = core.keyword( "reload" )
} else {
var nsList = listToArray( namespaces )
var reload = core.keyword( "reload-all" )
}
core.apply( core._require(), core.keyword( reload ), core.map( core._symbol(), nsList ) )
}
This is a CFC that wraps cfmljure. We install( variables.ns, this ) so that the CFC itself is our Clojure root and we inject it as a dependency into our CFML code (as variables.clj). So we can say clj.reload( "all" ) to do a simple reload of each namespace that starts with worldsingles, or we can say clj.reload( "my.ns, my.other.ns" ) to do a full dependency-level reload of those specified namespaces (reload does not refresh dependencies, reload-all does - but is slower since it recompiles all dependencies in the specified namespace).