Newbie advice on cljs + om?

694 views
Skip to first unread message

Colin Yates

unread,
Oct 19, 2014, 3:29:01 PM10/19/14
to clojur...@googlegroups.com
Any advice for a newbie about to embark on a new non-trivial SPA using cljs, om and (om-)bootstrap.

I am not a newbie in terms of CSS, JS (coffeescript for the win!) or Clojure (despite the evidence :)).

The app itself will live on an internal LAN with a small number of clients, heavyish logic but low load. It will be heavily influenced by CQRS and event sourcing, with the server transmitting "domain events since you last checked in" to the client.

In particular, what do you wish you had done differently, specifically in regard to:
- using JS from cljs (which unfortunately still makes my eyes bleed :))
- integrating 3rd party components (e.g. jquery ui) with om
- unit testing (previously used midge work but I think I will stick with core.test with the humane plugin)
- cross browser javascript (I assume the google closure library helps here)
- hooking up a browser to the REPL (IE8 unfortunately!)
- web sockets/polling (again IE8)

I am close to finalising on (but counter-arguments welcome!):
- Cursive clojure (falling back to emacs if necessary - so far it isn't)
- lein-cljsbuild
- garden for CSS (but happy to hear stories around asset management)
- core.typed
- core.test or midje
- transmit for encoding data

(I have had a look at luminus and it seems great. However, I "get" om architecturally more than reagent and I have already settled on a number of other libraries. I have also looked at pedestal but it needs to be deployed on Windows which they don't support.)

Anything you wish somebody had told you before you started?

Thanks a bunch!

Mike Haney

unread,
Oct 19, 2014, 7:16:20 PM10/19/14
to clojur...@googlegroups.com
Here are a few things I've picked up working on a large Clojure/Clojurescript app the last several months. Some of this is just personal opinion or what works for me, so as usual YMMV.

1) Reagent vs Om - I went back and forth (several times) between these two early in the project. I'm using Reagent now, but I have no qualms about using Om if it fits the app better. There are lots of factors of course, but for me the decision now comes down to this - if it makes sense for the app to store all the state in a single atom, use Om. If not, use Reagent. For my current app, multiple atoms is a more natural fit, so Reagent has been a good choice.

Not that you can't use a single atom with Reagent, it's just less convenient dealing with deeply nested data without the benefit of cursors, and you lose the performance advantage (i.e. with a single atom, any change to any part of app state will cause every component that reference any part of app state to re-render). Sean Corfield has introduced a pull request to add cursor support to Reagent, but I haven't tried it (I just don't have time to be messing with various forks - once it makes it into Reagent proper, I look forward to trying it out).

2) CSS/JS frameworks - I started off using Bootstrap, then switched to Foundation and I'm much happier with the results. It just seems cleaner, and I am finding it easier to create semantic classes, which lets me avoid putting a billion classes on each item in my markup, plus lets me shrink the CSS file by cutting out unused stuff. One cool thing about Foundation is that if you are only using their mixins to create semantic styles, you can set a flag to turn off generation of all the presentational classes, which makes the generated CSS tiny (I'm targeting Cordova, so the less markup/CSS, the better).

With any framework, I avoid their JS components and just use the markup/styling. I did try Om-bootstrap (twice actually) and each time I could never get even the most basic examples working, I think it was just too immature back then.

When you think about how these frameworks work, often times we can do a lot better using the power of React. For example, consider the ubiquitous drop-down menu. In a framework like Boostrap/Foundation, the implementation is basically the same. The markup is usually a <div> with 2 inner <div>'s, one for the always-visible part of the menu, and one for the menu itself, which is hidden to start with. The displaying of the menu is usually toggled by adding a CSS class. So while this is easy enough to do in React, another approach that's more "Reacty" is to only generate the menu markup when it is visible, rather than always creating it and hiding it until needed.

That being said, often times I do just use the traditional approach and toggle visibility using styles for one specific reason - animations. There are some libraries for doing animation with React, I just haven't had time to look into them, and it's SOOO easy with CSS, not to mention one of the most common "performance tips" you see for Cordova is to use CSS animations because they tend to be hardware-accelerated on mobile browsers. So in general, when I need an animated effect for a menu or panel sliding in versus just appearing, I stick with the CSS approach, otherwise I do it the React way and just generate the markup I need for the current application state.

3) JS components - so a lot of those components that come with frameworks can be problematic with React, and like I said before emulating or replacing their functionality is easy to do in a lot of cases. But there are times when you REALLY need to integrate a complex component - a D3 chart, a calendar control, ACE editor, etc. The way you do this with React is to let React create the DOM node for the component, then tell React to forget about it and never update it. Check out this blog from Robert Stuttaford for a great detailed explanation/example - http://www.stuttaford.me/2014/08/10/om-interop-with-3rd-party-libs/

4) Cross-browser - For the DOM and event stuff, React is the driver here. You would still use the Google Closure library for things like accessing the browser history, AJAX, and there are some nice general utilities in there for stuff like date/time handling. Most of these things have 3rd party libraries available that simply wrap Google Closure, so you can avoid the direct JS interop code.

Just don't try to use the Closure library to do stuff that React is in control of. I've never actually tried it, but for example say you grab a ref to a node React manages and then use Closure to register event listeners on it or manipulate it in some way, bad things are bound to happen (at best, React just overwrites all your changes on the next render cycle, at worst ???).

5) IDE - I started using Lighttable and it's still working pretty well for me. It does sometimes crash after several days of heavy use, but I'm in the habit of using an NRepl connection instead of letting Lighttable create its own connection, so when it crashes I just restart and connect to my still-running REPL and I haven't lost anything.

I've played around with Cursive and I really like it and would like to switch, but what keeps me with Lighttable is the Clojurescript REPL works so well and it's trivial to get it up and running. I keep hearing stories of how hard it is to get a browser repl working in just about every other environment, but with Lighttable, it "just works".

6) Websockets - for various reasons, I'm using Firebase right now (this is temporary, but working quite well). Before that I was experimenting with Sente and I had a contractor working for me that implemented some stuff using vert.x (he pulled out just the distributed event bus piece, so we didn't have to run the whole stack). Both of those solutions worked very well.

7) cljsbuild - of course lein cljsbuild is the way to get started, and that's what I'm still using. But climbing up my to-do list is to look into shadow-build. It's supposed to be better at building multiple targets from a single code base, which is something I need (i.e. I have 3 different web apps, with shared components between them, and I want to generate distinct builds with just the parts needed for each app).

Thomas Heller

unread,
Oct 20, 2014, 4:28:43 AM10/20/14
to clojur...@googlegroups.com
As the author of shadow-build [1] and shadow [2] I'm obviously a bit biased on what to use on the CLJS side of things so I'll skip that part.

As for CSS and other "static" assets (images, fonts, ...) I found it is best to make it a build/compile step and deploy it seperately from the application. Many tools in other languages or even Clojure aim to make it part of the request/response cycle via "middleware" (ring, rack, etc). I found this to be absolutely unacceptable when the apps reach a certain point since it gets quite common to release a new "client" build (cljs, css, ...) without touching/restarting the server which is required if you ship everything in a jar. That requires a seperate server for static files but that is my normal setup anyways. (nginx -> varnish -> clojure)

As for the tools used:
- SASS/SCSS via ruby gem
- thor gem (also ruby, clojure startup is too slow) for versioning/packaging
- capistrano (also ruby) to build/deploy remote
- custom file watcher to invoke the shell commands (clojure)

That stack is still completely ruby based since "it works" and has seen quite a lot more work and use than their clojure counterparts (if they exist).

I don't have any tips on what to avoid since I haven't tried anything "new" for quite some time, apart from the CLJS bits of course which I had very specific requirements that weren't met anywhere else. ;)

HTH,
/thomas

PS: @mike: shadow-build can definitely help with multiple targets, happy to help if you have questions.

[1] https://github.com/thheller/shadow-build
[2] https://github.com/thheller/shadow

David Della Costa

unread,
Oct 20, 2014, 10:57:56 AM10/20/14
to clojur...@googlegroups.com
Hi Colin, you may find a description of our stack and experiences with it useful as one data point.

Note that we were supporting IE8 up until fairly recently, when we determined that our client base didn't require it any longer, and since it's such a pain to maintain we ditched it--but our stack could still probably support IE8 just fine.  Otherwise we support IE9, IE10, Chrome, FF and Safari.  The issues we run into for cross-browser support are generally unrelated to anything in the Clojure/ClojureScript ecosystem.

Anyways, a quick summary of our stack:

- Om on the CLJS side
- SASS and Compass for CSS (that is, wholly Clojure-ecosystem un-aware)
- clojure.test for all of our testing infrastructure, pretty much
- browserchannel for providing a persistent client-server connection (we are the current maintainers of clj-browserchannel, having taken it over from Gijs not too long ago: https://github.com/diligenceengine/clj-browserchannel)
- our own custom lib (https://github.com/diligenceengine/views, sorry docs still nonexistent) for database connectivity, wrapping HoneySQL and c.j.j
- minimal integration with 3rd-party JS libs, only where absolutely necessary--for example, we rely on plupload for supporting multi-file upload in some browsers, but it is a major pain.
- a bit of basic scaffolding on the server-side in enlive to get things up and running, load up our one-page-app front end, etc.
- compojure and the usual suspects on the server-side for serving things.

We use a pretty simple build script to get all our assets in order.  It works for us, but I could easily see people making solid arguments for other solutions.

You won't be able to support IE8/IE9 without browserchannel unless you do some sort of polling...use browserchannel.

Om has been a life-saver, and we were looking for something like it right before it came out.  I can't speak to other solutions, Reagent seems like it could be great for the right use-case too, but Om has fit our needs really well.  Om + browserchannel + our views library is like magic.

As mentioned above in general integrating 3rd-party JS libs is a pain.  I've learned to stay away from it and go full-on React/Om as much as possible, even when it comes to Google Closure widgets--it's just not worth it.  Our code size balloons everytime we have to do something using a Google Closure widget.  And just say no to jQuery--there is no reason, if you are using Om or Reagent, to be relying heavily on a DOM manipulation lib.  If you need to use one, I would choose dommy (https://github.com/Prismatic/dommy) out of all the contenders.

As far as testing--I've used midje on some other projects and as of six months ago, found it more complex than I needed--I like the simplicity of clojure.test.  I'd like to be using QuickCheck (er, test.check) more actually.

We unfortunately have almost entirely eliminated the CLJS repl from our workflow.  The flakiness <-> usefulness value ratio is just too heavily leaning in the direction of flaky, in our experience, and lein-cljsbuild compile times are pretty fast in auto mode.  YMMV.

Recently we have been trying to integrate Prismatic's schema (https://github.com/Prismatic/schema) wherever we can, and if there is anything I regret it's that we didn't do so sooner.  I think core.typed is probably a good choice too.  I think type-checking of some flavor is a must (as a project grows in size, especially).

Happy to answer any further questions you may have--hope this helps.

DD


--
Note that posts from new members are moderated - please be patient with your first post.
---
You received this message because you are subscribed to the Google Groups "ClojureScript" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojurescrip...@googlegroups.com.
To post to this group, send email to clojur...@googlegroups.com.
Visit this group at http://groups.google.com/group/clojurescript.

Gary Johnson

unread,
Oct 21, 2014, 3:49:28 PM10/21/14
to clojur...@googlegroups.com
Hi Colin,

Some additional thoughts before you dive in:

1. Browser REPL: The most polished approach I've seen seems to still be Chas Emerick's Austin plugin for leiningen. However, I've started to leave my browser REPL at home and go straight for the livecoding approach that you can see in "lein figwheel" and Immo Heikkinen's "lively" library. If you haven't tried it yet, I would STRONGLY urge you to take a look at lively before diving into the browser REPL craziness. I've used it while working through David Nolen's Om tutorials, and it was a breeze. https://github.com/immoh/lively

2. Auto-generated assets: Although I realize it seems to be the dominant approach in the Clojurescript community to auto-generate DOM nodes, CSS, and SQL within your web code, I've found it extremely refreshing to build CLJ/CLJS sites that keep all of the HTML and CSS in their own format-specific files in the resources directory and then pull them in with Enlive-style transformations using Enfocus (or now Kioo over Om - https://github.com/ckirkendall/kioo). This significantly shortens my CLJS code and allows me to edit HTML and CSS using their own modes within my editor. For SQL, I accomplish the same thing by using YesQL (https://github.com/krisajenkins/yesql), which lets me write SQL code in .sql files and generate function wrappers for each query in my CLJS code using the defquery macro.

Anyway, these are just a few more thoughts to throw into your consideration list.

Happy hacking!
~Gary

Colin Yates

unread,
Oct 23, 2014, 7:52:47 AM10/23/14
to clojur...@googlegroups.com
Thanks all for replying - good food for thought!

Stanislav Yurin

unread,
Oct 24, 2014, 8:00:23 AM10/24/14
to clojur...@googlegroups.com
I had a very long personal struggle with cljs REPL. Was using LT for almost a year, but unfortunately LT is losing the pace with the very recent cljs updates past months, plus it becomes hard to manage complex projects in there. Especially when you are missing essential features that are present in classic IDEs for years.
Finally, after the third try or something, I have settled with Cursive + cljs REPL over weasel+piggieback, and very happy with that. One really huge weasel upside is that you do not need the running webserver to connect to the REPL, which works over the websockets in weasel.

Having something like the following in the project.clj, I just run the Cursive REPL and enter "(browser-repl)" in there, done.
:profiles {:uberjar {:aot :all}
:dev {:repl-options {:init-ns stats-webapp.core
:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]
:init (do
(require 'weasel.repl.websocket)
(defn browser-repl []
(cemerick.piggieback/cljs-repl
:repl-env (weasel.repl.websocket/repl-env
:ip "0.0.0.0" :port 9001))))}}}

Joel Holdbrooks

unread,
Oct 24, 2014, 4:16:18 PM10/24/14
to clojur...@googlegroups.com

Regarding Garden, what are your concerns around asset management? Garden supports :preamble much like ClojureScript so you're able to include other flat CSS files and has built-in minification. There are several other nice features such as automatic prefixing as well.

I won't deny that Sass has much better library support than Garden (because virtually no one is sharing them) but on the flip-side you're exchanging, as I mention in the README, a _preprocessor_ for a _programming language_. Although Garden is still young it's extraordinarily powerful but if you're not a serious CSS author this power may not be useful to you. Garden is also capable of being used both from Clojure and ClojureScript which can be a "nice to have".

We use Garden in production at Outpace and Prismatic also uses it for their stylesheets as well.

Anyway, this is just my opinion.

Colin Yates

unread,
Oct 24, 2014, 4:18:35 PM10/24/14
to clojur...@googlegroups.com

No concerns - it has worked out great so far,  I just wanted to hear other's experience :).

--
Note that posts from new members are moderated - please be patient with your first post.
---
You received this message because you are subscribed to a topic in the Google Groups "ClojureScript" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojurescript/9cDFfAGsDE4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojurescrip...@googlegroups.com.

Joel Holdbrooks

unread,
Oct 24, 2014, 4:32:51 PM10/24/14
to clojur...@googlegroups.com
On Sunday, October 19, 2014 12:29:01 PM UTC-7, Colin Yates wrote:

One more thing regarding library support in Sass. Many of the frameworks you'll find out there which give you a responsive grid, button helpers, etc. are trivial to implement in Clojure. Many times it's just simple arithmetic or map manipulation. Some frameworks like Compass have no answer but unless you need that level of sophistication (many times you don't) you should be fine.

Here is a responsive grid system and modular scale: https://github.com/noprompt/noprompt.github.io/blob/master/clojure/blog/src/blog/util.clj

Kyle Cordes

unread,
Oct 24, 2014, 6:56:58 PM10/24/14
to clojur...@googlegroups.com
On Friday, October 24, 2014 at 3:32 PM, Joel Holdbrooks wrote:
> One more thing regarding library support in Sass. Many of the frameworks you'll find out there which give you a responsive grid, button helpers, etc. are trivial to implement in Clojure. Many times it's just simple arithmetic or map manipulation. Some frameworks like
>

It seems pretty slick to use one strong programming language (Clojure) rather than a collection of ad hoc programming languages. I wonder if it is feasible to convert Sass code to Clojure code that uses Garden to emit CSS - rather than starting from scratch, this would make it possible to pick up an already-polished Sass-CSS starting point then run with it leaving Sass behind.


--
Kyle Cordes
http://kylecordes.com




Joel Holdbrooks

unread,
Oct 24, 2014, 9:31:00 PM10/24/14
to clojur...@googlegroups.com, ky...@kylecordes.com

Well said. There has been some initial work on trying to extract the good parts of something like Compass or other Sass libraries but nothing has surfaced yet. On my behalf, this has been more an issue of time and where my energies are currently focused. My hope is that fairly soon I should be able to dedicate more energy into improving Garden across the board so that it is a stronger candidate when folks have to make these sorts of decisions.

Colin Yates

unread,
Oct 27, 2014, 7:22:00 AM10/27/14
to clojur...@googlegroups.com
Hi David,

I have had a look at browserchannel - it look's fantastic, thanks for the work and the pointer. Our production deployment model tends to be towards deploying WAR files on Tomcat on Windows. Do you have any hints on getting this to work there as I notice the plugins start the web server via lein.

Is that a common approach BTW - am I in the minority of still using WARs (so 90s :)). I really must look into http://commons.apache.org/proper/commons-daemon/procrun.html one of these days.

Kurt Mueller

unread,
Jul 19, 2015, 1:40:47 AM7/19/15
to clojur...@googlegroups.com

Hi Joel,

What do you mean by a "serious" CSS author? Regarding Garden, can you give any example on why Garden is powerful? I've got a sense of what I could do with Garden but I'd love to hear about real world examples where it's incredibly useful.

Thanks!

Kurt

Reply all
Reply to author
Forward
0 new messages