воскресенье, 15 мая 2016 г.

Using an external JavaScript library in your ClojureScript application

My recent playing with ClojureScript and Om was a great trip into the field of functional front-end programming. However it was disrupted by a small yet irritating problem. I wanted to consume an external JavaScript library - the client for Trello API - in my Om application and call a method of the object defined there. It all worked fine while I was developing the thing, but the first attempt to deploy it to the production server resulted in my application not working and spitting errors like "Trello.rf is not a function".

This problem is caused by ClojureScript munging names during compilation, which is disabled - as well as other optimizations - in the default dev profile. Because the idea of using an external JS library is not something completely stupid, there are some ways to avoid munging external names - outlined here and here. The approach with the externs file is quite easy to adopt, but it took me some experimenting to make it work, so I will describe it step-by-step here to have a reference in future.

Inputs first. I have a ClojureScript Om application enabled by cljsbuild 1.1.3. The compiled JavaScript is served from the /resources/public/js folder (or from somewhere under it). My application uses the Trello client JS library to make one call to the Trello.authorize method. The Trello library has to be brought in through the index.html like this:

<script src="https://api.trello.com/1/client.js?key=myappkey"></script>
<script src="js/compiled/trellodonelist.js" type="text/javascript"></script>

To prevent ClojureScript compiler from turning the names defined in the external library into strange symbols we only need to introduce an externs file and supply its name to the compiler. All this is done in three simple steps:

1. Create a plain JavaScript file and store it in a place where the compiler will be able to find it. I chose /resources/public/js/externs.js (the name doesn't matter that much).

2. In the externs.js file touch all the names from external libraries that you plan to refer in your app. For me that looked like:

var Trello = {};
Trello.authorize = function() {};

3. In your html refer the externs file along with the external library and your compiled ClojureScript:

<script src="js/externs.js" type="text/javascript"></script>
<script src="https://api.trello.com/1/client.js?key=myappkey"></script>
<script src="js/compiled/trellodonelist.js" type="text/javascript"></script>

4. In the project.clj file add the path to the externs file in a vector under [:cljsbuild :builds :app :compiler :externs]. You aim for something like this:

(defproject trellodonelist "0.1.0-SNAPSHOT"
  ; various meaningful things
      {:source-paths ["src/cljs"]
       :compiler {:main trellodonelist.core
                  :asset-path "js/compiled/out"
                  :output-to "resources/public/js/compiled/trellodonelist.js"
                  :output-dir "resources/public/js/compiled/out"
                  :source-map-timestamp true

                  ; this one
                  :externs ["resources/public/js/externs.js"]}}}}

  ; other meaningful things

I had to play a bit with various locations of externs.js and missed one thing or another, but the above setup finally did the trick. Now, even if the ClojureScript is compiled with all the optimizations, the compiler is aware of the names brought in from the external libs and won't change them.

There are some other ways to make external names work and I recommend to read the articles mentioned above to get a better understanding of the reasons behind the issue and a wider set of alternative solutions. For my simple case the self-made externs file seemed the easiest and the most concise approach - maybe it will suit you as well. Happy coding!

воскресенье, 1 мая 2016 г.


Some months ago we wanted to get a new developer on the team. My personal desire was to get him as soon as possible and of course I needed our recruiter's help with this. We had a short discussion with the lady about the kind of a person we'd like to hire and I was sure that we got on the same page and seeing the right candidate is only a matter of days. However, some weeks passed and I wasn't getting any resumés and not a single interview was scheduled. I was concerned almost to the point of going to the recruiter with the WTF?! expression in my face. Fortunately, though I chose to spend some 20 minutes writing a short description of two types of candidates that fitted my needs and emailing these to her.

What happened next surprised me a lot. Almost instantly I started getting a constant stream of CVs that matched my descriptions. It took us a couple days to schedule the first interview and only two or three weeks later (which means pretty soon in this context) we made an offer to a bright young fellow.

This story taught me a great lesson. I may believe that I have agreed on the goals and plans with someone, but if there is a slightest chance that doing some simple thing may help them get going I should do it without doubt and waiting. Spoken agreements made with your peers in a walkway are rarely clear and may easily get pushed away by newer and more comprehensible tasks coming from elsewhere. On the other side, taking some initiative to follow up and elaborate the problem may bring tremendous results.

Another example that I have on the same matter is concerned with an internal knowledgebase for developers. Despite having a vast code base rich with patterns and non-trivial solutions to various problems, we had limited guidance on this treasure and the reasoning behind its bits. Certainly our developers could benefit from a knowledgebase that would collect advice on various development questions.

I had a plenty of ideas on why we didn't have the knowledgebase, including the conspiracy theory that my other colleagues knew why such a thing wouldn't work. It turned out, though, that the only real reason for its absence was that being loaded with other tasks we simply didn't chose to set it up at some point. Thus, once a dedicated section was created in our internal wiki and declared a place to store all development-related knowledge, I started seeing different people contributing an article or two or simply voicing  support for the idea.

Starting things is difficult and if that's true for you, it's likely the same way for your friends and colleagues. Sometimes people lack a clear picture of the destination, in other cases they simply don't perceive the goal as important to anyone. No matter what's the core reason, just showing some gentle initiative may be enough to start the fire and get things going. It's only important to remember that initiative is not just about talking or thinking - it's all about acting, making the first step and showing the way.