ClojureScript: Up and Running


Stuart Sierra and Luke VanderHart ClojureScript: Up and Running ISBN: 978-1-449-32743-9 [LSI] ClojureScript: Up and Running by Stuart Sierra and Luke VanderHart Copyright © 2013 Stuart Sierra, Luke VanderHart. All rights reserved. Printed in the United States of America. Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472. O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://my.safaribooksonline.com). For more information, contact our corporate/ institutional sales department: 800-998-9938 or corporate@oreilly.com. Editor: Meghan Blanchette Production Editor: Rachel Steely Proofreader: Kara Ebrahim Cover Designer: Karen Montgomery Interior Designer: David Futato Illustrator: Rebecca Demarest Revision History for the First Edition: 2012-10-24 First release See http://oreilly.com/catalog/errata.csp?isbn=9781449327439 for release details. Nutshell Handbook, the Nutshell Handbook logo, and the O’Reilly logo are registered trademarks of O’Reilly Media, Inc. ClojureScript: Up and Running, the image of a yellow-back duiker, and related trade dress are trademarks of O’Reilly Media, Inc. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and O’Reilly Media, Inc., was aware of a trade‐ mark claim, the designations have been printed in caps or initial caps. While every precaution has been taken in the preparation of this book, the publisher and authors assume no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein. Table of Contents Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii 1. Introduction: Why ClojureScript?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 The Rise of Browser Applications 1 The Rise of JavaScript 2 The Need for a Better Language 2 Introducing ClojureScript 3 2. Hello World. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Leiningen 6 Installing Leiningen on OS X and Linux 6 Installing Leiningen on Windows 7 Using lein-cljsbuild 7 Getting Started with the REPL 8 Compiling a ClojureScript File to JavaScript 9 Running ClojureScript in the Browser 11 Other Capabilities of lein-cljsbuild 11 3. The Compilation Process. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Architecture 13 Google Closure Compiler 13 The Google Closure Library 15 ClojureScript and Google Closure 16 The Compilation Pipeline 16 How to Compile 17 Compiling ClojureScript 17 Compilation in Depth 19 Compilation Sources 19 Compilation and Optimization Options 19 Other Compilation Options 23 iii Summary 23 4. ClojureScript Basics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 ClojureScript versus Clojure 25 Expressions and Side Effects 26 Syntax and Data Structures 26 Symbols and Keywords 27 Data Structures 27 Special Forms and Definitions 28 Functions 29 Multi-Arity Functions 30 Variadic Functions 30 Local Bindings 30 Destructuring 31 Closures 31 Flow Control 32 Conditional Branching 32 JavaScript Interop 35 The js Namespace 35 Methods and Fields 36 Constructor Functions 36 Scope of this 37 Exceptions 38 Summary 38 5. Data and State. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Primitives 39 Strings 40 Keywords 40 Symbols 40 Characters 41 Numbers 41 Booleans 41 Functions 41 nil 42 Data Structures 42 Collection Types 43 Immutability 46 Persistence 47 Identity and State 48 iv | Table of Contents Atoms 48 6. Sequences. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 The Sequence Abstraction 51 Lazy Sequences 52 Letting Go of the Head 53 The Sequence API 54 map 54 reduce 54 filter 55 Other Useful Sequence Functions 55 7. Namespaces, Libraries, and Google Closure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Namespaces 57 Using Namespaces 58 Using Namespaces Effectively 59 The Implementation of Namespaces 60 Advanced Compilation Mode 61 Consuming Libraries 62 ClojureScript Libraries 62 JavaScript Libraries 63 Creating Libraries 66 For Consumption by ClojureScript 67 For Consumption by JavaScript 68 8. Macros. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Code as Data 69 Writing Macros 69 Syntax-Quote 71 Auto-Gensyms 71 Using Macros 72 When to Write Macros 72 Summary 73 9. Development Process and Workflow. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Installing ClojureScript 75 Checking Out from Source Control 76 Downloading a Compressed Archive 76 Installing Dependencies 77 The Built-In Tools 77 Command-Line Compilation 77 Clojure REPL 78 Table of Contents | v ClojureScript REPL 78 The Browser REPL 78 Setting Up the Browser REPL 79 Additional lein-cljsbuild Features 82 Launching a Browser REPL 82 Custom bREPL Launch Commands 83 Hooking Into Default Leiningen Tasks 83 Testing ClojureScript Code 84 Including ClojureScript in JAR Files 85 Compiling the Same Code as Clojure and ClojureScript 85 10. Integration with Clojure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 AJAX 87 The Reader and Printer 88 Example Client-Server Application 89 Extending the Reader 93 User-Defined Tagged Literals 93 Sharing Code 94 Summary 95 A. Libraries. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 vi | Table of Contents Preface Who Should Read This Book This book is for software developers who want to learn how to get started using Clo‐ jureScript to build web browser applications. This book will not assume any prior knowledge of ClojureScript. We do assume that you have at least a basic working knowl‐ edge of the core JavaScript language. For the sections of this book that deal with ClojureScript in a web browser, we assume you are familiar with HTML, CSS, the DOM, and how they are manipulated in JavaScript. While this book will not assume any prior knowledge of Clojure, it is not designed to be a comprehensive reference to the Clojure programming language. We will explain Clojure language concepts in ClojureScript as they become important, but we also rec‐ ommend picking up a book on Clojure for a more thorough guide to the language. The authors of this book wrote Practical Clojure (Apress, 2010) and O’Reilly has released Clojure Programming by our friends Chas Emerick, Brian Carper, and Christophe Grand. How to Use This Book This book is both a how-to guide for using ClojureScript and a tutorial on the language itself. We have arranged the chapters in what we felt was the best order for someone who is completely new to the language but wants to get started quickly. If you already know Clojure or ClojureScript and just want advice on development tools and workflow, focus on Chapters 2, 3, 7, 9, and 10. If you want to dive into the language right away, start with Chapters 4 through 6 before reading about the development process. vii Chapter 1, Introduction: Why ClojureScript? In this chapter, we lay out the motivation for ClojureScript: why it exists and what role it is designed to fill. Chapter 2, Hello World In this chapter, we work through a complete, albeit trivial, ClojureScript application. We introduce Leiningen, the lein-cljsbuild plug-in, and how to use ClojureScript in an HTML page. We save explanation for later chapters, but this chapter should be enough to get your first ClojureScript code “up and running.” Chapter 3, The Compilation Process This chapter goes into the ClojureScript compiler in detail, explaining how it works, most of the configuration options it supports, and how it integrates with the Google Closure Compiler. Chapters 4 through 6 cover the basics of the ClojureScript language itself. Although not a complete guide to every corner of the language, they cover most of the features that are required for everyday programming. Because ClojureScript and Clojure are so sim‐ ilar, we recommend books about Clojure to learn more about the language. Chapter 4, ClojureScript Basics This chapter introduces the essential syntax and control structures of the Clojure‐ Script language including functions, bindings, scope, and interoperation with JavaScript. Chapter 5, Data and State This chapter covers the primitive and composite data structures of ClojureScript, and shows how to work with them in programs. In particular, it explains Clojure‐ Script’s approach to immutability and state management. Chapter 6, Sequences This chapter introduces Lazy Sequences, an important data structure in Clojure‐ Script that makes up a substantial portion of the standard library. Chapter 7, Namespaces, Libraries, and Google Closure This chapter covers namespaces as a feature of the ClojureScript language and also explains how files are organized in ClojureScript projects. We go into detail about how the Google Closure Compiler affects the use of libraries in ClojureScript projects, and provide a detailed flowchart for determining how best to use any particular library. Chapter 8, Macros This chapter introduces macros, an advanced language feature provided by ClojureScript. viii | Preface Chapter 9, Development Process and Workflow This chapter covers a variety of alternative methods for working with ClojureScript code apart from the workflow we have used elsewhere in the book. We demonstrate some tools packaged with ClojureScript itself, including command-line compila‐ tion scripts and the ClojureScript Browser REPL (bREPL). Chapter 10, Integration with Clojure This chapter briefly demonstrates what can be achieved by combining Clojure and ClojureScript in the same application. Conventions Used in This Book The following typographical conventions are used in this book: Italic Indicates new terms, URLs, email addresses, filenames, and file extensions. Constant width Used for program listings, as well as within paragraphs to refer to program elements such as variable or function names, databases, data types, environment variables, statements, and keywords. Constant width bold Shows commands or other text that should be typed literally by the user. Constant width italic Shows text that should be replaced with user-supplied values or by values deter‐ mined by context. This icon signifies a tip, suggestion, or general note. This icon indicates a warning or caution. Using Code Examples This book is here to help you get your job done. In general, you may use the code in this book in your programs and documentation. You do not need to contact us for permis‐ sion unless you’re reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from this book does not require permission. Preface | ix Selling or distributing a CD-ROM of examples from O’Reilly books does require per‐ mission. Answering a question by citing this book and quoting example code does not require permission. Incorporating a significant amount of example code from this book into your product’s documentation does require permission. We appreciate, but do not require, attribution. An attribution usually includes the title, author, publisher, and ISBN. For example: “ClojureScript: Up and Running by Stuart Sierra and Luke VanderHart (O’Reilly). Copyright 2013 Stuart Sierra and Luke Van‐ derHart, 978-1-449-32743-9.” If you feel your use of code examples falls outside fair use or the permission given above, feel free to contact us at permissions@oreilly.com. Safari® Books Online Safari Books Online (www.safaribooksonline.com) is an on-demand digital library that delivers expert content in both book and video form from the world’s leading authors in technology and business. Technology professionals, software developers, web designers, and business and creative professionals use Safari Books Online as their primary resource for research, problem solving, learning, and certification training. Safari Books Online offers a range of product mixes and pricing programs for organi‐ zations, government agencies, and individuals. Subscribers have access to thousands of books, training videos, and prepublication manuscripts in one fully searchable database from publishers like O’Reilly Media, Prentice Hall Professional, Addison-Wesley Pro‐ fessional, Microsoft Press, Sams, Que, Peachpit Press, Focal Press, Cisco Press, John Wiley & Sons, Syngress, Morgan Kaufmann, IBM Redbooks, Packt, Adobe Press, FT Press, Apress, Manning, New Riders, McGraw-Hill, Jones & Bartlett, Course Technol‐ ogy, and dozens more. For more information about Safari Books Online, please visit us online. How to Contact Us Please address comments and questions concerning this book to the publisher: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebastopol, CA 95472 800-998-9938 (in the United States or Canada) 707-829-0515 (international or local) 707-829-0104 (fax) x | Preface We have a web page for this book, where we list errata, examples, and any additional information. You can access this page at http://oreil.ly/ClojureScript. To comment or ask technical questions about this book, send email to bookques tions@oreilly.com. For more information about our books, courses, conferences, and news, see our website at http://www.oreilly.com. Find us on Facebook: http://facebook.com/oreilly Follow us on Twitter: http://twitter.com/oreillymedia Watch us on YouTube: http://www.youtube.com/oreillymedia Acknowledgments We would like to thank everyone involved in the development of ClojureScript as an open-source project, especially its creator, Rich Hickey. Thanks also to our technical reviewers Brenton Ashworth, Michael Fogus, and David Nolen, and to all our readers who sent in notes and corrections on early drafts. Finally, a big thank you to Justin Gehtland and Stuart Halloway, founders of Relevance, Inc., for creating a unique work‐ place that gives us the freedom to explore new technologies and contribute to the open- source community. Preface | xi CHAPTER 1 Introduction: Why ClojureScript? This book aims to get you up and running with ClojureScript, a dialect of the Clojure programming language that compiles to JavaScript. To begin, this chapter will provide some motivation for why ClojureScript exists. The Rise of Browser Applications Web applications have come a long way from simple CGI scripts, but they have always been constrained by the stateless request-response model of HTTP. As the “pages” in a web application become more elaborate, the cost in time and bandwidth of reloading an entire page just to update a single piece of information becomes prohibitively high. One of the first major uses of JavaScript on the web was to ameliorate the cost of small updates, but “web applications” remained primarily server applications for a long time, and for good reason. Deploying an application to a web server is much easier than distributing it to diverse client machines: the server is a controlled environment, with many more options for programming languages and frameworks. But by treating web browsers like dumb terminals, applications were severely limited by how quickly they could push updates to a client. Recent years have witnessed the rise of what one might call browser applications. These applications typically still have server-side components, but a significant part of their logic runs client-side, in a web browser. The web browser acts like a virtual machine, executing JavaScript code to communicate with a server, render a graphical user inter‐ face, and make all the local decisions that do not require a server. The result is a more responsive, more fluid style of interaction for client applications. While the original World Wide Web of hyperlinked documents will likely remain for many years to come, it seems probable that web server applications will be largely supplanted by web browser applications. 1 The Rise of JavaScript Browser applications were made possible by dramatic improvements in the JavaScript execution environments packaged with web browsers. High-performance JavaScript engines such as WebKit’s SquirrelFish, Mozilla’s TraceMonkey, and Google’s V8 proved that JavaScript could be fast and launched the browser performance wars. JavaScript began to succeed as a general-purpose application platform where other in-browser execution environments had failed. It was a historical accident that no one could have predicted, least of all the early developers of JavaScript. Although JavaScript has many flaws, it has a few strengths that allowed it to succeed: 1. It is a small language. Core JavaScript has a limited number of keywords, concepts, and built-in features. This makes it easy to embed in different environments. 2. It is flexible. Features missing from core JavaScript, such as namespaces or classes, can be added using the language itself. 3. JavaScript functions are first-class. Although JavaScript is not a “functional” pro‐ gramming language in the usual sense, the ability to create and compose functions as values grants it immense power. 4. It’s there. Every web browser has had JavaScript built-in since the mid-1990s. Beyond that, the ease of embedding JavaScript in other applications has led to its inclusion in products as diverse as databases and television set-top boxes. The Need for a Better Language Despite JavaScript’s overwhelming success, it still has many flaws (see Douglas Crock‐ ford’s excellent book, JavaScript: The Good Parts (O’Reilly)). It was a product of unpre‐ dictable evolution, not a carefully thought-out design process. And the vast reach and diversity of JavaScript runtimes is both a blessing and a curse: it will be difficult to create a new and improved version of the language that can replace all of the “legacy” versions deployed around the world. So JavaScript is here to stay, probably in its current form, for some time. Some have gone so far as to say that “JavaScript is the assembly language of the web” (see Scott Hansel‐ man’s article, “JavaScript is Assembly Language for the Web”). So now we are beginning to see the rise of tools and languages that treat JavaScript as a compilation target, much like bytecode on a virtual machine or object code in a traditional compiler. For example, the Google Web Toolkit compiles a subset of the Java language to JavaScript. We even have entirely new languages, such as CoffeeScript and Dart, designed to target JavaScript compilation directly. 2 | Chapter 1: Introduction: Why ClojureScript? Any cross-language compiler has to make decisions about where to draw boundaries between the source language and the target language. CoffeeScript, for example, is de‐ liberately designed to have semantics very close to those of JavaScript, adding only a cleaner syntax and protection from some of JavaScript’s more egregious flaws. Google Web Toolkit, on the other hand, is designed to hide JavaScript from developers who want to work exclusively with the Java language. Introducing ClojureScript ClojureScript is a version of the Clojure programming language, which compiles to JavaScript. Its primary target is web browser applications, but it is also applicable to any environment where JavaScript is the only programmable technology available. Clojure is a powerful, expressive, Lisp-like language developed for the Java Virtual Ma‐ chine (there is also a community-maintained port of Clojure to the .NET Common Language Runtime (CLR)). ClojureScript is more than Clojure syntax layered on top of JavaScript: it supports the full semantics of the Clojure language, including immutable data structures, lazy sequences, first-class functions, and macros. Explaining how to use these features in ClojureScript will be the subject of this book. Clojure was designed to have a symbiotic relationship with the JVM: it does not try to hide all the details of its host platform. In the same vein, ClojureScript does not try to hide all the details of JavaScript or the browser execution environment. ClojureScript uses the same native types as JavaScript, such as strings and numbers, and can call JavaScript functions directly without any special “wrapper” or “foreign-function” code. ClojureScript is also designed to work closely with best-of-breed JavaScript optimization tools such as the Google Closure Compiler. These relationships will be explored in Chapter 3. In summary, ClojureScript provides developers with a language that is more powerful than JavaScript, which can reach all the same places JavaScript can, with fewer of Java‐ Script’s shortcomings. Introducing ClojureScript | 3 CHAPTER 2 Hello World The next chapter will explain in detail how the ClojureScript compiler works, and its various options and their applications. But for now, you probably want to jump right in and get started. Due to the relative youth of ClojureScript as a technology, there aren’t any highly stand‐ ardized ways of working or best practices yet. What conventions there are tend to change frequently, and the built-in tools that ClojureScript ships with are somewhat low-level and labor-intensive to use. Therefore, in the spirit of the Up and Running title of this book, we will recommend Leiningen and lein-cljsbuild as tools for getting started, and these will be introduced in this chapter and used throughout the rest of the book. They are more mature than other tools currently available, are relatively easy to use, work on all three major platforms (Windows, Linux, and OS X), and are likely to be around for some time. Instructions for installing ClojureScript from source and running its lower-level, more primitive tools will also be included in Chapter 9. However, for most users, Leiningen should prove more than sufficient for both learning and real-world production use. Java Development Kit Clojure, ClojureScript, and Leiningen all run on top of the Java Virtual Machine (JVM), which is provided by a Java Development Kit (JDK). Many operating systems come pre‐ packaged with a JDK. For those that don’t, you can download one for free here. Get the latest version of the Java Standard Edition (SE) JDK available for your operating system. Clojure requires at least version 5. There are other JDKs available but these are not as thoroughly tested with Clojure and ClojureScript, so we recommend the Oracle JDK as the easiest way to get started. 5 Leiningen Leiningen is a build system for Clojure, named to highlight its opposition to the ven‐ erable but labor-intensive Ant build system for Java (see the short story Leiningen Versus the Ants by Carl Stephenson). It is the de facto standard for building Clojure projects in the Clojure community, and has a wide array of useful features. It utilizes Maven for dependency resolution, and can seamlessly connect to any Maven repository to acquire dependencies. However, it features an original build system opti‐ mized for Clojure workflows, and can also compile Java source code. In addition, it exposes integration points for third-party plug-ins, enabling its use with a wide variety of other programming languages, including ClojureScript via the lein-cljsbuild plug-in discussed below. This book describes Leiningen version 2, which is much more featureful than previous versions and is recommended for new projects at the time of writing. If you do need to use ClojureScript with existing versions of Leiningen, don’t worry: lein-cljsbuild is fully compatible with Leiningen 1.7.0 and up. However, you’ll need to read the legacy Lei‐ ningen documentation, as the examples included here use new configuration properties introduced in 2.0.0. Don’t worry if some things described in this chapter don’t make sense, or if you don’t understand some of the syntax or terms used. Everything covered here will be elaborated in much greater detail throughout the rest of the book. Installing Leiningen on OS X and Linux 1. Download the latest version of the lein script from the Leiningen GitHub page, and save it to a location on your system’s PATH (typically ~/bin or /usr/local/ bin). 2. Set the script to be executable (e.g., chmod +x ./lein). 3. Run the script (e.g., ./lein). Leiningen will automatically download everything it needs to function properly. That’s it! You’re now ready to use Leiningen. Git and GitHub Git is a powerful source code management system that is extremely popular among open source developers and is used for most open source projects. If you’re not already using it, you can install it and learn about how it works from its website. 6 | Chapter 2: Hello World You will probably also see frequent references to GitHub, a featureful and easy-to-use Git hosting service that is free for open source projects. ClojureScript itself is hosted on Git‐ Hub, as are practically all ClojureScript tools and libraries. Installing Leiningen on Windows 1. Download the lein.bat file from the Leiningen GitHub page, and save it to your hard drive. 2. Install either wget or curl. These are programs that the Leiningen batch script can use to automatically download the rest of its dependencies. 3. Run lein.bat, passing it the self-install argument (.\lein.bat self- install). Leiningen will download the rest of its dependencies and finish installing itself. That’s all! Leiningen is now installed on your Windows system. Using lein-cljsbuild Leiningen does not yet support building ClojureScript code on its own. Fortunately, thanks to its plug-in system, using the lein-cljsbuild plug-in for ClojureScript develop‐ ment is easy: just reference it in the :plugins key of your project.clj build configu‐ ration (demonstrated below). Before you can use lein-cljsbuild, you’ll need to create a Leiningen project (if you don’t have one already). In your command console, switch to a directory of your choice, then type: lein new hello-world This will generate a new directory prepopulated with some default files. It should contain a project.clj file, which initially will look something like this: (defproject hello-world "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.4.0"]]) To enable lein-cljsbuild, you’ll need to add two lines: a :plugins key adding lein- cljsbuild to the project, and a :cljsbuild key containing build configurations (which will start out empty). Once you’ve added them, your project.clj should look some‐ thing like the following: (defproject hello-world "0.1.0-SNAPSHOT" :description "FIXME: write description" Using lein-cljsbuild | 7 :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.4.0"] [org.clojure/clojurescript "0.0-1450"]] :plugins [[lein-cljsbuild "0.2.7"]] :cljsbuild {:builds []}) Note that on a new project, you should specify whichever versions of Clojure, Clojure‐ Script, and lein-cljsbuild are most recent (at the time of writing, this is 1.4.0, 0.0-1450, and 0.2.7, respectively, as shown in the example project.clj). Getting Started with the REPL The fastest way to start writing ClojureScript code is to fire up the REPL. For those not already familiar with the concept of a REPL from Clojure or another Lisp, REPL stands for Read Eval Print Loop, and is similar to a shell console in other languages because it can be used to program interactively. It works by successively reading text input into Lisp data structures, evaluating them in the running environment (via compilation to JavaScript, in ClojureScript’s case), printing the results of the expression back to the console, and looping back and waiting for more input. To start a basic REPL in a lein-cljsbuild project, type the following at the command line from anywhere in the project’s directory structure: lein trampoline cljsbuild repl-rhino This statement deserves some unpacking: • lein invokes the Leiningen build system. • trampoline is some ceremony Leiningen requires for running tasks with interactive user input in the console. • cljsbuild invokes the lein-cljsbuild plug-in. • repl-rhino specifies that you’ll use the Rhino REPL. Rhino is a headless JavaScript engine that runs on the JVM, which is convenient for basic experimentation with ClojureScript. Once the REPL starts up, you should see the ClojureScript REPL prompt: ClojureScript:cljs.user> Type a ClojureScript expression (for example, the println function to print to standard out in Rhino), and press Enter to evaluate it: ClojureScript:cljs.user> (println “Hello, world!”) Hello, world! nil 8 | Chapter 2: Hello World You will immediately see the string you specified printed, and the return value of the expression (which is nil, in the case of println). You can use the Rhino REPL like this to explore any of ClojureScript’s basic syntax and standard libraries. Rhino REPL versus the Browser REPL ClojureScript actually ships with two REPLs: the Rhino REPL and the Browser REPL. The Rhino REPL is much simpler and easier to use, but runs in a sandboxed, headless JavaScript instance, implemented using Rhino. For basic exploration of ClojureScript and its syntax, it works great. However, one major use case for ClojureScript is browser programming, and for that, ideally, one wants a REPL that actually runs against a real browser JavaScript environment with full access to the DOM (Document Object Model) and the ability to see changes reflected in a running browser. ClojureScript supports this, but out of necessity the model is slightly more complicated. The Browser REPL runs as two components: a client, which runs as ClojureScript in a browser, and a server, which is a separate Java process that runs on the developer’s machine and exposes an interactive console. The browser client maintains a long polling connection to the server, and whenever the developer enters an expression at the REPL console, it is compiled to JavaScript and sent to the browser, which evaluates the expression and sends back the result. Full instructions for configuring and using the Browser REPL are included in Chapter 9. Compiling a ClojureScript File to JavaScript Structuring the Leiningen project To add a ClojureScript file to your Leiningen project, you’ll want to make a few tweaks to your project directory layout. Initially, your project layout will look something like this: - hello-world/ - README.md - project.clj - src/ - hello_world/ - core.clj (Note that test files and folders are omitted for clarity, but you should definitely write unit tests wherever appropriate.) Using lein-cljsbuild | 9 Since this default structure is designed around having only one type of source code (Clojure), you’ll want to modify the directory structure slightly, to match the following: - hello-world/ - README.md - project.clj - src/ - clj/ - hello_world/ - core.clj - cljs/ - hello_world - resources/ - public/ As you can see, the src folder now has two subfolders, one for each type of source code. You’ll need to add a :source-paths configuration key to your project.clj file to reflect the new location of the Clojure source code (see the example below for what the new project.clj file will look like). You will also need to create a folder in which to place the compiled JavaScript: resources/public is a common choice. Updating the project configuration Then, you must add a build entry in the :cljsbuild configuration map in project.clj: (defproject hello-world "0.1.0-SNAPSHOT" :plugins [[lein-cljsbuild "0.2.7"]] :dependencies [[org.clojure/clojure "1.4.0"] [org.clojure/clojurescript "0.0-1450"]] :source-paths ["src/clj"] :cljsbuild { :builds [{ :source-path "src/cljs" :compiler { :output-to "resources/public/hello.js" :optimizations :whitespace :pretty-print true}}]}) The :source-path key specifies where the build looks for ClojureScript source files, and the :output-to key of the :compiler option map specifies where the ClojureScript compiler will emit compiled JavaScript files. Other compiler options will be explained in more detail in the next chapter: for now, just use the ones provided. Writing a ClojureScript file Finally, write a ClojureScript file! You can start with something very simple, intended to be run in a browser. The following ClojureScript file just declares a namespace, and then prints out “Hello World” using the document.write JavaScript function. Place it in a file named hello.cljs in the src/cljs/hello_world/ folder (named to match the namespace you declared) in your ClojureScript source folder. 10 | Chapter 2: Hello World (ns hello-world.hello) (.write js/document "

Hello, world!

") Compiling Your Leiningen project is now fully configured to compile ClojureScript. Try compiling your ClojureScript by invoking the lein cljsbuild once command from the com‐ mand line, anywhere inside your Leiningen project folder. You should see a status mes‐ sage about successfully compiling resources/public/hello.js. If you like, you can inspect the emitted JavaScript file: Be aware, though, that it also includes the core Clo‐ jureScript runtime and parts of the standard library, so it’s quite long. See the next chapter for details of how this process works. You might also want to try running lein cljsbuild auto. This will keep a process open that will watch all the *.cljs files in the specified source directories, and whenever one is saved, it will recompile it automatically and replace the output file. You should also be aware of the lein cljsbuild clean command, which will delete all the compiled JavaScript files. By default, lein-cljsbuild will not recompile a file unless it detects that the file has been changed by comparing timestamps. Sometimes, however, it’s useful to force a recompile by wiping all the compiler output and restarting with a clean slate. Running ClojureScript in the Browser If you’ve written a ClojureScript file as described in the previous section, all you need to do to see it run in a browser is to write an HTML file that includes the emitted JS files in the standard way. It is common practice to place static HTML files in resources/ public. ClojureScript Hello World Open this file in the browser, and you should see your greeting, as coded in your hel lo.cljs file. If you’re running lein-cljsbuild in automatic mode, simply edit the message in hello.cljs, save the file, and refresh the browser to see your change. Other Capabilities of lein-cljsbuild Note that in addition to this basic compilation, lein-cljsbuild provides several other use‐ ful development tools and options. These include: Using lein-cljsbuild | 11 • Multiple ClojureScript builds with different options. • Launching the browser REPL. • Cross compiling the same code as both Clojure and ClojureScript (provided it meets certain requirements). See Chapter 9 for full instructions on all the configuration options and features available. 12 | Chapter 2: Hello World CHAPTER 3 The Compilation Process ClojureScript has a tight symbiotic relationship with other tools. This chapter will ex‐ plain how all the different parts fit together and then demonstrate the ClojureScript compilation process. Architecture ClojureScript is a compiler—that is, a program that takes a “source” representation as input and emits a “target” representation as output. The source representation of the ClojureScript compiler is the ClojureScript language, and the target representation is JavaScript. Unlike some JavaScript-generation tools and frameworks, ClojureScript itself does not do any “minification” or other optimizations to reduce the size of the JavaScript code it emits. Instead, ClojureScript is designed to work with the Google Closure Compiler to produce optimized JavaScript. Google Closure Compiler The Google Closure Compiler is a free, open-source compiler that uses JavaScript as both source and target representations. That is, it compiles JavaScript into JavaScript. Along the way, it can perform sophisticated optimizations to reduce the size and improve the runtime performance of JavaScript code. The fact that “Clojure” and “Closure” are homophones is an unfortunate historical accident. The owners/authors of the two projects have no re‐ lationship to one another. In this book, we will always refer to the “Google Closure Compiler” and the “Google Closure Library” by their full names. 13 The Google Closure Compiler can run in three different modes: Whitespace Only This mode removes only comments and unnecessary whitespace from JavaScript source code. The target JavaScript is functionally identical to the source JavaScript. This is similar to some simple JavaScript “minifiers.” Simple Optimizations This mode does all the same optimizations as Whitespace Only mode and further reduces the size of target JavaScript by renaming local variables and function pa‐ rameters to shorter names. Advanced Optimizations This mode does all the same optimizations as the previous two modes and also performs aggressive whole-program optimizations of JavaScript code. It will com‐ pletely remove “dead” or unreachable code, rename functions and global variables to shorter names, and even rename inline function bodies when doing so will save space. While the more aggressive optimization modes of the compiler can dramatically reduce the size of JavaScript source code, they come with a few caveats. In order to perform the optimizations in Simple and Advanced modes, the Google Closure Compiler must make certain assumptions about the source JavaScript. If the source JavaScript code violates these assumptions, the Google Closure Compiler will produce target JavaScript code that does not work as intended. For example, Simple Optimizations mode will break JavaScript code that uses JavaScript’s with operator, eval function, or any string representation of function or parameter names. Advanced Optimizations mode is even more restrictive: because it renames global variables and functions to shorten their names, it will break any code that depends on names being stable. For example, code that refers to object property names as strings (like user["name"] instead of user.name) will sometimes break under Advanced mode. The documentation for the Google Closure Compiler explains all the effects of Advanced Optimizations mode in detail. Essentially, using the Google Closure Compiler in Ad‐ vanced mode requires that developers follow strict conventions for how their JavaScript code is structured. The JavaScript code that results from following these conventions often looks “unnatural” to developers accustomed to writing optimized JavaScript code by hand, but the final result produced by the Google Closure Compiler is generally just as or more efficient than hand-optimized JavaScript run through a “minifier.” Google makes a Closure Compiler demo application available for de‐ velopers to experiment with the effects of different compilation modes. 14 | Chapter 3: The Compilation Process The Google Closure Library The Google Closure Compiler is distributed along with an extensive collection of free and open-source libraries, written in JavaScript, which follow all the conventions re‐ quired by the compiler in Advanced Optimizations mode. These libraries include data structures, common algorithms, abstractions over browser quirks, and even a GUI tool‐ kit. Because of the Advanced-mode conventions, the source code of these libraries may look “unnatural” to a JavaScript developer. The Google Closure Library code is written to target the Google Closure Compiler, so it is more verbose than most JavaScript written to target web browsers directly. Common by-hand JavaScript optimizations, such as using short names for common functions, do not matter in Advanced mode, because the compiler will rename everything anyway. The Google Closure Library is much larger than most JavaScript libraries—several megabytes as opposed to a few hundred kilobytes. Again, a JavaScript developer accus‐ tomed to hand-optimized code would think this is grossly inefficient. But the Google Closure Compiler’s Advanced-mode optimizations ensure the actual code delivered in a production application is much smaller. Any “dead” library code not actually used by the application will be eliminated during compilation. In short, you only pay (in down‐ load size) for what you use. A Few Words on Caching The Google Closure Compiler is designed to reduce the overall download size of your application, but it does not facilitate re-use of JavaScript libraries across different appli‐ cations in the same client. Experienced JavaScript developers may be more accustomed to fetching popular JavaScript libraries from Content Delivery Networks (CDNs) and relying on browser caches to reduce the overall download size. But caching is not a panacea (see Sam Saffron’s article, “Stop paying your jQuery tax”): • Many users will not have the library in their cache. • Even if a library is in the cache, web browsers will still perform an HTTP request to make sure the cache is up to date. • Parsing and executing a large JavaScript library takes time, even in the fastest browsers. As with any performance optimization problem, only exhaustive testing can prove which method is more efficient overall. Using the Google Closure Compiler, you can still utilize CDNs and client-side caching for the application code itself. Given the growing diversity of JavaScript libraries and applications, this seems like a good approach. Google itself has used this technique to deploy large, complex browser applications such as GMail and Google Docs. Architecture | 15 ClojureScript and Google Closure ClojureScript is designed to work with the Google Closure Compiler and Library. The ClojureScript compiler emits JavaScript code that is fully compatible with the Advanced Optimizations mode of the Google Closure Compiler. As a result, when programming in ClojureScript you rarely need to think about the JavaScript conventions required by Advanced mode. Many of the core libraries included with ClojureScript make use of functions in the Google Closure Library. Using ClojureScript does not mean that you are restricted to using code only in the Google Closure Library. ClojureScript can make use of any JavaScript library with a little additional configuration. However, most hand-optimized JavaScript libraries are not written with the Google Closure Compiler in mind, so they will not be compatible with Advanced Optimizations mode. ClojureScript can still use libraries such as jQuery or Prototype, but the libraries themselves will not receive the benefit of Advanced-mode compilation. Chapter 7 will cover using third-party JavaScript libraries in ClojureScript. The Compilation Pipeline The final picture of ClojureScript compilation looks like Figure 3-1. Figure 3-1. ClojureScript Compilation Process The entire compilation process happens inside a Java Virtual Machine (JVM), presum‐ ably running on a server or developer’s machine. The ClojureScript compiler is written in the Clojure language, which runs on the JVM. The Google Closure Compiler is written in the Java language. The ClojureScript compiler takes ClojureScript source code and compiles it into unop‐ timized JavaScript, which it passes to the Google Closure Compiler along with JavaScript libraries. The Google Closure Compiler takes in all the unoptimized JavaScript and emits a single optimized JavaScript source file. The JavaScript output by the Google Closure Compiler in Advanced Optimizations mode is intended for consumption by JavaScript execution engines, not humans. It is not readable and not very suitable for JavaScript debugging tools. When developing your 16 | Chapter 3: The Compilation Process application, it is more common to omit the Google Closure Compiler from the compi‐ lation process, which will result in readable JavaScript. Function and variable names in the emitted JavaScript can easily be correlated with sources in ClojureScript. Debugging support in ClojureScript still has room for improvement, but the process is already usable. In addition, ClojureScript has some unique debugging tools such as the browser- connected Read-Eval-Print-Loop (REPL), which we will cover in Chapter 9. How to Compile In this section, we will walk through the ClojureScript compilation process in detail, showing how the parts interact. The Java Classpath Most programming language implementations assume that source code libraries will be installed in some standard location, accessible system-wide. Java is different. Every time you launch the JVM, you must explicitly specify a classpath, a list of directories and files to search when loading code. The classpath is fixed when the JVM starts and cannot be changed while it is running. (Technically, it is possible to manipulate the classpath using classloaders, an esoteric JVM feature that is far outside the scope of this book.) Most Java libraries are published as Java Archive (JAR) files. JAR files are simply com‐ pressed files in the ZIP format with some additional metadata. The Clojure runtime, the ClojureScript compiler, and the Google Closure Compiler are all distributed as JAR files. (You can find links to download the JAR files at the Central Maven Repository, the most widely-used repository of JAR files. Search for “clojurescript” or “google closure” to find the latest releases.) In addition, the ClojureScript authors have packaged and distributed a version of the Google Closure Library as a JAR file for convenience. Although it is possible to launch the Java Virtual Machine and specify the classpath directly from the command line, this is rarely done in practice. Managing the classpath is one of the principal concerns of build tools, IDEs, and application servers targeting the Java language. Clojure has its own such tool, Leiningen, which was introduced in Chapter 2 and will be covered further in Chapter 9. Compiling ClojureScript The entire ClojureScript build chain, including the ClojureScript compiler and the Google Closure Compiler, can be invoked as a single function in Clojure. In this section, we will use the Clojure REPL to explore the various options of the ClojureScript com‐ piler. We’ll use a variant of the “Hello, World” example from Chapter 2. Instead of using How to Compile | 17 lein-cljsbuild, this example will invoke the ClojureScript compiler directly. This process is unlikely to become part of your day-to-day development workflow, but it is helpful to understand how the parts work. You can also use this section as a guide to incorpo‐ rating ClojureScript into customized builds. Hello, Compiler Create a new project like this: lein new ch03-hello-compiler Then modify the project.clj file to look like this: (defproject ch03-hello-compiler "0.1.0-SNAPSHOT" :dependencies [[org.clojure/clojure "1.4.0"] [org.clojure/clojurescript "0.0-1450"]] :source-paths ["src/clj"]) Create the src/clj and src/cljs directories as in Chapter 2, then put the following ClojureScript source file in src/cljs/hello_compiler/hello.cljs: (ns hello-compiler.hello) (defn ^:export main [] (.write js/document "

Hello, ClojureScript compiler!

")) Finally, create an HTML file at public/resources/index.html: ClojureScript Hello Compiler The Clojure REPL Both Clojure and ClojureScript have their own REPLs. In this chapter, we are going to invoke the ClojureScript compiler, which is implemented in Clojure, so we will be using the Clojure REPL. In your new project, you can launch the Clojure REPL by running: lein repl Then type the following to load the ClojureScript compiler: (require 'cljs.closure) Then type the following (long) expression to compile your project with the Google Closure Compiler in Advanced Mode: (cljs.closure/build "src/cljs" {:output-to "resources/public/hello.js" :optimizations :advanced}) 18 | Chapter 3: The Compilation Process The Advanced Mode optimizations are time-consuming: this simple build may take 20 seconds or more. When it finishes, you will have an optimized JavaScript source file at resources/public/hello.js. Compare the size of this file with the unoptimized file you created in Chapter 2—the optimized JavaScript emitted by the Google Closure Compiler is much smaller. Compilation in Depth When you type (cljs.closure/build ...) in the Clojure REPL you are invoking a function. The entire function call is wrapped in parentheses. The cljs.closure/ build function takes two arguments, a source and a map of options: (cljs.closure/build source options-map) Compilation Sources The source argument tells the compiler where to find our ClojureScript source files. Typically, it is the name of a directory, given as a string. The compiler will find all files with the .cljs extension in that directory and compile them together. The source argument can also be the name of a single file to be compiled. This might be useful during development, when you only want to recompile part of a project. Compilation and Optimization Options The options are passed to the cljs.closure/build function in a Clojure map, written as a series of pairs inside curly braces. In the previous example, we passed two options: :output-to "resources/public/hello.js" :optimizations :advanced The words that begin with colons are keywords, a special kind of literal data in Clojure and ClojureScript. For our purposes, they act like constants. :optimizations We have already seen two possible values for the :optimizations option, in this and the previous chapter. This option controls the optimization mode in which to run the Google Closure Compiler. :optimizations Value Google Closure Compiler Mode :none (disabled) :whitespace Whitespace-Only :simple Simple Optimizations :advanced Advanced Optimizations Compilation in Depth | 19 With an :optimizations value of :none, the Google Closure Compiler will not be in‐ voked at all, and the build will write out the JavaScript produced by the ClojureScript compiler directly. This mode is useful for development and debugging. However, the JavaScript output will be split across many individual files, requiring slightly different handling in a browser (more on this later). Where do the files go? The ClojureScript compiler produces one JavaScript file for each ClojureScript source file. These files go in a directory controlled by the :output-dir option, which defaults to a directory named out in the current working directory. The current working direc‐ tory is whatever directory the Java (or Leiningen) process was started in. The JVM does not support changing the current working directory once a program has started. The Google Closure Compiler is designed to optimize JavaScript for delivery over slow networks. As a consequence, it always produces a single JavaScript file for the entire compiled application. When any one of the optimization modes is enabled, the output of cljs.closure/build will always be a single JavaScript file. Compiling with optimizations Figure 3-2 shows the behavior of the cljs.closure/build function when compiling with optimizations. The :output-dir option controls where the ClojureScript compiler writes intermediate files. The :output-to option specifies the file location of the final output from the Google Closure Compiler. When you are compiling your application for production use, this is the JavaScript file you would put on your web server and reference in HTML pages. Figure 3-2. Compiler inputs and outputs with optimization If you do not specify an output file, the cljs.closure/build function simply returns the compiled JavaScript source code as one giant string. This might be interesting if you want to understand how the compiler works, but it’s still going to be a big blob of your entire application, so it’s probably not useful. 20 | Chapter 3: The Compilation Process Loading optimized code in a browser To run your optimized code in a browser, simply include the :output-to file in a ClojureScript programs usually do not act like “scripts” in the conventional sense. Load‐ ing the compiled JavaScript does not do anything except define functions. You typically launch your application with a “main” or “start” function invoked in a separate The details of how the ClojureScript function names translate to JavaScript object names will be covered in more detail in Chapter 7, but the short version is that hyphens become underscores. Compiling without optimizations When you specify :optimizations :none the Google Closure Compiler does not run at all (Figure 3-3). But the :output-to option is still important. Figure 3-3. Compiler inputs and outputs without optimization The Google Closure Library includes a dependency-resolution feature that makes it possible to split a JavaScript application across many source files and automatically load the right files in a web browser. This mechanism will be covered in detail in Chapter 7. For now, just know that the dependency resolution mechanism requires a special file that declares all the dependency relationships in your source code. When compiling without optimizations, the ClojureScript compiler writes this information to the file specified by the :output-to option. Compilation in Depth | 21 In order for a browser to load the individual files, the :output-dir option must be set to a directory that you can reference in the The first If your Ring server and bREPL server are running, you can visit localhost:3000/ brepl-hello.html in your browser, and upon loading, the bREPL client will establish a connection to the bREPL server and you can start evaluating forms. If you had already entered a form prior to starting the bREPL client, the REPL should have come “unstuck” as soon as you started the client and an execution environment became available. To see an example of a live update to the HTML page, enter the following form at the browser REPL: ClojureScript:cljs.user> (js/alert "Hello from bREPL!") The Browser REPL | 81 You should see a JavaScript alert box pop up from the web page. Congratulations! You now have a live REPL running against a real web page. If you like, you can start your browser’s debugging console and watch the bREPL send messages back and forth via AJAX requests as you type forms at the REPL. Stability of the bREPL Unfortunately, the browser REPL isn’t as stable as it could be, although it is getting better. If it hangs, it’s easy enough to restart, usually by refreshing the browser, but this can get annoying if you’ve already established state in the browser environment that you’re work‐ ing with. A few tips to help you avoid bREPL crashes and lockups: • Always start the bREPL server before loading the HTML page containing the bREPL client. • Don’t enter any expressions that return something that’s not easily printable. These include infinite or very long sequences, very large data structures, and native browser objects that don’t have a reasonable string representation. • Don’t enter any expressions that wipe the contents of the page (such as docu ment.write). If you erase the page, it will also remove all scripts on the page and the bREPL client will disappear, causing the bREPL to hang. • Try not to enter any expressions that will take longer than a few seconds to process. Some browsers will assume that the script has become unresponsive and will behave erratically, or prompt you to terminate it. Additional lein-cljsbuild Features lein-cljsbuild provides several features beyond the basic functionality we have covered so far. Launching a Browser REPL As you may have noticed while working through the browser REPL section above, starting up a Clojure REPL just to start a bREPL server can be annoying, and tedious if you have to do it often. To alleviate this problem, lein-cljsbuild includes the repl-listen task, which will start a bREPL server and drop you into a ClojureScript REPL in a single step. Run it using this command: lein trampoline cljsbuild repl-listen 82 | Chapter 9: Development Process and Workflow (Recall that the trampoline task is necessary for Leiningen to receive interactive input.) This command will start the bREPL server and a ClojureScript REPL in your current console. Note that you will still need to configure the bREPL client side as discussed above—the REPL started with repl-listen will not be functional until you’ve loaded a browser page with the client side code to establish the client-server connection. Custom bREPL Launch Commands lein-cljsbuild also offers a repl-launch command which, in addition to starting the bREPL server, also launches a browser for the bREPL client. repl-launch does essentially the same thing as repl-listen, but also executes shell commands defined in the :repl-launch-commands key in the :cljsbuild configura‐ tion of project.clj. By specifying a vector representing a shell command that launches a browser, you can launch both the bREPL client and server with the same command. For example: (defproject my-project "1.0.0-SNAPSHOT" ;; other leiningen configuration items :cljsbuild { ;; other configuration items & build configurations :repl-launch-commands {"firefox" ["firefox" "page.html"]}}) If you have the “firefox” binary on your system’s PATH, you can run lein trampoline cljsbuild repl-launch firefox. The final parameter, firefox, will be looked up in the :repl-launch-commands map, and the associated command (firefox page.html) will be executed, launching Firefox and opening the specified page. Of course, you are still responsible for ensuring that the page you specified (in this case, page.html) calls the clojure.browser.repl/connect function to launch the client side of the bREPL. Hooking Into Default Leiningen Tasks Normally, ClojureScript-specific build actions are triggered using the lein cljsbuild command, which pertains exclusively to ClojureScript code. However, for some projects, it’s desirable to set up Leiningen to build everything at once. To do this, it is necessary to add hooks to Leiningen’s default tasks so that when you run lein compile, lein clean, or lein jar, the appropriate lein-cljsbuild plug-in task is also executed. lein-cljsbuild already includes these hooks; all you have to do is add them to the :hooks configuration key at the root of your project.clj: (defproject my-project "1.0.0-SNAPSHOT" ;; ... :hooks [leiningen.cljsbuild]) Additional lein-cljsbuild Features | 83 Once you’ve added this line, invoking lein compile will kick off a ClojureScript com‐ pilation (the same as lein cljsbuild once). lein clean will also run lein cljsbuild clean, and lein jar will add ClojureScript source to the JAR if it’s configured for that cljs build (see “Including ClojureScript in JAR Files” (page 85)). Testing ClojureScript Code There are a variety of ways to test ClojureScript code. lein-cljsbuild does not attempt to dictate a particular testing methodology or tools, but instead provides a generic hook for executing tests. By specifying a map of test configurations in a :test-commands key in the :cljsbuild configuration map, you can set up lein-cljsbuild to invoke any command-line sequence after compilation by invoking lein cljsbuild test. For example, if you have ClojureScript tests that compile to a file called resources/ test/test.js, you might wish to run the script in a headless browser such as Phan‐ tomJS. At the command line, you would run them by executing phantomjs resources/ test/test.js. To set up the same script to run within Leiningen, do something like this: (defproject my-project "1.0.0-SNAPSHOT" ;; other leiningen configuration items :cljsbuild { ;; other configuration items & build configurations :test-commands {"unit" ["phantomjs" "resources/test/test.js"]}}) This will create a test configuration (named “unit”), so that when you run lein cljsbuild test it will first compile the ClojureScript, then execute the phantomjs resources/test/test.js shell command in a single step. Because the test command is just a system shell invocation, you can replace it with any command you like, making it flexible enough to use any testing runtime or framework you might choose. The downside is, of course, that you’ll have to set up the test execution code yourself. But as long as you can run the tests from the command line, you can add them to the Leiningen test cycle using this technique. The :test-commands configuration also supports capturing the stdout and stderr output streams from commands that it runs. To do this, add :stdout or :stderr key/ value pairs after the command sequence: (defproject my-project "1.0.0-SNAPSHOT" ;; ... other leiningen configuration items ... :cljsbuild { ;; ... other configuration items & build configurations ... :test-commands {"unit" ["phantomjs" "test.js" :stdout "test.out.txt" :stderr "test.err.txt"]}}) With this configuration, output from the phantomjs process will be redirected to the test.out.txt and test.out.err files. 84 | Chapter 9: Development Process and Workflow Note that you can also capture the output of :repl-launch-commands in the same way. Including ClojureScript in JAR Files If you’re creating a ClojureScript library that you’d like to be available for other projects, it’s a good idea to bundle it as a JAR file so that your clients can just add the JAR to their ClojureScript compiler classpath and start referencing your namespaces in their Clo‐ jureScript code. By default, however, Leiningen does not include *.cljs files when creating a JAR file. To tell it to do so, you must first enable the lein-cljsbuild Leiningen hooks as described above. Then, you must add the :jar true key to the ClojureScript build configurations you want included in the JAR file. When added, your project.clj looks something like this: (defproject my-project "1.0.0-SNAPSHOT" ;; ... other leiningen configuration items ... :hooks [leiningen.cljsbuild] :cljsbuild { ;; ... other cljsbuild configuration items ... :builds [{:source-path "src/cljs" ;; ... other build options ... :jar true}]}) Once you’ve added these configuration items, you can build a JAR like you normally would in Leiningen, with lein jar. The emitted JAR file will contain all the *.cljs files specified in the build configuration, making them available on the classpath for any program that includes the JAR. Compiling the Same Code as Clojure and ClojureScript If you have code that is both valid Clojure and ClojureScript, lein-cljsbuild supports cross-compiling the code using its crossovers feature. Obviously, such code must consist only of the subset of Clojure that is also valid ClojureScript, and vice versa. It must not use any of the interop forms from either language, nor can it rely on any platform-specific features. To use crossovers, specify a :crossovers key in the :cljsbuild configuration map. The value should be a vector of Clojure namespaces, which will then also be compiled as ClojureScript. So, for example, if you have a namespace called myapp.shared that you want to be available as both Clojure and ClojureScript code, your project.clj might look some‐ thing like this: Additional lein-cljsbuild Features | 85 (defproject myapp "1.0.0-SNAPSHOT" ;; ... other leiningen configuration items ... :cljsbuild { ;; ... other cljsbuild configuration items & builds ... :crossovers [myapp.shared]}) Under the hood, lein-cljsbuild implements this feature by literally copying the *.clj files containing the specified namespaces, giving them a *.cljs extension, and placing them in a interim directory, which is added to the ClojureScript source path. By default, it is .crossover-cljs. If you wish to use a different directory for this purpose, you may do so by specifying the desired path as the value of a :crossover-path key in the :cljsbuild configuration map. If you set :crossover-jar to true, the copied cross‐ over *.cljs files will also be added when building JAR files (if lein-cljsbuild is configured to do so, as described in the previous section). There is one additional caveat when using crossovers: code that contains macros. Be‐ cause ClojureScript macros are actually written in Clojure, it isn’t possible to simply copy Clojure files that contain macros to ClojureScript, and thus they won’t work as crossover code as outlined above. lein-cljsbuild does provide some (arcane) tools for resolving this situation. Refer to lein-cljsbuild’s documentation on the feature here. 86 | Chapter 9: Development Process and Workflow CHAPTER 10 Integration with Clojure ClojureScript, as we have seen, is targeted primarily at web browsers. Although this makes it possible to design complete applications that run in a browser, it is even more powerful when combined with a web server running Clojure on the JVM. Clojure’s literal data structures provide a rich data format for communication between a client and server, and with a little care you can even share code between the two languages. AJAX In spite of its original definition, Asynchronous JavaScript and XML, AJAX has become a catch-all term for rich client applications running in web browsers, communicating with a web server. The Google Closure Library provides the goog.net.XhrIo class to support asynchronous HTTP requests to a server across many different browser implementations. Here is a simple example function that performs an HTTP POST request to a server: (ns example (:require [goog.net.XhrIo :as xhr])) (defn receiver [event] (let [response (.-target event)] (.write js/document (.getResponseText response)))) (defn post [url content] (xhr/send url receiver "POST" content)) The goog.net.XhrIo/send function takes a URL, a callback function, a method name, and an optional request body. When the server responds to the request, it will invoke the callback function on an object from which you can retrieve the status code, headers, and response body sent by the server. 87 1. http://bit.ly/TdqTtU The goog.net.XhrIo class and the associated goog.net.XhrManager class provide many more options for controlling HTTP server requests. Covering all of them is outside the scope of this book, but for more information you can consult the Google Closure Library1 or Chapter 7 of Michael Bolin’s Closure: The Definitive Guide (O’Reilly). In addition, some ClojureScript libraries are growing to support easier access to the HTTP features in the Google Closure Library; see Appendix A for details. The Reader and Printer Although they started with XML, many web browser applications now use JSON (Java‐ Script Object Notation) for communication between client and server. You can use JSON in ClojureScript as well: the Google Closure Library class goog.json.Serializer can serialize data to and from JSON, and there are several JSON libraries for Clojure. However, JSON is a feeble data format when compared with Clojure’s own literal syntax. It cannot distinguish between strings and keywords, and its maps (objects) only support strings as keys. Almost any application using JSON as a data format will eventually need to translate between native application data structures and their “lossy” JSON repre‐ sentations. Clojure’s data structures, on the other hand, are rich enough to represent almost any application domain, and they have a string representation that is just as compact as JSON. Furthermore, Clojure’s literal data syntax is extensible, which we will explore later in this chapter. Table 10-1 highlights the differences between JSON and Clojure data. Table 10-1. JSON and Clojure data differences Feature JSON Clojure Numbers Yes Yes Strings Yes Yes Symbols - Yes Keywords - Yes Lists (Arrays) Yes Yes Maps with string keys Yes Yes Maps with arbitrary keys - Yes Sets - Yes Metadata - Yes Extensibility - Yes 88 | Chapter 10: Integration with Clojure Like any LISP-like language, both Clojure and ClojureScript have a reader, a function that transforms a stream of characters into data structures such as lists, maps, and sets. The ClojureScript compiler uses the same reader as the Clojure language runtime. The Clojure reader (invoked through the functions read and read-string) is implemented in the Java language, so it is not available to ClojureScript programs. But ClojureScript has its own reader, implemented in ClojureScript, which is designed to be fully com‐ patible with the Clojure reader. The ClojureScript reader is invoked through the function cljs.reader/read-string. As the name suggests, it takes a string argument and returns a single data structure read from that string: (ns example (:require [cljs.reader :as reader])) (reader/read-string "{:a 1 :b 2}") ;;=> {:a 1, :b 2} The opposite of read-string is the built-in ClojureScript function pr-str, or “print to string,” which takes a data structure and returns its string representation: (pr-str {:language "ClojureScript"}) ;;=> "{:language \"ClojureScript\"}" Notice that pr-str automatically escapes special characters and places strings in double quotes, which the print and println functions do not: (println {:language "ClojureScript"}) ;; {:language ClojureScript} ;;=> nil In general, the print, println, and str functions are used for human-readable output, whereas the pr, prn, and pr-str functions are used for machine-readable output. Example Client-Server Application Building a complete client-server application in Clojure and ClojureScript requires some knowledge of Clojure web libraries, which are outside the scope of this book. But the following example should give you an idea of how easy it is to communicate between the two languages. This simple application will allow you to type Clojure expressions into a web form, evaluate them on the server, and display the result back in the web page. Create a new project directory with the following project.clj file: (defproject client-server "0.1.0-SNAPSHOT" :plugins [[lein-cljsbuild "0.2.7"]] :dependencies [[org.clojure/clojure "1.4.0"] [org.clojure/clojurescript "0.0-1450"] [domina "1.0.0"] [compojure "1.1.0"] Example Client-Server Application | 89 2. http://bit.ly/QiaNjY 3. http://bit.ly/QiaS7h 4. http://bit.ly/SQL6sZ [ring/ring-jetty-adapter "1.1.1"]] :source-paths ["src/clj"] :main client-server.server :cljsbuild { :builds [{ :source-path "src/cljs" :compiler { :output-to "resources/public/client.js" :optimizations :whitespace :pretty-print true}}]}) Our application will use the Clojure libraries Ring2 and Compojure3 for the server side of the application, and the ClojureScript library Domina4 for the client. Here is the server implementation, in the file src/clj/client_server/server.clj: (ns client-server.server (:require [compojure.route :as route] [compojure.core :as compojure] [ring.util.response :as response] [ring.adapter.jetty :as jetty])) (defn eval-clojure [request] (try (let [expr (read-string (slurp (:body request)))] (pr-str (eval expr))) (catch Throwable t (str "ERROR: " t)))) (compojure/defroutes app (compojure/POST "/eval" request (eval-clojure request)) (compojure/GET "/" request (response/resource-response "public/index.html")) (route/resources "/")) (defn -main [] (prn "View the example at http://localhost:4000/") (jetty/run-jetty app {:join? true :port 4000})) Next, the client side, at src/cljs/client_server/client.cljs: (ns client-server.client (:require [goog.net.XhrIo :as xhr] [domina :as d] [domina.events :as events])) (def result-id "eval-result") (def expr-id "eval-expr") 90 | Chapter 10: Integration with Clojure (def button-id "eval-button") (def url "/eval") (defn receive-result [event] (d/set-text! (d/by-id result-id) (.getResponseText (.-target event)))) (defn post-for-eval [expr-str] (xhr/send url receive-result "POST" expr-str)) (defn get-expr [] (.-value (d/by-id expr-id))) (defn ^:export main [] (events/listen! (d/by-id button-id) :click (fn [event] (post-for-eval (get-expr)) (events/stop-propagation event) (events/prevent-default event)))) Finally, we need an HTML page to contain the application: ClojureScript Client-Server Example

ClojureScript Client-Server Example

The result:

      
This example is slightly different from most of the HTML in this book: the