Chestnut + component integration next steps


#1

Now that Component has been integrated into master, I’ve been thinking on where I personally would like to see Chestnut heading:

  • Refactoring sass and less integration into their own separate components rather than having their own start function, which would let the entire dev stack be started and stopped through the same function.
  • Component integration for cljs - I’m finding that a lot of apps have some sort of configuration that need to be started/stopped/reset in a dev environment that could take advantage of cljs component integration such as bidi, and sente. The cljs components could be reset using figwheel’s on-jsload event. I’ve made a reloaded.repl port to cljs to do just this.
  • Skeleton for configuration - have a space or file for configuration via default config, and merge that with env, and/or external config files, then pass that map into the system as an easy way to manage app config. Personally I spend a lot of time trying to figure out the “right” way to configure applications and it’d be nice if Chestnut could provide some direction on this.

I realize that items on the list may be outside the scope of the original idea of the template, but as it’s evolving I figured I’d propose them here. Thoughts?


#2

Refactoring sass and less integration into their own separate components

Definitely! The whole less/sass thing needs to be reconsidered. For SASS I’d like to move to sass4clj-component [1], which is based on sass4clj [2]. It uses JNI to bind with libsass, meaning in the end it uses the same SASS implementation as sassc.

There’s also less4clj [3], there’s no corresponding less4clj-component apparently, but that shouldn’t be too hard to create.

[1] https://github.com/rufoa/sass4clj-component
[2] https://github.com/Deraen/sass4clj
[3] https://github.com/Deraen/less4clj

Component integration for cljs

I’m not against this, but also not 100% convinced we need it. My current feeling is that for what Chestnut currently does it’s overkill. On the CLJ side introducing Component makes sense because it allows us to clean up and streamline stuff we’re already doing. I don’t see that so much on the CLJS side.

My mind might change on this, I just haven’t used Component at all so far on the client side, but I’m planning to use Sente for a project so I might give it a try. A concrete example of how this would look in Chestnut would also be helpful.

Skeleton for configuration

I understand the need/want for this, and mainly want to balance that out with Chestnut’s desire for minimalism. If there’s a common pattern that people are using for this, that’s easy to wrap your head around, that doesn’t add much boilerplate, and that I find aesthetically pleasing (;)), then I think I’d be all for. I think for this one it would be best to discuss concrete proposals.


(long diversion ahead)

I think it makes sense to talk a bit about the principles that I think make Chestnut what it is, and also where Chestnut is situated in the “market”

Chestnut tries to be “minimal but complete”. It provides a hand picked stack of libraries and tools, preferably things that are pretty standard and widely used, all integrated to “just work” out of the box. It provides the glue to make it all work together, and it provides a skeleton so you have some obvious entry points where you can start adding code.

That’s where it ends. In particular generated code should not contain any code that actually “does something”, like custom middleware, or custom components, these should all be provided by libraries.

Chestnut aims to be user friendly and accessible for beginners. It does this by being small enough that it’s easy to wrap your head around, by being unsurprising (try to follow patterns you might see elsewhere), and by being well documented. This last part is very important and is something that I’d like to further improve in the future.

I think the two main “competitors” at the moment are Luminus and Duct. Their goals are different enough though that there’s still room for a Chestnut. I also think Chestnut is more focused on ClojureScript than either of those. I’m really liking what I’m seeing from Duct, but it’s decidely harder to wrap your head around, it’s also scarcely documented. I think Luminus is fantastic for someone looking for a complete Rails-esque complete solution and doesn’t care so much for minimalism. Luminus is superbly documented, definitely an inspiration.

Regarding the future, my own insights as well as those of the community as a whole have evolved over the few years that Chestnut has been around, and I think it makes sense that Chestnut evolves with those. This does not violate Rich Hickey’s “rename if you change it” rule, because Chestnut is not a library. We don’t break existing apps by changing Chestnut. The main reason I’ve held off of extra features, especially extra optional flags, is because of rising complexity in testing and maintaining the template. I used to test that live reloading of code+css worked, as well as the browser repl, for every combination of flags. I’ve tried to automate this but it’s tricky, so it’s still mostly a manual process. However I’m willing to relax my stance on this, other templates have dozens of flags and seem to be doing fine. We’ll just have to be careful not to break things :slight_smile:


#3

The feature proposals are based on my own pain points when getting started with Clojure + Clojurescript via Chestnut. I got started very quickly using the original template, but then when integrating different cljs libraries - specifically with bidi and sente - I hit reload bugs between those apps and figwheel that forced me to investigate interesting combinations with defonce, atoms, or just give up and refresh the browser. I’m also not convinced that Chestnut itself should provide such reload functionality, it would have been nice as a new user to have a reloadable system to integrate in on cljs. Also, as we’re adopting system, arguably there should be skeleton code to using the cljs parts of the library there as well.

For the live-example project using system + cljs, I’ve been working on a chat app to learn some of the libraries in the ecosystem, and get myself familiar with writing clojure applications at large. What I’ve found using component’s sente in cljs is that it is easy to have situations where figwheel reloads my code/routes/system which will either start a new system without shutting down the old one and have two parallel handlers handling events, or a static system that is not re-loadable due to a defonce, and there is no obvious way to allow for a system to gracefully shutdown and reset, like we can do in reloaded.repl.

To solve the problem and get cljs systems reloadable, I ported over reloaded.repl into a small cljs library that works with figwheel: the clojure.tools.namespace.repl/disable-reload! call is replicated with figwheel’s ^:figwheel-no-load metadata to support graceful reloads. I’ve also added a convenience function set-init-go! to reset a running system, or start a new system if none exist.

On the application side, it’s called here:

https://github.com/featheredtoast/sente-websockets-rabbitmq/blob/master/src/cljs/sente_websockets_rabbitmq/core.cljs#L142

And to get automatic reloads of the cljs system, use the on-jsload hook via figwheel:

As for configuration, I personally enjoy a “defaults” map merged with an edn file + environ. I don’t quite have a good solution or library at the moment, but perhaps a good first small step is to be able to configure the system with a map. I’m taking inspiration from the component examples on their readme:

(defn example-system [config-options]
  (let [{:keys [host port]} config-options]
    (component/system-map
      :db (new-database host port)
      :scheduler (new-scheduler)
      :app (component/using
             (example-component config-options)
             {:database  :db
              :scheduler :scheduler}))))

#4

Hey, sorry for the lack of response on this.

I’m warming up to having component there on the CLJS side as well. Maybe we could even have a +sente option to demonstrate the usefulness of it.

I’m late with producing the next Lambda Island episode, so I’m going to be busy with that for a bit longer. The good news is I’ve been digging into Component and System more. I have a better grasp now of how the Duct-style components in System work, so I definitely intend to revisit this to make sure it’s all set up really nicely.


#5

Just to drive this forward, I’ve put together a proof of concept patch your way:

Hoping it’ll be a good starting point for you to review and decide exactly what you’re looking for in terms of getting a good cljs development experience while integrating with component. This patch is a port of the above sample project, with a few extra dev nicities including all reloading functions ported directly into the default cljs.user namespace.