Anyone following my tech trajectory will know that, after starting to learn Clojure in 2010, I’ve moved increasingly away from CFML and toward Clojure. In 2014, my team decided that Clojure would be our official primary language and all new development would happen there instead of in CFML. We still have a lot of CFML code in production - about 90kloc - but we consider it “legacy code” at this point. Most of that CFML code is a large ColdBox app that we built about five years ago (technically it’s three ColdBox apps but they share a lot of code). Our application Model has been slowly moving to Clojure so that we can reuse that code in new applications we’re building in Clojure. We’ve also been building new apps with FW/1 (and reusing both our CFML code and our Clojure code). We still like CFML as a templating language for views and our controllers - in our FW/1 apps at least - are often mostly just “glue” code that lets us call into our Clojure model code.
I’ve talked in passing on the FW/1 mailing list about the possibility of deeper integration with Clojure and I recently published cfmljure 0.1.0 - which I’ll be blogging about shortly - and so the topic came up again on the mailing list about this integration. I’d been thinking about how to write controllers in Clojure so that you could have a FW/1 app that used CFML for the views - where it excels as a templating language - and Clojure for the controllers and the model, leveraging the expressive power and immutable safety for all your business logic.
Over the last few days, I created a fork of FW/1 3.0 that included cfmljure and built a proof of concept of Clojure controllers. You can take a look at the FW/1 example with Clojure controllers on the
clojure branch of the FW/1 repo. I created the project using Leiningen (Clojure’s build tool) and then added
index.cfm, and the
views/ tree. Then I wrote the
controllers/main.clj file (in
src/hello) and the
controllers_test.clj test file (in
test/hello). Unit testing is built in, so you can run
lein test to see the results. Then I refactored the Clojure code (creating the separate
greet.clj file) and added a “service” in Clojure just for fun. The FW/1 app uses
framework.ioclj - a extended version of DI/1 that uses cfmljure - to auto-discover the Clojure code (and the CFML code - you can mix’n’match) and wraps the Clojure controllers in
framework.cljcontroller (to adapt to Clojure’s pure function calling convention, and to handle some FW/1-specific functionality). The CFML views are run as usual (and if you look in
views/main/default.cfm you’ll see a call to the Clojure “service” via the bean factory:
I’m rather excited about this because it means we’ll have a way, at work, to further migrate our model code from CFML to Clojure, while maintaining “legacy” CFML code alongside, right there in the same FW/1 application!
This won’t be part of FW/1 3.0. Instead it will stay on the
clojure branch until release 3.0 is out (
develop will be merged to
master for that), but it will be part of FW/1 3.5 which will be the next release. That way it can get some field testing in production as well as some polish and some documentation love. Stay tuned!
p.s. Right now cfmljure only runs on Railo. The CFML code itself could be made portable enough to run on ColdFusion but the real problem is interop with Java/Clojure: ColdFusion thinks 42 is a string and so you need to do a lot of string-to-number conversions to interact with Clojure through cfmljure. I haven’t used ColdFusion for over five years - just Railo - so I don’t have much incentive, but if you feel inclined to send a Pull Request with changes to make cfmljure ColdFusion-compatible…
p.p.s. cfmljure now runs on Adobe ColdFusion 11, Lucee, and Railo! Thanks to Andrew Myers for tackling ColdFusion support.