{"id":552,"date":"2012-08-29T00:48:18","date_gmt":"2012-08-29T07:48:18","guid":{"rendered":"http:\/\/blog.mezeske.com\/?p=552"},"modified":"2012-08-29T10:17:52","modified_gmt":"2012-08-29T17:17:52","slug":"building-school-seating-charts-in-clojure-and-clojurescript-part-1","status":"publish","type":"post","link":"http:\/\/blog.mezeske.com\/?p=552","title":{"rendered":"Building School Seating Charts in Clojure and ClojureScript: Part&nbsp;1"},"content":{"rendered":"<p><a href=\"https:\/\/www.schoolseatingcharts.com\"><img loading=\"lazy\" src=\"http:\/\/blog.mezeske.com\/wp-content\/uploads\/2012\/08\/ssc-screenshot-small.jpg\" alt=\"School Seating Charts\" title=\"School Seating Charts\" width=\"540\" height=\"353\" class=\"aligncenter size-full wp-image-578\" srcset=\"http:\/\/blog.mezeske.com\/wp-content\/uploads\/2012\/08\/ssc-screenshot-small.jpg 540w, http:\/\/blog.mezeske.com\/wp-content\/uploads\/2012\/08\/ssc-screenshot-small-300x196.jpg 300w\" sizes=\"(max-width: 540px) 100vw, 540px\"><\/a><\/p>\n<p>I recent\u00adly launched a lit\u00adtle side-project web app, <a href=\"https:\/\/www.schoolseatingcharts.com\">School Seating Charts<\/a>, which makes it eas\u00adi\u00ader (and faster!) for teach\u00aders to build seat\u00ading charts for their class\u00adrooms.  The site is built entire\u00adly in <a href=\"http:\/\/clojure.org\/\">Clojure<\/a> and <a href=\"https:\/\/github.com\/clojure\/clojurescript\">ClojureScript<\/a>, which have been a plea\u00adsure to work&nbsp;with.<\/p>\n<p>While writ\u00ading this post, I real\u00adized that, for bet\u00adter or worse, I have a lot to say about Clojure and ClojureScript devel\u00adop\u00adment.  So, to make my life eas\u00adi\u00ader I\u2019ll be split\u00adting my thoughts into sev\u00ader\u00adal posts.  In this first post, I will give a gen\u00ader\u00adal overview of my devel\u00adop\u00adment expe\u00adri\u00adence, and in future posts I will dive more deeply into the details.<\/p>\n<h4>Tooling<\/h4>\n<p>To build the app, man\u00adage depen\u00adden\u00adcies, and gen\u00ader\u00adal\u00adly keep myself from set\u00adting my hair on fire, I use <a href=\"https:\/\/github.com\/technomancy\/leiningen\">Leiningen<\/a>.  There\u2019s not much that needs to be said here \u2014 if you\u2019re using Clojure with\u00adout Leiningen, you are doing it&nbsp;wrong!<\/p>\n<p>I devel\u00adop Clojure with Vim, which puts me in the (slight) minor\u00adi\u00adty ver\u00adsus Emacs users.  Overall, while the LISP-edit\u00ading tools in Emacs are prob\u00ada\u00adbly more refined, edit\u00ading Clojure in Vim is not bad at all.  Between <a href=\"https:\/\/bitbucket.org\/kotarak\/vimclojure\/\">VimClojure<\/a> and <a href=\"http:\/\/www.vim.org\/scripts\/script.php?script_id=3998\">Paredit.vim<\/a>, I\u2019m not left want\u00adi\u00adng.  If you go down the VimClojure route, be sure to use the <a href=\"https:\/\/github.com\/sattvik\/lein-tarsier\">lein-tar\u00adsi\u00ader<\/a> Leiningen plu\u00adg\u00adin, which makes it much eas\u00adi\u00ader to get Vim talk\u00ading to an instance of your&nbsp;app.<\/p>\n<p>Finally, of course, I use the <a href=\"https:\/\/github.com\/emezeske\/lein-cljsbuild\">lein-cljs\u00adbuild<\/a> Leiningen plu\u00adg\u00adin for com\u00adpil\u00ading and test\u00ading my ClojureScript code (as the author of the plu\u00adg\u00adin, it would be a bit weird if I did\u00adn\u2019t).  School Seating Charts is the rea\u00adson that lein-cljs\u00adbuild exists in the first place \u2014 it\u2019s a clas\u00adsic exam\u00adple of an open source project scratch\u00ading its author\u2019s itch.<\/p>\n<h4>Production Environment<\/h4>\n<p>The app runs on <a href=\"https:\/\/developers.google.com\/appengine\/\">Google App Engine<\/a> (<span class=\"caps\">GAE<\/span>).  Now, I work for Google, so I\u2019m prob\u00ada\u00adbly biased (although I began work\u00ading for Google well after I chose to tar\u00adget <span class=\"caps\">GAE<\/span>), but I have been real\u00adly hap\u00adpy with it so far as a Clojure deploy\u00adment target.<\/p>\n<p><span class=\"caps\">GAE<\/span> sup\u00adports Clojure by virtue of the fact that it sup\u00adports Java.  There\u2019s a lot of interop required to work with the <span class=\"caps\">GAE<\/span> APIs, but luck\u00adi\u00adly the <a href=\"https:\/\/github.com\/gcv\/appengine-magic\">appengine-mag\u00adic<\/a> library has already tak\u00aden care of almost all of this by wrap\u00adping the Java APIs in an idiomat\u00adic Clojure <span class=\"caps\">API<\/span>.  With this in place, it real\u00adly feels like there\u2019s native sup\u00adport for Clojure.<\/p>\n<p>Like many oth\u00ader cloud providers, <span class=\"caps\">GAE<\/span> takes care of a ton of the fun\u00adda\u00admen\u00adtals for a web app: the data\u00adbase, mem\u00adcache, serv\u00ader infra\u00adstruc\u00adture, load bal\u00adanc\u00ading, log\u00adging, met\u00adrics, and a lot more.  This con\u00adve\u00adnience comes with many restric\u00adtions, though, such as lim\u00adit\u00aded access to local files and out\u00adgo\u00ading net\u00adwork con\u00adnec\u00adtions.  However, my app\u2019s require\u00adments hap\u00adpened to mesh nice\u00adly with the fea\u00adtures and lim\u00adi\u00adta\u00adtions of <span class=\"caps\">GAE<\/span>, which I think is a large part of the rea\u00adson I\u2019ve been so hap\u00adpy with&nbsp;it.<\/p>\n<p>Deployment to <span class=\"caps\">GAE<\/span> is sim\u00adple.  The <em>lein appengine-pre\u00adpare<\/em> com\u00admand gets things ready and then the <span class=\"caps\">GAE<\/span> <span class=\"caps\">SDK<\/span> takes it from there and uploads and starts the new ver\u00adsion of the&nbsp;app.<\/p>\n<h4>ClojureScript: The&nbsp;Good<\/h4>\n<p>I\u2019m ambiva\u00adlent about the JavaScript lan\u00adguage.  I don\u2019t hate it, but nei\u00adther do I like it.  So, after I first read about ClojureScript, I jumped at the oppor\u00adtu\u00adni\u00adty to write my app\u2019s client-side code in a&nbsp;<span class=\"caps\">LISP<\/span>.<\/p>\n<p>Overall, ClojureScript has been an absolute blast.  It has numer\u00adous advan\u00adtages, such as a sol\u00adid name\u00adspace sys\u00adtem, com\u00adpile-time macros, and much of the oth\u00ader good\u00adness that one would expect from Clojure.  The sin\u00adgle biggest win, though, is being able to freely share code between the client and the serv\u00ader.  Of course, this can be done in JavaScript with <a href=\"http:\/\/nodejs.org\/\">node.js<\/a>, but JavaScript is real\u00adly just not that good of a serv\u00ader-side lan\u00adguage.  Performance aside, I\u2019d pre\u00adfer Clojure sim\u00adply due to it\u2019s access to the mas\u00adsive ecosys\u00adtem of Java libraries.<\/p>\n<p>What kinds of code does School Seating Charts share between Clojure and ClojureScript?  Well, my favorite thing is all of the <span class=\"caps\">HTML<\/span> IDs and <span class=\"caps\">CSS<\/span> selec\u00adtors.  The <span class=\"caps\">HTML<\/span> gen\u00ader\u00adat\u00aded by the serv\u00ader and the <span class=\"caps\">DOM<\/span> lookups made on the client always agree because the IDs and selec\u00adtors are defined in one spot.  This helps to pre\u00advent a whole class of errors from those things being mismatched.<\/p>\n<p>The app\u2019s con\u00adfig <span class=\"caps\">API<\/span> is shared between the client and serv\u00ader.  So, things like \u201cis debug mode on\u201d or \u201cwhat\u2019s the price of the app\u201d come from a cen\u00adtral place and are han\u00addled by the same code, so the client and serv\u00ader always agree on their val\u00adues.  Other shared code includes var\u00adi\u00adous geom\u00ade\u00adtry util\u00adi\u00adties that are required in both places.<\/p>\n<p>The best exam\u00adple of why I love using the same lan\u00adguage on the client and serv\u00ader prob\u00ada\u00adbly has to do with the code I wrote to shuf\u00adfle stu\u00addents around the class\u00adroom.  The teacher lays out desks and inputs a stu\u00addent ros\u00adter, and then can press a but\u00adton to ran\u00addom\u00adize the stu\u00addent place\u00adment, all the while respect\u00ading oth\u00ader cri\u00adte\u00adria (e.g. keep talk\u00aders away from one anoth\u00ader).  I orig\u00adi\u00adnal\u00adly exe\u00adcut\u00aded this algo\u00adrithm on the client, which worked fine in mod\u00adern browsers, but did\u00adn\u2019t per\u00adform very well in <span class=\"caps\">IE8<\/span>.  After con\u00adsid\u00ader\u00ading my options (rewrite the code in JavaScript, sim\u00adpli\u00adfy the algo\u00adrithm), I decid\u00aded to move the cal\u00adcu\u00adla\u00adtions to the serv\u00ader and have the client retrieve them via <span class=\"caps\">XHR<\/span>.  This change took me around 10 min\u00adutes to imple\u00adment.  To me, this is mind-blow\u00ading.  The algo\u00adrithm in ques\u00adtion is pret\u00adty tricky, with lots of edge-cas\u00ades, and even a straight port between lan\u00adguages would have tak\u00aden hours and intro\u00adduced&nbsp;bugs.<\/p>\n<h4>ClojureScript: The&nbsp;Bad<\/h4>\n<p>So, what are ClojureScript\u2019s rough edges?  Well, there are a few things.  For one, with so few peo\u00adple using the lan\u00adguage, you are more like\u00adly to run into edge-cas\u00ades that haven\u2019t had the bugs beat\u00aden out of them.  For instance, I used ClojureScript\u2019s <em>pr-str<\/em> func\u00adtion to seri\u00adal\u00adize data struc\u00adtures to send to the serv\u00ader.  Apparently, not many oth\u00ader peo\u00adple had tried using <em>pr-str<\/em> with a large data struc\u00adture on <span class=\"caps\">IE8<\/span>.  Performance was unus\u00adably bad, and I end\u00aded up hav\u00ading to <a href=\"http:\/\/dev.clojure.org\/jira\/browse\/CLJS-340\"> patch the com\u00adpil\u00ader<\/a> to get accept\u00adable performance.<\/p>\n<p>Debugging is anoth\u00ader rough edge.  At the time of writ\u00ading, ClojureScript does not have source map sup\u00adport, which means that when your code throws an error at run\u00adtime, you\u2019re stuck look\u00ading at a JavaScript stack trace with lit\u00adtle to no ClojureScript-spe\u00adcif\u00adic infor\u00adma\u00adtion.  Personally, I found the gen\u00ader\u00adat\u00aded JavaScript pret\u00adty easy to read, as it retains most of the sym\u00adbols from the code it was com\u00adpiled from.  Regardless, this clear\u00adly needs to improve.  Thankfully, peo\u00adple are <a href=\"http:\/\/dev.clojure.org\/jira\/browse\/CLJS-5\">work\u00ading on it<\/a>.<\/p>\n<p>ClojureScript, like Clojure, is a host\u00aded lan\u00adguage.  This means that interop with the JavaScript plat\u00adform is a first-class fea\u00adture.  Overall, interop with JavaScript code is impres\u00adsive\u00adly easy.  School Seating Charts makes exten\u00adsive use of <a href=\"http:\/\/jquery.com\/\">jQuery<\/a> and sev\u00ader\u00adal jQuery plugins.<\/p>\n<p>The com\u00adpil\u00ader is imple\u00adment\u00aded on top of the <a href=\"https:\/\/developers.google.com\/closure\/compiler\/\">Google Closure Compiler<\/a> (yes, the ter\u00admi\u00adnol\u00ado\u00adgy is extreme\u00adly con\u00adfus\u00ading), which means that ClojureScript can take advan\u00adtage of its excel\u00adlent opti\u00admiz\u00ader for things like dead code elim\u00adi\u00adna\u00adtion and com\u00adpres\u00adsion.  This is absolute\u00adly nec\u00ades\u00adsary for pro\u00adduc\u00adtion deploy\u00adment, as for School Seating Charts, the JavaScript out\u00adput is 1.<span class=\"caps\">8MB<\/span> before opti\u00admiza\u00adtion (it\u2019s <span class=\"caps\">188K<\/span> after opti\u00admiza\u00adtion, and <span class=\"caps\">46K<\/span> after&nbsp;gzip).<\/p>\n<p>However, the advanced opti\u00admiza\u00adtions come at a cost: if your ClojureScript code calls into any exter\u00adnal JavaScript libraries, you must pro\u00advide an <em>externs<\/em> file to tell the com\u00adpil\u00ader which sym\u00adbols need to be passed through uncom\u00adpressed (Luke VanderHart wrote an <a href=\"http:\/\/lukevanderhart.com\/2011\/09\/30\/using-javascript-and-clojurescript.html\">excel\u00adlent post about this<\/a>).  The doc\u00adu\u00admen\u00adta\u00adtion on how to cre\u00adate these <em>externs<\/em> files is very poor, and for me, at least, required a lot of frus\u00adtrat\u00ading tri\u00adal and error.  While this lack of doc\u00adu\u00admen\u00adta\u00adtion is ulti\u00admate\u00adly a Google Closure Compiler prob\u00adlem, it very much affects ClojureScript devel\u00adop\u00adment as&nbsp;well.<\/p>\n<p>With all of that said, please don\u2019t take my crit\u00adi\u00adcisms of ClojureScript too seri\u00adous\u00adly.  In real\u00adi\u00adty, it\u2019s inspir\u00ading that the lan\u00adguage is scarce\u00adly 14 months old and yet is total\u00adly usable for pro\u00adduc\u00adtion sys\u00adtems.  The com\u00admu\u00adni\u00adty around the lan\u00adguage is aware of all rough edges that I high\u00adlight\u00aded, and there\u2019s work being done to address them&nbsp;all.&nbsp;<\/p>\n<h4>Appendix: Libraries<\/h4>\n<p>This is just a sur\u00advey of all of the app\u2019s direct depen\u00adden\u00adcies, tak\u00aden from its project.clj con\u00adfig file.  Some of these libraries are pret\u00adty spe\u00adcif\u00adic to the way the app is built (stripe-java), and oth\u00aders are like\u00adly to be found in every Clojure web app out there (com\u00adpo\u00adjure).<\/p>\n<ul>\n<li><\/li><a href=\"https:\/\/github.com\/gcv\/appengine-magic\">appengine-mag\u00adic<\/a><br>\n    Makes it much eas\u00adi\u00ader to write a Google App Engine app in Clojure.\n<li><\/li><a href=\"https:\/\/github.com\/mmcgrana\/clj-stacktrace\">clj-stack\u00adtrace<\/a><br>\n    Handy for print\u00ading out nice\u00adly for\u00admat\u00adted (and col\u00adored) stacktraces.\n<li><\/li><a href=\"https:\/\/github.com\/seancorfield\/clj-time\">clj-time<\/a><br>\n    An idiomat\u00adic Clojure wrap\u00adper around Java\u2019s Joda Time library.\n<li><\/li><a href=\"https:\/\/github.com\/cemerick\/friend\">com.cemerick\/friend<\/a><br>\n    Makes authen\u00adti\u00adca\u00adtion and autho\u00adriza\u00adtion straight\u00adfor\u00adward for Compojure apps.\n<li><\/li><a href=\"https:\/\/github.com\/stripe\/stripe-java\">com.stripe\/stripe-java<\/a><br>\n    With this, the serv\u00ader-side billing code took maybe an hour to write and&nbsp;test.\n<li><\/li><a href=\"https:\/\/github.com\/weavejester\/compojure\">com\u00adpo\u00adjure<\/a><br>\n    A sim\u00adple and beau\u00adti\u00adful <span class=\"caps\">HTTP<\/span> rout\u00ading lay\u00ader on top of <a href=\"https:\/\/github.com\/mmcgrana\/ring\">Ring<\/a>.\n<li><\/li><a href=\"https:\/\/github.com\/ibdknox\/crate\">crate<\/a><br>\n    Provides the same <span class=\"caps\">HTML<\/span> <span class=\"caps\">DSL<\/span> as Hiccup, but for ClojureScript.\n<li><\/li><a href=\"https:\/\/github.com\/weavejester\/hiccup\">hic\u00adcup<\/a><br>\n    Provides a sim\u00adple <span class=\"caps\">DSL<\/span> for build\u00ading well-formed <span class=\"caps\">HTML<\/span>.\n<li><\/li><a href=\"https:\/\/github.com\/ibdknox\/jayq\">jayq<\/a><br>\n    A lit\u00adtle ClojureScript wrap\u00adper around com\u00admon jQuery features.\n<li><\/li><a href=\"https:\/\/github.com\/marick\/Midje\">mid\u00adje<\/a><br>\n    A test frame\u00adwork for Clojure with very pow\u00ader\u00adful mock\u00ading capabilities.&nbsp;\n<li><\/li><a href=\"https:\/\/github.com\/emezeske\/prism\">prism<\/a><br>\n    A tiny library to help share code between Clojure and ClojureScript.\n<li><\/li><a href=\"https:\/\/github.com\/scgilardi\/slingshot\">sling\u00adshot<\/a><\/ul>\n<p>    A nice lit\u00adtle improve\u00adment on the built-in excep\u00adtion fea\u00adtures in Clojure.&nbsp;\n<\/p><h4>Part 2<\/h4>\n<p>If this post was inter\u00adest\u00ading, you might want to tune in for the next post in the series.  I haven\u2019t writ\u00adten it yet, but I promise it\u2019s com\u00ading&nbsp;soon!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I recent\u00adly launched a lit\u00adtle side-project web app, School Seating Charts, which makes it eas\u00adi\u00ader (and faster!) for teach\u00aders to build seat\u00ading charts for their class\u00adrooms. The site is built entire\u00adly in Clojure and ClojureScript, which have been a plea\u00adsure to work&nbsp;with. While writ\u00ading this post, I real\u00adized that, for bet\u00adter or worse, I&nbsp;have&nbsp;[\u2026]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"wp_typography_post_enhancements_disabled":false},"categories":[3],"tags":[35,36,37],"_links":{"self":[{"href":"http:\/\/blog.mezeske.com\/index.php?rest_route=\/wp\/v2\/posts\/552"}],"collection":[{"href":"http:\/\/blog.mezeske.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.mezeske.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.mezeske.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.mezeske.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=552"}],"version-history":[{"count":55,"href":"http:\/\/blog.mezeske.com\/index.php?rest_route=\/wp\/v2\/posts\/552\/revisions"}],"predecessor-version":[{"id":610,"href":"http:\/\/blog.mezeske.com\/index.php?rest_route=\/wp\/v2\/posts\/552\/revisions\/610"}],"wp:attachment":[{"href":"http:\/\/blog.mezeske.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=552"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.mezeske.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=552"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.mezeske.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=552"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}