Repository: limist/literate-programming-examples Branch: master Commit: 90a1d2f9ce32 Files: 12 Total size: 190.1 KB Directory structure: gitextract_jmrys2px/ ├── .gitignore ├── 00-just-code-blocks/ │ └── simple-code-blocks.org ├── 01-clojure-literate-ants/ │ └── literate-ants.org ├── 02-minimal-clojure-app/ │ └── clojure-app-skeleton.org ├── 02-minimal-clojure-project/ │ └── clojure-default-skeleton.org ├── 03-pedestal-app/ │ └── pedestal-app-skeleton.org ├── 03-pedestal-service/ │ └── pedestal-service-skeleton.org ├── 05-luminus-site/ │ ├── luminus-site-skeleton.org │ └── mysite/ │ └── resources/ │ └── public/ │ ├── css/ │ │ └── screen.css │ └── md/ │ └── docs.md ├── LICENSE └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ 01-clojure-literate-ants/literate-ants.clj 01-clojure-literate-ants/project.clj 02-minimal-clojure-app/src/ 02-minimal-clojure-app/test/ ================================================ FILE: 00-just-code-blocks/simple-code-blocks.org ================================================ #+TITLE: Simple Code Blocks - a Literate Programming File #+AUTHOR: Kai Wu #+EMAIL: k@limist.com #+LANGUAGE: en #+STARTUP: align hidestars lognotestate * Introduction This file just shows simple usage of code blocks in Org mode; unlike later examples, there are no Org =:tangle= file locations used here. Instead, we just show how easy and convenient it is to mix structured prose/markup with code blocks. * 4Clojure problem 42 , [[http://www.4clojure.com/problem/42][Factorial Fun]] - first solved [2012-05-23 Wed] Write a function which calculates factorials. ** My solution, <1 minute: reduce w/ range #+BEGIN_SRC clojure #(reduce * (range 1 (inc %))) #+END_SRC ** Other solutions: =reduce=, or use =apply= with =range= #+BEGIN_SRC clojure ;; maximental's solution: #(apply * % (range 2 %)) ;; daowen's solution: #(apply * (range 1 (inc %))) ;; redraiment's solution: #(apply * % (range 1 %)) ;; sheldon's solution: #(loop [x % r 1] (if (= x 1) r (recur (dec x) (* r x)))) ;; vyzamyatin's solution: #(reduce * (range 1 (inc %))) ;; amcnamara's solution: #(apply * (range 1 (inc %))) #+END_SRC ================================================ FILE: 01-clojure-literate-ants/literate-ants.org ================================================ #+TITLE: The Clojure Ants Simulation, in Literate Form #+AUTHOR: Rich Hickey (this literate/org-babel version, Kai Wu) #+EMAIL: k@limist.com #+LANGUAGE: en #+STARTUP: align indent fold nodlcheck hidestars oddeven lognotestate #+PROPERTY: tangle literate-ants.clj * Introduction ** What is this, why is it worthwhile? Quickly please! + You're looking at a literate programming (LP) style, single-file version of Rich Hickey's Clojure ants simulator, in Org mode format. + For best results please *use Emacs with Org mode to view this* =.org= *file*. If you're looking at this on Github.com, STOP - the rendering there is neither complete nor correct! ** The *benefits* of LP using Emacs + Org 1. Docs matter, a lot. With LP, documentation is integral to development, never an afterthought. - For all but small throwaway systems, you're likely keeping a separate file of development notes already; LP would integrate that. 2. With one LP file, avoid the incidental/inessential complexity of the filesystem: avoid context-switch overhead moving between files, and sidestep your language's imposed filesystem structure. 3. Org rocks for prose: - Org's plain-text *markup is lightweight*, yet more powerful than Markdown, and cleaner than rST. - The *structural editing* provided by Org documents lets you organize your thoughts/writing/code very quickly. With good structure even major revisions are easy. - Org's exporter lets your *write-once, express-many-times*: you can export an Org file to HTML (e.g. for blogging) or LaTeX (for serious publishing). - It's easy to version-control Org files. 4. Org rocks for code: - Each code block has flexible granularity: can be named and referred to; evaluated or not; have data sent in or exported; specify different REPL sessions; specify different target/tangled files. - Code blocks are syntax-highlighted. - Code blocks are ready to edit: jump to major-mode editing easily. - A single Org file can mix different languages. 5. Meta-development, manage complexity from a coherent perspective: a unified, single-file approach encourages holistic software development and exposition, in a natural order, using structure to enhance understanding. LP is not just documentation and code together: it's a *process and abstraction unifying the development lifecycle*: requirements, architecture, design, code, tests, deployment, and maintenance - can all be bound coherently in one active format. ** Why Clojure? [[http://clojure.org][Clojure]] is a modern Lisp dialect that runs (primarily) on the [[http://en.wikipedia.org/wiki/Jvm][Java Virtual Machine]]. As a language its main paradigm is [[http://en.wikipedia.org/wiki/Functional_programming][functional programming]] (vs. the usual [[http://en.wikipedia.org/wiki/Object-oriented_programming][OO]] or [[http://en.wikipedia.org/wiki/Imperative_programming][imperative]] languages of Java, Python, Ruby), and it also provides powerful constructs for concurrency such as [[http://en.wikipedia.org/wiki/Software_transactional_memory][software transactional memory (STM)]]. So why use or learn Clojure? 1. There are *technical* reasons, better covered elsewhere. 2. There are *aesthetic* reasons: code is poetry, and like the best poems, expressiveness (the ratio of power:symbols) matters, and Clojure excels there. 3. Last but certainly not least, there are *human* reasons: I've found the Clojure community to be one of the smartest and friendliest around: there's a lot of brilliant-yet-humble innovation going on. If you've watched Hickey's talks and gotten a sense of his character, the same spirit generally pervades Clojurians. ** Why literate ants? When Clojure was invented and began picking up interest from 2007/2008, its creator Rich Hickey would [[http://www.youtube.com/watch?v=dGVqrGmwOAw][highlight Clojure's features with a demo program]]: a visual simulation of ants seeking food on a finite 2-D "world" board. I found the =ants.clj= program fascinating, particularly as simulation is a core interest. Historically, [[https://en.wikipedia.org/wiki/Object-oriented_programming#History][simulations motivated the object-oriented programming]] paradigm, so seeing a very different yet concise way to fulfill requirements of encapsulation, concurrent state, and modeling changes over time drew me in to Clojure. To build more skill in Clojure, I wanted to fully understand =ants.clj=. I also wanted to try Knuth's [[http://vasc.ri.cmu.edu/old_help/Programming/Literate/literate.html][literate programming approach]] (LP), using my favorite tool: [[http://www.gnu.org/software/emacs/][Emacs]] and its peerless [[http://orgmode.org][org-mode]]. Thus this =.org= file (and its HTML or PDF version) you're now looking at is the literate version of Hickey's =ants.clj= code. I took the original program, made minor changes for my comprehension, and offer it as a working example of literate Clojure. ** Overview and setup *** Prerequisites 1. A recent version of Emacs (ideally 24.3+). 2. Both org-mode and =clojure-mode= installed; use Emacs ELPA. - Consider using an Emacs "starter package" that provides a good baseline, like [[http://batsov.com/prelude/][Emacs Prelude]] or [[http://overtone.github.io/emacs-live/][Emacs Live]]. Then if you start Emacs and load this file, you'll see it the way it's meant to be seen: as a multi-level, hierarchically organized and structured literate code file, w/ syntax-highlighted code blocks. *** Useful commands - Use =SHIFT-TAB= and org-mode will cycle through top-level, headings, and full-expanded displays. - To generate a source-file from this =.org= file, =CTRL-c-v-t= to do the /tangling/ step; that means org-mode will process each code block below, and generate the source file =literate-ants.clj= *** Skip to the end? Good idea! Before you dive into the actual code, you may want to run the ants simulation first - seeing it in action will help with understanding the details too. So tangle this literate file per above instructions, so you have the =literate-ants.clj= file, then jump down to [[Running the Program]]. ** Caveats - this may not be the LP you're looking for 1. Don't take this file as anything like an ideal literate programming example! This is just my version of understanding Rich Hickey's code, thus it does not reflect a complete or proper literate programming approach to use. - And what's *proper LP*? See the last 2009 comment on the [[http://www.literateprogramming.com/][literateprogramming.com page]]. LP is not just about documentation, but is a tool/approach for higher-level abstraction, combining human thought and code. - So beware: much of my prose below is relatively verbose and explanatory (the /what/ and /how/ of code), as opposed to what could/should be in the literate sections: meta, /why/, high-level discussion of major design choices. 2. This version does not yet reflect more recent (post Clojure 1.2) changes to the language, e.g. =defstruct= is still used below, but has been deprecated in favor of [[http://clojure.org/datatypes][Clojure records]]. 3. My Java experience is quite limited, so parts which rely heavily on Java, such as the UI, I don't attempt to explain in-depth. * The Simulation World The first part of =ants.clj= sets up the simulation world, where we'll be introduced to some of Clojure's powers. ** Initial setup of constants/magic-numbers After the copyright notice, the initial setup code of =ants.clj= is easy to understand (for coders at least), even if you've never dealt with Lisp before. We see parameters (aka constants and magic numbers) being defined for later use using Clojure's =[[http://clojure.org/special_forms#def][def]]= special form: =def= creates a var (a mutable storage location) which connects a symbol to a value in the current [[http://clojure.org/namespaces][namespace]]. #+name: sim-world-setup #+BEGIN_SRC clojure :exports code :results silent :session s1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ant sim ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Copyright (c) Rich Hickey. All rights reserved. ;; ;; The use and distribution terms for this software are covered by the ;; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) ;; which can be found in the file CPL.TXT at the root of this distribution. ;; By using this software in any fashion, you are agreeing to be bound by ;; the terms of this license. ;; ;; You must not remove this notice, or any other, from this software. ;; Set dimensions of the world, as a square 2-D board: (def dim 80) ;; Number of ants = nants-sqrt^2 (def nants-sqrt 7) ;; Number of places with food: (def food-places 35) ;; Range of amount of food at a place: (def food-range 100) ;; Scale factor for pheromone drawing: (def pher-scale 20.0) ;; Scale factor for food drawing: (def food-scale 30.0) ;; Evaporation rate: (def evap-rate 0.99) (def animation-sleep-ms 100) (def ant-sleep-ms 40) (def evap-sleep-ms 1000) (def running true) #+END_SRC ** The board: ready to mutate via transactions Things get more interesting once the actual simulation environment needs defining: #+BEGIN_SRC clojure :exports code :results silent :session s1 (defstruct cell :food :pher) ; May also have :ant and :home values #+END_SRC First, a call to =[[http://clojuredocs.org/clojure_core/clojure.core/defstruct][defstruct]]= (like a hashmap or dictionary in other languages) defines a baseline /cell/. - =defstruct= is like a very lightweight class or constructor/template function, and conveniently wraps Clojure's =[[http://clojuredocs.org/clojure_core/clojure.core/create-struct][create-struct]]=. - Here, a cell has two keys to start, =:food= and =:pher=, to indicate the presence of food and pheromones. A cell may also have keys of =:ant= and =:home=, depending on whether an ant and/or the home-colony is present. Next, the =world= function creates the 2-dimensional "board" of cells (here, a square of 80x80 cells), represented as vectors (rows or the vertical y-dimension) of a vector (the horizontal x-dimension columns in one row): #+name sim-world-board-creation #+BEGIN_SRC clojure :exports code :results silent :session s1 ;; World is a 2d vector of refs to cells (def world (apply vector (map (fn [_] (apply vector (map (fn [_] (ref (struct cell 0 0))) (range dim)))) (range dim)))) #+END_SRC Reading the above: - Start with the innermost =[[http://clojuredocs.org/clojure_core/clojure.core/map][map]]= call, which uses an anonymous function to create one column of 80 cells, per =(range dim)=. The =[[http://clojuredocs.org/clojure_core/clojure.core/struct][struct]]= returns a new structmap instance using the earlier cell as the basis, initializing the =:food= and =:pher= values to zero. - But notice that =struct= is wrapped with a [[http://clojure.org/refs][transactional ref]], and here's the first glimpse of Clojure's concurrency powers. With each cell being stateful (possibly time-varying values of =:food=, =:pher=, =:ant=, and =:home= values) and with multiple threads updating the board and board elements, we'd typically think of using locks on each cell when updating its state. But in Clojure with its [[http://en.wikipedia.org/wiki/Software_transactional_memory][software transactional memory]] (STM), we just use =ref= for safe references to mutable collections (here, a =struct=) - all changes to a cell will then be atomic, consistent, and isolated![fn:Databases-ACID] Like using an RDBMS, you don't need to manually manage concurrency. - Once you understand the innermost =(ref (struct cell 0 0 ))= =map= call, the rest of =(def world...)= is straightforward: =apply= uses =vector= as a constructor function with the =map= function producing the vector's arguments, creating a "column" in the 2-D board. - Then the pattern is repeated in the outermost =(apply vector (map...))= call, creating all the columns of the 2-D board. - Note that as defined, each vector in =world= (again, a 2-D vector of vectors) corresponds to an x-position, and of course, within that vector are the y-positions (here, a total of 80 cells). The =place= function is a selector function (think of "place" as the noun, not the verb) returning particular cells in the 2-D world. Once we have a cell, we can then mutate it to represent ants, food, and pheromones (or their absence): #+BEGIN_SRC clojure :exports code :results silent :session s1 (defn place [[x y]] (-> world (nth x) (nth y))) #+END_SRC - =place= takes a single vector argument (having two elements x and y), then applies the [[http://www.colourcoding.net/blog/archive/2011/07/09/another-go-at-explaining-the-thrush-operator-in-clojure.aspx][thrush operator]] (the [[http://clojuredocs.org/clojure_core/clojure.core/-%3E][arrow-like ->]]) on the world object, first selecting the "column" =(nth x)= on world, then the "row" =(nth y)= on that column. [fn:Databases-ACID] STM is like a memory-only SQL database, thus the last property of being durable/persistent won't be satisfied. *** Aside: the thrush operator The thrush operator helps make code more concise, and arguably clearer: instead of reading code "inside-out" to mentally evaluate it, we can read it left-to-right.[fn:Fogus-on-thrush] Consider how the equivalent =place= function would look without thrushing: #+BEGIN_SRC clojure :exports code (defn place-verbose [[x y]] (nth (nth world x) y)) #+END_SRC [fn:Fogus-on-thrush] Apparently Clojure's thrush is not quite a true thrush, see [[http://blog.fogus.me/2010/09/28/thrush-in-clojure-redux/][Michael Fogus' article]]. ** Ants as agents - doing asynchronous uncoordinated changes Next we'll consider the "active things" in =ants.clj=, the ants themselves. As before, we start with =defstruct=, defining an ant as having only one required key, its direction. (An ant may temporarily have another key, =:food=.) #+name ants-defined #+BEGIN_SRC clojure :exports code :results silent :session s1 (defstruct ant :dir) ; Always has dir heading; may also have :food (defn create-ant "Create an ant at given location, returning an ant agent on the location." [location direction] (sync nil (let [the-place (place location) the-ant (struct ant direction)] (alter the-place assoc :ant the-ant) (agent location)))) #+END_SRC To explain the above constructor function for ants, =create-ant=: + Takes two arguments, =location= and =direction=. =location= will be a vector =[x y]=, and as we saw, passed on to the place function as an argument; =direction= is a number from 0-7 inclusive corresponding to one of the eight cardinal directions. + More concurrency support: the [[http://clojuredocs.org/clojure_core/clojure.core/sync][sync function]] takes a flags argument (as of Clojure 1.3, it's still ignored so just pass nil), and then a list of expressions that will be executed together atomically (all or nothing) as a transaction. + The [[http://clojuredocs.org/clojure_core/clojure.core/let][let special form]] binds pairs of symbols and expressions in its arguments vector, providing local, lexical bindings within the scope of the body following. + =sync= will ensure that any mutations of refs using the [[http://clojuredocs.org/clojure_core/clojure.core/alter][alter function]] will be atomic. Previously we had used =ref= around each cell, so in the above code where =the-place= is such a ref-wrapped cell, =alter= takes =the-place= ref as its first argument, then =[[http://clojuredocs.org/clojure_contrib/clojure.contrib.generic.collection/assoc][assoc]]= as the function to be [[http://clojuredocs.org/clojure_core/clojure.core/apply][apply]]'ed on the-place, tying a new ant instance to it (remember that as a cell, =the-place= is sure to have =:food= and =:pher= key-values already, now we add =:ant=). Like the thrush operator earlier, the syntax of =alter= enables convenient left-to-right reading. + Finally, the [[http://clojuredocs.org/clojure_core/clojure.core/agent][agent function]]. What are Clojure agents? To quote the docs, #+BEGIN_QUOTE Agents provide shared access to mutable state. They allow non-blocking (asynchronous as opposed to synchronous atoms) and independent change of individual locations (unlike coordinated change of multiple locations through refs). #+END_QUOTE Clojure's =agent= function takes one required argument of state, returning an agent object with initial value of that given state. Here, as the last line of =create-ant=, =agent= effectively returns the ant object at its starting location. Ants as agents make sense: we expect them to move around independently (i.e. asynchronously) in the simulation world. ** Setting up the home, and ants The home of the ants is not a single cell on the world-board, but a square of cells, with its top-left corner offset from the origin (0, 0). Its sides are proportional to the number of ants because the home square will initially contain all the ants - one ant per cell - before the simulation runs. We can see these two aspects of the home-square in the two =def= calls for =home-offset= and =home-range= below. #+name home-setup #+BEGIN_SRC clojure :exports code :results silent :session s1 (def home-offset (/ dim 4)) (def home-range (range home-offset (+ nants-sqrt home-offset))) (defn setup "Places initial food and ants, returns seq of ant agents." [] (sync nil (dotimes [i food-places] (let [p (place [(rand-int dim) (rand-int dim)])] (alter p assoc :food (rand-int food-range)))) (doall (for [x home-range y home-range] (do (alter (place [x y]) assoc :home true) (create-ant [x y] (rand-int 8))))))) #+END_SRC The =setup= function's docstring tells us what it's doing, so on to the details: + =setup= takes no arguments. + As we saw before in =create-ant=, the =sync= function wraps a sequence of expressions that together should be executed atomically, all-or-nothing. + Setup initial food: The [[http://clojuredocs.org/clojure_core/clojure.core/dotimes][dotimes function]] takes two arguments, the first a vector =[name n]= with =n= being the number of times that the =body= (the second argument) will be repeatedly executed, usually for its side-effects/mutations. - Here, the unused name =i= is bound to the integers from 0 to 34, since we had specified food-places as 35 initially. - The =body= is clear enough: bind =p= to the randomly chosen place on the world-board (using the [[http://clojuredocs.org/clojure_core/clojure.core/rand-int][rand-int function]] for x, y). The already-seen =alter= function modifies that =p= to have a random amount of food value. + Placing the ants in their starting positions: The [[http://clojuredocs.org/clojure_core/clojure.core/doall][doall function]] forces immediate evaluation of a lazy sequence - in this case the lazy sequence produced by the [[http://clojuredocs.org/clojure_core/clojure.core/for][for function]]. - Here, the =for= function's first argument is: two binding-form/collection-expr pairs for every x and y position within the square of the ants' home. - The =for= function's second argument is the body-expression, here wrapped in the [[http://clojuredocs.org/clojure_core/clojure.core/do][do special form]] which ensures order of evaluation (usually, of expressions having side-effects): designate the place as a home position, then create an ant on that place with a random initial direction. In sum, the =setup= function shows how to deal with state and its mutation in Clojure: we started with a 2-D world-board of places (cells) as Clojure refs; then we modify/mutate each place using =alter=. We can use various looping functions such as =dotimes= and =doall= to process a batch of state-mutations (of the world-board) atomically and consistently. ** Orientation and moving around the world Next, consider facing/orientation and moving to another place in the 2-D world. Three functions below, followed by explanations: #+name world-wrapping #+BEGIN_SRC clojure :exports code :results silent :session s1 (defn bound "Returns given n, wrapped into range 0-b" [b n] (let [n (rem n b)] (if (neg? n) (+ n b) n))) ;; Directions are 0-7, starting at north and going clockwise. These are ;; the 2-D deltas in order to move one step in a given direction. (def direction-delta {0 [0 -1] 1 [1 -1] 2 [1 0] 3 [1 1] 4 [0 1] 5 [-1 1] 6 [-1 0] 7 [-1 -1]}) (defn delta-location "Returns the location one step in the given direction. Note the world is a torus." [[x y] direction] (let [[dx dy] (direction-delta (bound 8 direction))] [(bound dim (+ x dx)) (bound dim (+ y dy))])) #+END_SRC With the 2-D world board, we have the 8 cardinal directions (North, North-East, East, etc.), and board edges that wrap-around to the opposite side - like the old arcade games of the 1980's, e.g. [[http://en.wikipedia.org/wiki/Pac-Man][Pac-Man]] and [[http://en.wikipedia.org/wiki/Asteroids_(video_game)][Asteroids]]. The functions =bound= and =delta-location= help enforce these world-behaviors, while the definition of =direction-delta= maps a movement in a cardinal direction to the corresponding change in x and y. A few comments on each: - The =bound= function using the built-in [[http://clojuredocs.org/clojure_core/clojure.core/rem][rem (i.e. remainder) function]] is straightforward. Observe how =bound= is used in delta-location to ensure wrap-around behavior in: 1) cardinal directions; 2) the world-board, at its edges given by =dim=. - =direction-delta= maps the eight cardinal directions (0 is North) to the corresponding changes in =[x y]=. Note the syntax: it's an array-map literal, where the order of insertion of key-value pairs (here, keys 0-7) will be preserved. - =delta-location= takes the current =[x y]= location and a direction, returning the new corresponding location on the world-board. ** Ant-agent behavior functions In Hickey's simulation, ants need to move (rotation and translation), pick up and drop-off food, and make rudimentary decisions. *** Ant movements Our ants need two behaviors to get around their world: turning (or changing the direction they "face"), and stepping forward. Let's deal with turning first: #+name ant-agent-turn #+BEGIN_SRC clojure :exports code :results silent :session s1 ;; An ant agent tracks the location of an ant, and controls the ;; behavior of the ant at that location. (defn turn "Turns the ant at the location by the given amount." [loc amt] (dosync (let [p (place loc) ant (:ant @p)] (alter p assoc :ant (assoc ant :dir (bound 8 (+ (:dir ant) amt)))))) loc) #+END_SRC The =turn= function takes two arguments, location and the amount of turn. What's interesting is the usage of [[http://clojuredocs.org/clojure_core/clojure.core/dosync][the dosync function]], which ensures the ant's turn - the changes of state within the =assoc= function calls - is all-or-nothing. The ant gets a new direction per the innermost =assoc=, then the outermost =assoc= updates the =place= with the updated ant. Now for actual movement to a new place: #+name ant-agent-move #+BEGIN_SRC clojure :exports code :results silent :session s1 (defn move "Moves the ant in the direction it is heading. Must be called in a transaction that has verified the way is clear." [startloc] (let [oldp (place startloc) ant (:ant @oldp) newloc (delta-location startloc (:dir ant)) newp (place newloc)] ;; move the ant (alter newp assoc :ant ant) (alter oldp dissoc :ant) ;; leave pheromone trail (when-not (:home @oldp) (alter oldp assoc :pher (inc (:pher @oldp)))) newloc)) #+END_SRC The =move= function changes state of both the ant and board, thus the doc-string note that it must be called in a transaction. The code is self-explanatory, though if "pheromone" is a new term to you, you'll want to [[http://en.wikipedia.org/wiki/Pheromone][learn about a dominant form of chemical communication]] on Earth. Whenever our artificial ant is not within its home, it will "secrete" pheromone (=inc= the =:pher= value by 1) at the place it just left, making it easier (more likely) for it and other ants to travel between home and food locations in the future (instead of doing a completely random walk). *** Ants and food When an ant finds food, it "picks up" one unit of it; when it returns home with a food unit, it will "drop" its food there. These two interactions (each having two steps) change the board, and as with the =move= function, they need to occur atomically (all-or-nothing) to ensure the [[http://www.youtube.com/watch?v=z_KmNZNT5xw][world is in a consistent state]]. #+name ant-agent-food #+BEGIN_SRC clojure :exports code :results silent :session s1 (defn take-food [loc] "Takes one food from current location. Must be called in a transaction that has verified there is food available." (let [p (place loc) ant (:ant @p)] (alter p assoc :food (dec (:food @p)) :ant (assoc ant :food true)) loc)) (defn drop-food [loc] "Drops food at current location. Must be called in a transaction that has verified the ant has food." (let [p (place loc) ant (:ant @p)] (alter p assoc :food (inc (:food @p)) :ant (dissoc ant :food)) loc)) #+END_SRC Notice how similar the structure is for the two functions above; possibly they're candidates for macro refactoring. *** Ant judgment Our ants need some decision-making for their overall task of finding food and bringing it home. As we'll see shortly, an ant's behavior is based on two states, either: 1. The ant does not have food, and is looking for it. In this mode, it weighs the three map locations ahead of it (ahead, ahead-left, ahead-right) by the presence of either food or pheromone. 2. The ant has food, and needs to bring it to the home box/location. Now it weighs which of the three ahead-positions to take by the presence of pheromone, or home. So we need functions to express preference of the next location for an ant. The functions =rank-by= and =wrand= help with that. #+name ant-agent-judgment-1 #+BEGIN_SRC clojure :exports code :results silent :session s1 (defn rank-by "Returns a map of xs to their 1-based rank when sorted by keyfn." [keyfn xs] (let [sorted (sort-by (comp float keyfn) xs)] (reduce (fn [ret i] (assoc ret (nth sorted i) (inc i))) {} (range (count sorted))))) #+END_SRC The =rank-by= function gives weights to where an ant will move next in the simulation world. It takes two arguments, =keyfn= and =xs= - but what do those args look like, and where is =rank-by= used? In the =behave= function below; you'll see that the =keyfn= checks for the presence of =:food=, =:pher=, or =:home= - in the three cells (board locations) of the =xs= vector of =[ahead ahead-left ahead-right]=.[fn:Mutex-cell-values] - The [[http://clojuredocs.org/clojure_core/clojure.core/sort-by][(sort-by keyfn coll) function]] returns a sorted sequence of items in coll, ordered by comparing =(keyfn item)=. Here, for the local value sorted, it will be ascending order of cells/places, by their :food/:home/:pher values - each of those is valuable to an ant depending on whether it's looking for food, or bringing it home. - The [[http://clojuredocs.org/clojure_core/clojure.core/reduce][(reduce f initial-val coll) functionn]] in its 3-arguments form here has its 1st argument =f= as a function taking two arguments, the current/initial-val value and the next/first item from coll. In this case, it will "build-up" a map from the local sorted value, with the keys being the ranked cells/places, and the values being integers 1, 2 and 3. To get a sense of what's going on, try this on your Clojure REPL: #+BEGIN_SRC clojure (let [sorted [0 0.7 1.0]] (reduce (fn [ret i] (assoc ret (nth sorted i) (inc i))) {} (range (count sorted)))) ;; You should see {1.0 3, 0.7 2, 0 1} ;; ;; Within the behave function below, the return value might be ;; like { 3, 2, 1} ;; or similar. #+END_SRC [fn:Mutex-cell-values] Remember that =:food=, =:pher=, and =:home= are mutually exclusive in a cell. When an ant wants to go home with food, and the home cell(s) is ahead of it, it will always go home, there won't be competing =:pher= presence. Next: The =wrand= function helps with the larger task of randomizing which location/cell the ant moves to next in a weighted manner; i.e. the "dice" are loaded with =rank-by=, then "rolled" here: #+name ant-agent-judgment-2 #+BEGIN_SRC clojure :exports code :results silent :session s1 (defn wrand "Given a vector of slice sizes, returns the index of a slice given a random spin of a roulette wheel with compartments proportional to slices." [slices] (let [total (reduce + slices) r (rand total)] (loop [i 0 sum 0] (if (< r (+ (slices i) sum)) i (recur (inc i) (+ (slices i) sum)))))) #+END_SRC How is =wrand= used? Like =rank-by=, look in the =behave= function: its single argument of slices is a vector of 3 integers (from =rank-by= above), corresponding to the relative desirability of the 3 cells ahead of the ant. So if the slices argument looked like =[0 3 1]=, that would correspond to zero probability of moving ahead, and 3/4 chance moving to the ahead-left cell over the ahead-right cell. - The =let= value =total= uses =reduce= to set the upper bound on the random number; loosely like setting the maximum number of faces on the die to be rolled (albeit that some die numbers are geometrically impossible). - The [[http://clojuredocs.org/clojure_core/clojure.core/rand][rand function]] returns a random floating point number from 0 (inclusive) to n (exclusive). - Here's the only looping construct in the entire ants program: it's analogous to checking which compartment of the roulette wheel the ball fell in. The =if= checks if =r= "fell into" the current pocket - the size of which is given by =(slices i)=. If yes, return the index corresponding to that pocket; if not, check the next pocket/slice. *** Tying it all together: the =behave= function for ants The =behave= function below is the largest one, so it helps to keep in mind its main parts while diving into details: 1. =let= values - help with readability. 2. =Thread/sleep= - helps slow down ants in the UI display. 3. =dosync= - ensures ants behavior is transactional, all-or-nothing. 4. =if= branch: main logic for an ant, if ant has =:food= take it home, otherwise look for food. Also, consider the context of how =behave= is first used: within the main invocation at the end, there's the expression: src_clojure{(dorun (map #(send-off % behave) ants))} So the =behave= function is called on every ant agent via the [[http://clojuredocs.org/clojure_core/clojure.core/send-off][send-off function]], which is how Clojure dispatches potentially blocking actions to agents. And there certainly are potentially blocking actions when using =behave=, since ants may try to move into the same cell, try to acquire the same food, etc. #+name ant-agent-behave #+BEGIN_SRC clojure :exports code :results silent :session s1 (defn behave "The main function for the ant agent." [loc] (let [p (place loc) ant (:ant @p) ahead (place (delta-location loc (:dir ant))) ahead-left (place (delta-location loc (dec (:dir ant)))) ahead-right (place (delta-location loc (inc (:dir ant)))) places [ahead ahead-left ahead-right]] ;; Old way of Java interop: (. Thread (sleep ant-sleep-ms)) ;; New idiomatic way is, (Thread/sleep ant-sleep-ms) (dosync (when running (send-off *agent* #'behave)) (if (:food ant) ;; Then take food home: (cond (:home @p) (-> loc drop-food (turn 4)) (and (:home @ahead) (not (:ant @ahead))) (move loc) :else (let [ranks (merge-with + (rank-by (comp #(if (:home %) 1 0) deref) places) (rank-by (comp :pher deref) places))] (([move #(turn % -1) #(turn % 1)] (wrand [(if (:ant @ahead) 0 (ranks ahead)) (ranks ahead-left) (ranks ahead-right)])) loc))) ;; No food, go foraging: (cond (and (pos? (:food @p)) (not (:home @p))) (-> loc take-food (turn 4)) (and (pos? (:food @ahead)) (not (:home @ahead)) (not (:ant @ahead))) (move loc) :else (let [ranks (merge-with + (rank-by (comp :food deref) places) (rank-by (comp :pher deref) places))] (([move #(turn % -1) #(turn % 1)] (wrand [(if (:ant @ahead) 0 (ranks ahead)) (ranks ahead-left) (ranks ahead-right)])) loc))))))) #+END_SRC **** The =let= values The =let= values: quite straightforward, just note the twist in how =behave= receives a cell/location as its argument, not an ant (which an OO-centric design might expect). **** The only JVM/concurrency leakage: =Thread/sleep= The src_clojure{(. Thread (sleep ant-sleep-ms))}, or src_clojure{(Thread/sleep ant-sleep-ms)} call is our first encounter with [[http://clojure.org/java_interop][Clojure's Java Interop]]. - The first version uses [[http://clojure.org/java_interop#Java Interop-The Dot special form][the dot special form]] and in particular, the src_clojure{(. Classname-symbol (method-symbol args*))} format, with =Thread= as the Classname-symbol, and =sleep= as the method-symbol. - However, outside of macros, the idiomatic form for accessing method members is the second form, src_clojure{(Classname/staticMethod args*)} - Beyond syntax, the point of this expression is to slow down an ant (one ant-agent per thread) between their movements, so you can see in the UI what they're doing, and they'll appear more realistic. But more interesting still: in this highly concurrent program, the =sleep= expression is about the *only explicit reference to threads* in the entire code, i.e. one of the very few "leaky abstractions" hinting at Clojure's use of underlying JVM concurrency constructs. Besides this call, there are no locks, and no explicit thread allocations. **** The main =dosync= call Next, let's look at what's going on within the =dosync= transaction. ***** Repeating asynchronously, without looping The first expression is: src_clojure{(when running (send-off *agent* #'behave))} Initially this may seem strange; aren't we in the =behave= function because =send-off= already called it before entering it? Won't this just loop uselessly, not hitting the core =if= code below? Not quite: - Instead, =send-off= adds another execution of =behave= to the current agent's *queue* of work/functions, and immediately returns. - The current agent is referenced by the asterisk-surrounded ~*agent*~ which Clojure dynamically binds to the current active agent on a thread-local basis. - Thus after finishing this call of =behave= the ant will do another action (execute =behave= again), and another, and so on. No explicit looping, just *queue and repeat*. Also, note the ~#'~ sharp-quote, before =behave=; this is a Clojure Var, one of Clojure's mutable reference types. It's just syntactic sugar for =(var behave)=. Invoking a Var referring to a function is the same as invoking the function itself...so why bother with it? I don't know; here's what I could find: - Besides Clojure docs, this SO thread also suggests there's no difference, "Apply a =var= is the same as applying the value store in the =var=." http://stackoverflow.com/questions/9760480/in-clojure-difference-between-function-quoted-function-and-sharp-quote-functio - Maybe the #' prefix on =behave= causes the current thread's value of the function (with the current ant/location) to be sent to the queue? NO/unlikely. If it was mean to be a dynamic var, it would have asterisks around it like =*agent*=. Why use =send-off= instead of =send= ? - [[http://stackoverflow.com/questions/1646351/what-is-the-difference-between-clojures-send-and-send-off-functions-with-re][send vs. send-off]] - =send= uses threadpool of fixed size which has low switching overhead but blocking can dry up the threadpool. By contrast, =send-off= uses a dynamic threadpool and blocking is tolerated - and that's the right approach here as ant contention for the same location/food can certainly cause (temporary) blocking. - http://stackoverflow.com/questions/5964997/clojure-agent-question-using-send-off ***** Determining what the ant does next Finally, the ant's logic for what to do next is in the large =if= expression. The code looks dense but at the top level it's just a binary choice: + If the ant has food, take it home; the =cond= specifies 3 sub-cases: 1. At a home cell, drop the food and turn around 180 degrees, to exit home for more food. 2. If a home cell is ahead, move to it. 3. Otherwise, do a ranking of cells ahead (=places= has the cells =ahead=, =ahead-left=, =ahead-right=) per presence of pheromones, or home, and then randomly select from those 3 cells per their ranking/weighting. ** World behavior: pheromone evaporation #+BEGIN_SRC clojure :exports code :results silent :session s1 (defn evaporate "Causes all the pheromones to evaporate a bit." [] (dorun (for [x (range dim) y (range dim)] (dosync (let [p (place [x y])] (alter p assoc :pher (* evap-rate (:pher @p)))))))) #+END_SRC For a bit of realism and a cleaner UI/visual, it's useful to have the ants' pheromones diminish and evaporate from the world over time. The =evaporate= function fulfills that requirement: + It takes no arguments, it will work over the entire world/board of cells, accessed via the tuples of =x= and =y=. + The =[[http://clojuredocs.org/clojure_core/clojure.core/dorun][dorun]]= function takes a lazy collection/sequence (here, that of the =for= expression) and forces the realization of that collection for its side effects, discarding any returned values. - It's unlike the similarly-named =doall= where we do care about the values. - And it's unlike =doseq=, which is like Clojure's =for= but runs immediately and does not collect the results. + =dosync= is used as before, for lock-free updating of a =place= cell. Here, the desired side-effect/"mutation" is to update the =:pher= value at the =place= cell with a lower number. We'll see shortly that =evaporate= will run every second, a process that (like the ants) will be handled asynchronously using a Clojure agent. * The UI The user interface for the ants relies heavily on Clojure's Java inter-operation capabilities. But as we'll see, it's more than just wrapping calls to Java. ** Using the Java AWT #+BEGIN_SRC clojure :exports code :results silent :session s1 (import '(java.awt Color Graphics Dimension) '(java.awt.image BufferedImage) '(javax.swing JPanel JFrame)) #+END_SRC The =import= pulls in classes from [[http://docs.oracle.com/javase/6/docs/api/java/awt/package-summary.html][Java's Abstract Window Toolkit]] (AWT) package, and from the Java Swing package. (Aside: curious [[http://stackoverflow.com/questions/727844/javax-vs-java-package][why Swing is in the =javax= namespace]]?) Assuming unfamiliarity with Java Swing, let's describe the classes used: + The =[[http://docs.oracle.com/javase/6/docs/api/java/awt/Color.html][Color]]= class encapsulates a color in the standard RGB color space. In the code below, its usage as a constructor for a color instance follows several arities: - 4 integer arguments: r, g, b, and a for the alpha/transparency (0 transparent, 255 opaque) - 3 integer arguments: r g b - 1 argument: not a constructor call, but an access of a predefined static =Color= field by name, returning the color in the RGB color space. + The =[[http://docs.oracle.com/javase/6/docs/api/java/awt/Graphics.html][Graphics]]= class is an abstract base class for all graphics contexts, i.e. a =Graphics= instance holds the current state data needed for rendering it: the =[[http://docs.oracle.com/javase/6/docs/api/java/awt/Component.html][Component]]= object on which to draw, the current clip, color, and font, etc. Below, we'll see that the Clojure functions that take a =Graphics= instance as an argument: - =fill-cell= - =render-ant= - =render-place= - =render= ...all do some kind of rendering/drawing. + The =[[http://docs.oracle.com/javase/6/docs/api/java/awt/Dimension.html][Dimension]]= class encapsulates the integer width and height of a component. This class is used just once below, in setting the size of the panel of the UI. + =[[http://docs.oracle.com/javase/6/docs/api/java/awt/image/BufferedImage.html][BufferedImage]]= class is needed for raster image data; below, the =render= function uses it to paint the background panel. + The =[[http://docs.oracle.com/javase/1.4.2/docs/api/javax/swing/JPanel.html][JPanel]]= class is the generic "lightweight" UI container in Java Swing (seems like the =div= element in HTML). Below, it's used just once for the main display. + The =[[http://docs.oracle.com/javase/1.4.2/docs/api/javax/swing/JFrame.html][JFrame]]= class creates a top-level window (w/ title and border) in Swing; it's used just once below for the main ants UI window. ** Functions to render the board and the ants Each discrete cell on the world board is a square matrix of pixels; with an odd number of pixels chosen, we can have a central position: #+BEGIN_SRC clojure :exports code :results silent :session s1 (def scale 5) ; A world cell is 5x5 pixels. #+END_SRC By default, cells are empty; drawing cells having food or ant-deposited pheromones is done by filling with symbolic colors - here by running the Java methods =setColor= and =fillRect=: #+BEGIN_SRC clojure :exports code :results silent :session s1 (defn fill-cell [#^Graphics g x y c] (doto g (.setColor c) (.fillRect (* x scale) (* y scale) scale scale))) #+END_SRC Note the use of the =[[http://clojuredocs.org/clojure_core/clojure.core/doto][doto]]= function here and in many places below: in Java, procedural mutation of a newly constructed instance is common for initialization. Clojure's =doto= function is meant to be more concise in specifying the target object just once, and then methods/setters acting on it and then returning it, implicitly. Drawing an ant: the graphical appearance of an ant is just a (5-pixel long) line pointing in one of the 8 cardinal directions, of two different colors (having food or not): #+BEGIN_SRC clojure :exports code :results silent :session s1 (defn render-ant [ant #^Graphics g x y] (let [black (. (new Color 0 0 0 255) (getRGB)) gray (. (new Color 100 100 100 255) (getRGB)) red (. (new Color 255 0 0 255) (getRGB)) [hx hy tx ty] ({0 [2 0 2 4] ; Up/North pointing 1 [4 0 0 4] 2 [4 2 0 2] 3 [4 4 0 0] 4 [2 4 2 0] ; Down/South 5 [0 4 4 0] 6 [0 2 4 2] 7 [0 0 4 4]} (:dir ant))] (doto g (.setColor (if (:food ant) (new Color 255 0 0 255) (new Color 0 0 0 255))) (.drawLine (+ hx (* x scale)) (+ hy (* y scale)) (+ tx (* x scale)) (+ ty (* y scale)))))) #+END_SRC Note the cleverly concise destructuring for the start and end drawing coordinates, needed in AWT's =[[http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/Graphics.html#drawLine%28int,%20int,%20int,%20int%29][drawLine]]= method. If a cell in the ants' world is not empty, it has one or more of three things present: pheromone, food, or an ant. The =render-place= function updates the cell's appearance accordingly: #+BEGIN_SRC clojure :exports code :results silent :session s1 (defn render-place [g p x y] (when (pos? (:pher p)) (fill-cell g x y (new Color 0 255 0 (int (min 255 (* 255 (/ (:pher p) pher-scale))))))) (when (pos? (:food p)) (fill-cell g x y (new Color 255 0 0 (int (min 255 (* 255 (/ (:food p) food-scale))))))) (when (:ant p) (render-ant (:ant p) g x y))) #+END_SRC Finally, the =render= function ties everything together: initializing the UI/window appearance by applying =render=place= to every cell, and also drawing the home space of the ants. Note the heavy usage of the dot special form: the UI code relies heavily on Java, though Clojure's =for= and =doto= help us avoid Java boilerplate and stay concise: #+BEGIN_SRC clojure :exports code :results silent :session s1 (defn render [g] (let [v (dosync (apply vector (for [x (range dim) y (range dim)] @(place [x y])))) img (new BufferedImage (* scale dim) (* scale dim) (. BufferedImage TYPE_INT_ARGB)) bg (. img (getGraphics))] ;; First paint everything white, on the bg instance: (doto bg (.setColor (. Color white)) (.fillRect 0 0 (. img (getWidth)) (. img (getHeight)))) (dorun (for [x (range dim) y (range dim)] (render-place bg (v (+ (* x dim) y)) x y))) ;; Draw the home space of the ants: (doto bg (.setColor (. Color blue)) (.drawRect (* scale home-offset) (* scale home-offset) (* scale nants-sqrt) (* scale nants-sqrt))) (. g (drawImage img 0 0 nil)) (. bg (dispose)))) ; Finished using Graphics object, release it. #+END_SRC ** Setting the scene, then updating it continually Almost ready to begin our simulation; we need to setup some additional elements per AWT conventions: the main UI =panel= where visual changes take place, the top-level window =frame=, and an =animator= agent that continually updates the visual elements: #+BEGIN_SRC clojure :exports code :results silent :session s1 (def panel (doto (proxy [JPanel] [] (paint [g] (render g))) (.setPreferredSize (new Dimension (* scale dim) (* scale dim))))) (def frame (doto (new JFrame) (.add panel) .pack .show)) (def animator (agent nil)) #+END_SRC *** Animation, panel-by-panel Now for bringing the static starting "picture" to life - like the cartoons of old, the =animation= function will "draw" the next state of the main panel displaying the ants. Below, Hickey uses the queue-itself-then-run, again-and-again code pattern we've seen before (above, in updating an ant's state): #+BEGIN_SRC clojure :exports code :results silent :session s1 (defn animation [x] (when running (send-off *agent* #'animation)) (. panel (repaint)) (. Thread (sleep animation-sleep-ms)) nil) #+END_SRC Finally, we need another agent to handle one more time-track of changes: evaporation, using the =evaporate= function defined above. #+BEGIN_SRC clojure :exports code :results silent :session s1 (def evaporator (agent nil)) (defn evaporation [x] (when running (send-off *agent* #'evaporation)) (evaporate) (. Thread (sleep evap-sleep-ms)) nil) #+END_SRC * Running the Program ** The =project.clj= file When you tangle this file, the local =project.clj= file will be created alongside =ants.clj=. Assuming you've installed the excellent [[http://leiningen.org/][Leiningen]], you'd then: 1. Enter =lein deps= at the shell prompt to get dependencies. 2. Then you can start a REPL with =lein repl=, from which you can start the simulator (see next section). #+BEGIN_SRC clojure :tangle project.clj (defproject literate-ants "1.0.0-SNAPSHOT" :description "This is a literate version of: Rich Hickey's Ants simulator, demonstrating Clojure's concurrency support." :dev-dependencies [] :dependencies [[org.clojure/clojure "1.5.1"]] ) #+END_SRC ** Running the simulator At the REPL, you can enter the entire =do= expression below, or try each line within it separately: #+BEGIN_SRC clojure :tangle no (do (load-file "./literate-ants.clj") (def ants (setup)) (send-off animator animation) (dorun (map #(send-off % behave) ants)) (send-off evaporator evaporation)) #+END_SRC Either way you'll see a new window appear with a white background, blue square representing the ants' home, red squares of food, black or red (w/ food) moving lines representing each ant, and green squares for pheromones in various concentrations. A lot happening concurrently, with no locks, and beautifully concise code - welcome to Clojure! ** Unused :ARCHIVE:NOEXPORT: #+BEGIN_SRC clojure :exports code :results silent :session s1 :tangle no (comment ;demo (load-file "/Users/rich/dev/clojure/ants.clj") (def ants (setup)) (send-off animator animation) (dorun (map #(send-off % behave) ants)) (send-off evaporator evaporation) ) #+END_SRC #+name: ants #+BEGIN_SRC clojure :tangle no :exports none :noweb yes <> <> <> #+end_src ================================================ FILE: 02-minimal-clojure-app/clojure-app-skeleton.org ================================================ #+TITLE: Clojure App Skeleton, using Org literate programming #+AUTHOR: Kai Wu #+EMAIL: k@limist.com #+LANGUAGE: en #+STARTUP: align overview indent fold nodlcheck hidestars oddeven lognotestate #+PROPERTY: mkdirp yes * Meta: this file, Clojure + Org → LP, etc. You're looking at a literate programming (LP) file, specifically an [[http://orgmode.org][Org mode]] formatted file combining both documentation (Org's structured markup) and code blocks (Clojure code). For best results please *use Emacs 24.3 or later to view this* =.org= *file*. If you're looking at this on Github.com, STOP - the rendering there is neither complete nor correct! ** The *benefits* of LP using Emacs + Org 1. Docs matter, a lot. With LP, documentation is integral to development, never an afterthought. - For all but small throwaway systems, you're likely keeping a separate file of development notes already; LP would integrate that. 2. With one LP file, avoid the incidental/inessential complexity of the filesystem: avoid context-switch overhead moving between files, and sidestep your language's imposed filesystem structure. 3. Org rocks for prose: - Org's plain-text *markup is lightweight*, yet more powerful than Markdown, and cleaner than rST. - The *structural editing* provided by Org documents lets you organize your thoughts/writing/code very quickly. With good structure even major revisions are easy. - Org's exporter lets your *write-once, express-many-times*: you can export an Org file to HTML (e.g. for blogging) or LaTeX (for serious publishing). - It's easy to version-control Org files. 4. Org rocks for code: - Each code block has flexible granularity: can be named and referred to; evaluated or not; have data sent in or exported; specify different REPL sessions; specify different target/tangled files. - Code blocks are syntax-highlighted. - Code blocks are ready to edit: jump to major-mode editing easily. - A single Org file can mix different languages. 5. Meta-development, manage complexity from a coherent perspective: a unified, single-file approach encourages holistic software development and exposition, in a natural order, using structure to enhance understanding. LP is not just documentation and code together: it's a *process and abstraction unifying the development lifecycle*: requirements, architecture, design, code, tests, deployment, and maintenance - can all be bound coherently in one active format. * Using this file ** Prerequisites 1. A recent version of Emacs, 24.3+. 2. Both org-mode (included w/ Emacs 24) and =clojure-mode= installed; use Emacs ELPA as needed. - Consider using an Emacs "starter package" that provides a good baseline, like [[http://batsov.com/prelude/][Emacs Prelude]] or [[http://overtone.github.io/emacs-live/][Emacs Live]]. Then if you start Emacs and load this file, you'll see it the way it's meant to be seen: as a multi-level, hierarchically organized and structured literate code file, w/ syntax-highlighted code blocks. ** Weaving and tangling To use the original Knuth terminology, this single file can be /woven/ into documentation, or /tangled/ to code. *** Weave/export, to documentation + To /weave/: the Org equivalent of /weaving/ is to export this file, typically to HTML or LaTeX/PDF. The keystroke is =C-c-e= i.e. hold down the Control key while pressing "c" then "e" to view the export options. - e.g. export this file to HTML with =CTRL-c-e h= or, to see it immediately in a browser window, =CTRL-c-e b=. + You don't have to export this file though; if/when you're comfy in Emacs, the Org format itself is great. *** Tangle, to code + To /tangle/: in Org, it's the same word/term. =C-c-v-t= will cause all designated code blocks in this file to appear in the filesystem. Here, the code blocks go to files and directories matching a new Clojure app, as would be produced by =lein new app the-project-name=. ** Other coolness - =SHIFT-TAB= will *cycle* the display: top-level headings only, all headings, or fully-expanded. - Within a code block, =CTRL-c= ='= will open a buffer to edit the code. For full power, be sure =clojure-mode=, =paredit=, and =nrepl= are installed. - Org docs: see [[http://orgmode.org/org.html][main documentation]], especially sections on [[http://orgmode.org/org.html#Document-Structure][structure]], [[http://orgmode.org/org.html#Hyperlinks][links]], [[http://orgmode.org/org.html#Markup][markup]], and [[http://orgmode.org/org.html#Working-With-Source-Code][literate programming]] features. * Project meta ** Project definition #+BEGIN_SRC clojure :tangle project.clj (defproject skeleton-app "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.5.1"]] :main skeleton-app.core :profiles {:uberjar {:aot :all}}) #+END_SRC ** The README It would be nice to auto-generate the README.md from selected parts of this =org= file; TBD how. #+BEGIN_SRC markdown :tangle README.md # skeleton-app FIXME: description ## Installation Download from http://example.com/FIXME. ## Usage FIXME: explanation $ java -jar skeleton-app-0.1.0-standalone.jar [args] ## Options FIXME: listing of options this app accepts. ## Examples ... ### Bugs ... ### Any Other Sections ### That You Think ### Might be Useful ## License Copyright © 2013 FIXME Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version. #+END_SRC ** License notice #+BEGIN_SRC text :tangle LICENSE THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. "Contributor" means any person or entity that distributes the Program. "Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 3. REQUIREMENTS A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: a) it complies with the terms and conditions of this Agreement; and b) its license agreement: i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. When the Program is made available in source code form: a) it must be made available under this Agreement; and b) a copy of this Agreement must be included with each copy of the Program. Contributors may not remove or alter any copyright notices contained within the Program. Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor tocontrol, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. This Agreement is governed by the laws of the State of Washington and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. #+END_SRC * Requirements ** The user-story Max touches the GO button using this app, and justly rebuilds the world with Clojure. ** Non-user-visible requirements 1. Logging 2. Security * Architecture 1. Foo back-end 2. Bar front-end - Probably use a Pedestal App client | Part | Description | Alternatives | |------+-----------------------------+---------------| | Bar | Bar presents via D3 charts | Flash? Nein!! | | Foo | Foo has the data we worship | | | | | | * Design ** Foo design ** Bar design * Source-code The default =lein new app *= command just produces two files with actual Clojure code. ** core #+NAME: core #+BEGIN_SRC clojure :tangle src/skeleton_app/core.clj (ns skeleton-app.core (:gen-class)) (defn -main "I don't do a whole lot ... yet." [& args] (println "Hello, World!")) #+END_SRC *** Tests #+BEGIN_SRC clojure :tangle test/skeleton_app/core_test.clj (ns skeleton-app.core-test (:require [clojure.test :refer :all] [skeleton-app.core :refer :all])) (deftest a-test (testing "FIXME, I fail." (is (= 0 1)))) #+END_SRC ================================================ FILE: 02-minimal-clojure-project/clojure-default-skeleton.org ================================================ #+TITLE: Clojure Default/Project Skeleton, Using Org Literate Programming #+AUTHOR: Kai Wu #+EMAIL: k@limist.com #+LANGUAGE: en #+STARTUP: align overview indent fold nodlcheck hidestars oddeven lognotestate #+PROPERTY: mkdirp yes * Meta: this file, Clojure + Org → LP, etc. You're looking at a literate programming (LP) file, specifically an [[http://orgmode.org][Org mode]] formatted file combining both documentation (Org's structured markup) and code blocks (Clojure code). For best results please *use Emacs 24.3 or later to view this* =.org= *file*. If you're looking at this on Github.com, STOP - the rendering there is neither complete nor correct! ** The *benefits* of LP using Emacs + Org 1. Docs matter, a lot. With LP, documentation is integral to development, never an afterthought. - For all but small throwaway systems, you're likely keeping a separate file of development notes already; LP would integrate that. 2. With one LP file, avoid the incidental/inessential complexity of the filesystem: avoid context-switch overhead moving between files, and sidestep your language's imposed filesystem structure. 3. Org rocks for prose: - Org's plain-text *markup is lightweight*, yet more powerful than Markdown, and cleaner than rST. - The *structural editing* provided by Org documents lets you organize your thoughts/writing/code very quickly. With good structure even major revisions are easy. - Org's exporter lets your *write-once, express-many-times*: you can export an Org file to HTML (e.g. for blogging) or LaTeX (for serious publishing). - It's easy to version-control Org files. 4. Org rocks for code: - Each code block has flexible granularity: can be named and referred to; evaluated or not; have data sent in or exported; specify different REPL sessions; specify different target/tangled files. - Code blocks are syntax-highlighted. - Code blocks are ready to edit: jump to major-mode editing easily. - A single Org file can mix different languages. 5. Meta-development, manage complexity from a coherent perspective: a unified, single-file approach encourages holistic software development and exposition, in a natural order, using structure to enhance understanding. LP is not just documentation and code together: it's a *process and abstraction unifying the development lifecycle*: requirements, architecture, design, code, tests, deployment, and maintenance - can all be bound coherently in one active format. * Using this file ** Prerequisites 1. A recent version of Emacs, 24.3+. 2. Both org-mode (included w/ Emacs 24) and =clojure-mode= installed; use Emacs ELPA as needed. - Consider using an Emacs "starter package" that provides a good baseline, like [[http://batsov.com/prelude/][Emacs Prelude]] or [[http://overtone.github.io/emacs-live/][Emacs Live]]. Then if you start Emacs and load this file, you'll see it the way it's meant to be seen: as a multi-level, hierarchically organized and structured literate code file, w/ syntax-highlighted code blocks. ** Weaving and tangling To use the original Knuth terminology, this single file can be /woven/ into documentation, or /tangled/ to code. *** Weave/export, to documentation + To /weave/: the Org equivalent of /weaving/ is to export this file, typically to HTML or LaTeX/PDF. The keystroke is =C-c-e= i.e. hold down the Control key while pressing "c" then "e" to view the export options. - e.g. export this file to HTML with =CTRL-c-e h= or, to see it immediately in a browser window, =CTRL-c-e b=. + You don't have to export this file though; if/when you're comfy in Emacs, the Org format itself is great. *** Tangle, to code + To /tangle/: in Org, it's the same word/term. =C-c-v-t= will cause all designated code blocks in this file to appear in the filesystem. Here, the code blocks go to files and directories matching a new Clojure app, as would be produced by =lein new app the-project-name=. ** Other coolness - =SHIFT-TAB= will *cycle* the display: top-level headings only, all headings, or fully-expanded. - Within a code block, =CTRL-c= ='= will open a buffer to edit the code. For full power, be sure =clojure-mode=, =paredit=, and =nrepl= are installed. - Org docs: see [[http://orgmode.org/org.html][main documentation]], especially sections on [[http://orgmode.org/org.html#Document-Structure][structure]], [[http://orgmode.org/org.html#Hyperlinks][links]], [[http://orgmode.org/org.html#Markup][markup]], and [[http://orgmode.org/org.html#Working-With-Source-Code][literate programming]] features. * Project meta ** Project definition #+BEGIN_SRC clojure :tangle project.clj (defproject default-skeleton "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.5.1"]]) #+END_SRC ** The README It would be nice to auto-generate the README.md from selected parts of this =org= file; TBD how. #+BEGIN_SRC markdown :tangle README.md # default-skeleton FIXME: description ## Installation Download from http://example.com/FIXME. ## Usage FIXME: explanation $ java -jar default-skeleton-0.1.0-standalone.jar [args] ## Options FIXME: listing of options this app accepts. ## Examples ... ### Bugs ... ### Any Other Sections ### That You Think ### Might be Useful ## License Copyright © 2013 FIXME Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version. #+END_SRC ** License notice #+BEGIN_SRC text :tangle LICENSE THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. "Contributor" means any person or entity that distributes the Program. "Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 3. REQUIREMENTS A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: a) it complies with the terms and conditions of this Agreement; and b) its license agreement: i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. When the Program is made available in source code form: a) it must be made available under this Agreement; and b) a copy of this Agreement must be included with each copy of the Program. Contributors may not remove or alter any copyright notices contained within the Program. Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor tocontrol, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. This Agreement is governed by the laws of the State of Washington and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. #+END_SRC * Requirements ** The user-story Import this library, and enjoy magical powers. ** Non-user-visible requirements 1. Logging 2. Security * Architecture 1. Data processing, 2. Data storage 3. Long-running service 4. API | Part | Description | Alternatives | |---------+-----------------------------------------------+------------------------| | API | How other Clojure code accesses this library. | | | Service | Should execute data processing periodically. | Manual, cmd-line start | | | | | * Design ** API design - Always keep it minimal at first! You can add later, but removing stuff is a pain. - Don't require the client/user of the API to do anything the library could do. - See Joshua Bloch's talk, [[http://limist.com/coding/talk-notes-how-to-design-a-good-api-and-why-it-matters-bloch.html][How to Design an Good API and Why It Matters]] * Source-code The =lein new default my-new-library= command just produces two files with actual Clojure code. ** core #+NAME: core #+BEGIN_SRC clojure :tangle src/default_skeleton/core.clj (ns default-skeleton.core) (defn foo "I don't do a whole lot." [x] (println x "Hello, World!")) #+END_SRC *** Tests #+BEGIN_SRC clojure :tangle test/default_skeleton/core_test.clj (ns default-skeleton.core-test (:require [clojure.test :refer :all] [default-skeleton.core :refer :all])) (deftest a-test (testing "FIXME, I fail." (is (= 0 1)))) #+END_SRC ================================================ FILE: 03-pedestal-app/pedestal-app-skeleton.org ================================================ #+TITLE: Pedestal App, in Org literate programming form #+AUTHOR: Kai Wu #+EMAIL: k@limist.com #+STARTUP: align overview indent fold nodlcheck hidestars oddeven lognotestate #+PROPERTY: mkdirp yes * Project meta ** Project definition #+BEGIN_SRC clojure :tangle project.clj (defproject skeleton-app "0.0.1-SNAPSHOT" :description "FIXME: write description" :dependencies [[org.clojure/clojure "1.5.1"] [org.clojure/clojurescript "0.0-1586"] [domina "1.0.1"] [ch.qos.logback/logback-classic "1.0.7" :exclusions [org.slf4j/slf4j-api]] [io.pedestal/pedestal.app "0.1.8"] [io.pedestal/pedestal.app-tools "0.1.8"]] :profiles {:dev {:source-paths ["dev"]}} :min-lein-version "2.0.0" :source-paths ["app/src" "app/templates"] :resource-paths ["config"] :target-path "out/" :aliases {"dumbrepl" ["trampoline" "run" "-m" "clojure.main/main"]}) #+END_SRC * Main application ** Behavior #+BEGIN_SRC clojure :tangle app/src/skeleton_app/behavior.clj (ns ^:shared skeleton-app.behavior (:require [clojure.string :as string] [io.pedestal.app.messages :as msg])) ;; While creating new behavior, write tests to confirm that it is ;; correct. For examples of various kinds of tests, see ;; test/skeleton_app/test/behavior.clj. (defn set-value-transform [old-value message] (:value message)) (def example-app {;; There are currently 2 versions (formats) for dataflow ;; descritpion: the original version (version 1) and the current ;; version (version 2). If the version is not specified, the ;; descritpion will be assumed to be version 1 and an attempt ;; will be made to convert it to version 2. :version 2 :transform [[:set-value [:greeting] set-value-transform]]}) ;; Once this behavior works, run the Data UI and record ;; rendering data which can be used while working on a custom ;; renderer. Rendering involves making a template: ;; ;; app/templates/skeleton-app.html ;; ;; slicing the template into pieces you can use: ;; ;; app/src/skeleton_app/html_templates.cljs ;; ;; and then writing the rendering code: ;; ;; app/src/skeleton_app/rendering.cljs (comment ;; The examples below show the signature of each type of function ;; that is used to build a behavior dataflow. ;; transform (defn example-transform [old-state message] ;; returns new state ) ;; derive (defn example-derive [old-state inputs] ;; returns new state ) ;; emit (defn example-emit [inputs] ;; returns rendering deltas ) ;; effect (defn example-effect [inputs] ;; returns a vector of messages which effect the outside world ) ;; continue (defn example-continue [inputs] ;; returns a vector of messages which will be processed as part of ;; the same dataflow transaction ) ;; dataflow description reference {:transform [[:op [:path] example-transform]] :derive #{[#{[:in]} [:path] example-derive]} :effect #{[#{[:in]} example-effect]} :continue #{[#{[:in]} example-continue]} :emit [[#{[:in]} example-emit]]} ) #+END_SRC ** HTML templates #+BEGIN_SRC clojure :tangle app/src/skeleton_app/html_templates.clj (ns skeleton-app.html-templates (:use [io.pedestal.app.templates :only [tfn dtfn tnodes]])) (defmacro skeleton-app-templates [] ;; Extract the 'hello' template from the template file skeleton-app.html. ;; The 'dtfn' function will create a dynamic template which can be ;; updated after it has been attached to the DOM. ;; ;; To see how this template is used, refer to ;; ;; app/src/skeleton_app/rendering.cljs ;; ;; The last argument to 'dtfn' is a set of fields that should be ;; treated as static fields (may only be set once). Dynamic templates ;; use ids to set values so you cannot dynamically set an id. {:skeleton-app-page (dtfn (tnodes "skeleton-app.html" "hello") #{:id})}) ;; Note: this file will not be reloaded automatically when it is changed. #+END_SRC ** Rendering #+BEGIN_SRC clojurescript :tangle app/src/skeleton_app/rendering.cljs (ns skeleton-app.rendering (:require [domina :as dom] [io.pedestal.app.render.push :as render] [io.pedestal.app.render.push.templates :as templates] [io.pedestal.app.render.push.handlers.automatic :as d]) (:require-macros [skeleton-app.html-templates :as html-templates])) ;; Load templates. (def templates (html-templates/skeleton-app-templates)) ;; The way rendering is handled below is the result of using the ;; renderer provided in `io.pedestal.app.render`. The only requirement ;; for a renderer is that it must implement the Renderer protocol. ;; ;; This renderer dispatches to rendering functions based on the ;; requested change. See the render-config table below. Each render ;; function takes three arguments: renderer, render operation and a ;; a transmitter which is used to send data back to the application's ;; behavior. This example does not use the transmitter. (defn render-page [renderer [_ path] transmitter] (let [;; The renderer that we are using here helps us map changes to ;; the UI tree to the DOM. It keeps a mapping of paths to DOM ;; ids. The `get-parent-id` function will return the DOM id of ;; the parent of the node at path. If the path is [:a :b :c] ;; then this will find the id associated with [:a :b]. The ;; root node [] is configured when we created the renderer. parent (render/get-parent-id renderer path) ;; Use the `new-id!` function to associate a new id to the ;; given path. With two arguments, this function will generate ;; a random unique id. With three arguments, the given id will ;; be associated with the given path. id (render/new-id! renderer path) ;; Get the dynamic template named :skeleton-app-page ;; from the templates map. The `add-template` function will ;; associate this template with the node at ;; path. `add-template` returns a function that can be called ;; to generate the initial HTML. html (templates/add-template renderer path (:skeleton-app-page templates))] ;; Call the `html` function, passing the initial values for the ;; template. This returns an HTML string which is then added to ;; the DOM using Domina. (dom/append! (dom/by-id parent) (html {:id id :message ""})))) (defn render-message [renderer [_ path _ new-value] transmitter] ;; This function responds to a :value event. It uses the ;; `update-t` function to update the template at `path` with the new ;; values in the passed map. (templates/update-t renderer path {:message new-value})) ;; The data structure below is used to map rendering data to functions ;; which handle rendering for that specific change. This function is ;; referenced in config/config.clj and must be a function in order to ;; be used from the tool's "render" view. (defn render-config [] [;; All :node-create deltas for the node at :greeting will ;; be rendered by the `render-page` function. The node name ;; :greeting is a default name that is used when we don't ;; provide our own combines and emits. To name your own nodes, ;; create a custom combine or emit in the application's behavior. [:node-create [:greeting] render-page] ;; All :node-destroy deltas for this path will be handled by the ;; library function `d/default-exit`. [:node-destroy [:greeting] d/default-exit] ;; All :value deltas for this path will be handled by the ;; function `render-message`. [:value [:greeting] render-message]]) ;; In render-config, paths can use wildcard keywords :* and :**. :* ;; means exactly one segment with any value. :** means 0 or more ;; elements. #+END_SRC ** Back-end/external communications #+BEGIN_SRC clojurescript :tangle app/src/skeleton_app/services.cljs (ns skeleton-app.services) ;; The services namespace responsible for communicating with back-end ;; services. It receives messages from the application's behavior, ;; makes requests to services and sends responses back to the ;; behavior. ;; ;; This namespace will usually contain a function which can be ;; configured to receive effect events from the behavior in the file ;; ;; app/src/skeleton_app/start.cljs ;; ;; After creating a new application, set the effect handler function ;; to receive effect ;; ;; (app/consume-effect app services-fn) ;; ;; A very simple example of a services function which echos all events ;; back to the behavior is shown below (comment ;; The services implementation will need some way to send messages ;; back to the application. The queue passed to the services function ;; will convey messages to the application. (defn echo-services-fn [message queue] (put-message queue message)) ) ;; During development, it is helpful to implement services which ;; simulate communication with the real services. This implementaiton ;; can be placed in the file ;; ;; app/src/skeleton_app/simulated/services.cljs ;; #+END_SRC ** Start #+BEGIN_SRC clojurescript :tangle app/src/skeleton_app/start.cljs (ns skeleton-app.start (:require [io.pedestal.app.protocols :as p] [io.pedestal.app :as app] [io.pedestal.app.render.push :as push-render] [io.pedestal.app.render :as render] [io.pedestal.app.messages :as msg] [skeleton-app.behavior :as behavior] [skeleton-app.rendering :as rendering])) ;; In this namespace, the application is built and started. (defn create-app [render-config] (let [;; Build the application described in the map ;; 'behavior/example-app'. The application is a record which ;; implements the Receiver protocol. app (app/build behavior/example-app) ;; Create the render function that will be used by this ;; application. A renderer function takes two arguments: the ;; application model deltas and the input queue. ;; ;; On the line below, we create a renderer that will help in ;; mapping UI data to the DOM. ;; ;; The file, app/src/skeleton_app/rendering.cljs contains ;; the code which does all of the rendering as well as the ;; render-config which is used to map renderering data to ;; specific functions. render-fn (push-render/renderer "content" render-config render/log-fn) ;; This application does not yet have services, but if it did, ;; this would be a good place to create it. ;; services-fn (fn [message input-queue] ...) ;; Configure the application to send all rendering data to this ;; renderer. app-model (render/consume-app-model app render-fn)] ;; If services existed, configure the application to send all ;; effects there. ;; (app/consume-effect app services-fn) ;; ;; Start the application (app/begin app) ;; Send a message to the application so that it does something. (p/put-message (:input app) {msg/type :set-value msg/topic [:greeting] :value "Hello World!"}) ;; Returning the app and app-model from the main function allows ;; the tooling to add support for useful features like logging ;; and recording. {:app app :app-model app-model})) (defn ^:export main [] ;; config/config.clj refers to this namespace as a main namespace ;; for several aspects. A main namespace must have a no argument ;; main function. To tie into tooling, this function should return ;; the newly created app. (create-app (rendering/render-config))) #+END_SRC ** Simulated back-end #+BEGIN_SRC clojurescript :tangle app/src/skeleton_app/simulated/services.cljs (ns skeleton-app.simulated.services) ;; Implement services to simulate talking to back-end services #+END_SRC #+BEGIN_SRC clojurescript :tangle app/src/skeleton_app/simulated/start.cljs (ns skeleton-app.simulated.start (:require [io.pedestal.app.render.push.handlers.automatic :as d] [skeleton-app.start :as start] ;; This needs to be included somewhere in order for the ;; tools to work. [io.pedestal.app-tools.tooling :as tooling])) (defn ^:export main [] ;; Create an application which uses the data renderer. The :data-ui ;; aspect is configured to run this main function. See ;; ;; config/config.clj ;; (start/create-app d/data-renderer-config)) #+END_SRC * Assets #+BEGIN_SRC javascript :tangle app/assets/javascripts/xpath.js (function(){var ca=void(0);var da={targetFrame:ca,exportInstaller:false,useNative:true,useInnerText:true};var ea;if(window.jsxpath){ea=window.jsxpath;} else{var fa=document.getElementsByTagName('script');var ga=fa[fa.length-1];var ha=ga.src;ea={};var ia=ha.match(/\?(.*)$/);if(ia){var ja=ia[1].split('&');for(var i=0,l=ja.length;i=0){this.opera=true;} else if(ua.indexOf("Netscape")>=0){this.netscape=true;} else if(ua.indexOf("Mozilla/")==0){this.mozilla=true;} else{this.unknown=true;} if(ua.indexOf("Gecko/")>=0){this.gecko=true;} if(ua.indexOf("Win")>=0){this.windows=true;} else if(ua.indexOf("Mac")>=0){this.mac=true;} else if(ua.indexOf("Linux")>=0){this.linux=true;} else if(ua.indexOf("BSD")>=0){this.bsd=true;} else if(ua.indexOf("SunOS")>=0){this.sunos=true;}} else{ /*@cc_on @if(@_jscript)function jscriptVersion(){switch(@_jscript_version){case 3.0:return "4.0";case 5.0:return "5.0";case 5.1:return "5.01";case 5.5:return "5.5";case 5.6:if("XMLHttpRequest" in window)return "7.0";return "6.0";case 5.7:return "7.0";default:return true;}} if(@_win16||@_win32||@_win64){this.windows=true;this.trident=jscriptVersion();} else if(@_mac||navigator.platform.indexOf("Mac")>=0){this.mac=true;this.tasman=jscriptVersion();} if(/MSIE (\d+\.\d+)b?;/.test(ua)){this.ie=RegExp.$1;this['ie'+RegExp.$1.charAt(0)]=true;}@else @*/ if(/AppleWebKit\/(\d+(?:\.\d+)*)/.test(ua)){this.applewebkit=RegExp.$1;if(RegExp.$1.charAt(0)==4){this.applewebkit2=true;} else{this.applewebkit3=true;}} else if(typeof Components=="object"&&(/Gecko\/(\d{8})/.test(ua)||navigator.product=="Gecko"&&/^(\d{8})$/.test(navigator.productSub))){this.gecko=RegExp.$1;}/*@end @*/ if(typeof(opera)=="object"&&typeof(opera.version)=="function"){this.opera=opera.version();this['opera'+this.opera[0]+this.opera[2]]=true;} else if(typeof opera=="object"&&(/Opera[\/ ](\d+\.\d+)/.test(ua))){this.opera=RegExp.$1;} else if(this.ie){} else if(/Safari\/(\d+(?:\.\d+)*)/.test(ua)){this.safari=RegExp.$1;} else if(/NetFront\/(\d+(?:\.\d+)*)/.test(ua)){this.netfront=RegExp.$1;} else if(/Konqueror\/(\d+(?:\.\d+)*)/.test(ua)){this.konqueror=RegExp.$1;} else if(ua.indexOf("(compatible;")<0&&(/^Mozilla\/(\d+\.\d+)/.test(ua))){this.mozilla=RegExp.$1;if(/\([^(]*rv:(\d+(?:\.\d+)*).*?\)/.test(ua))this.mozillarv=RegExp.$1;if(/Firefox\/(\d+(?:\.\d+)*)/.test(ua)){this.firefox=RegExp.$1;} else if(/Netscape\d?\/(\d+(?:\.\d+)*)/.test(ua)){this.netscape=RegExp.$1;}} else{this.unknown=true;} if(ua.indexOf("Win 9x 4.90")>=0){this.windows="ME";} else if(/Win(?:dows)? ?(NT ?(\d+\.\d+)?|\d+|ME|Vista|XP)/.test(ua)){this.windows=RegExp.$1;if(RegExp.$2){this.winnt=RegExp.$2;} else switch(RegExp.$1){case "2000":this.winnt="5.0";break;case "XP":this.winnt="5.1";break;case "Vista":this.winnt="6.0";break;}} else if(ua.indexOf("Mac")>=0){this.mac=true;} else if(ua.indexOf("Linux")>=0){this.linux=true;} else if(/(\w*BSD)/.test(ua)){this.bsd=RegExp.$1;} else if(ua.indexOf("SunOS")>=0){this.sunos=true;}}};var Ea=function(Fa){var Ga=Ea.prototype;var Ha=Fa.match(Ga.regs.token);for(var i=0,l=Ha.length;i]=|(?![0-9-])[\w-]+:\*|\s+|./g,strip:/^\s/};Ea.prototype.peek=function(i){return this[this.index+(i||0)];};Ea.prototype.next=function(){return this[this.index++];};Ea.prototype.back=function(){this.index--;};Ea.prototype.empty=function(){return this.length<=this.index;};var Ia=function(Ja,Ka,La){this.node=Ja;this.position=Ka||1;this.last=La||1;};var Ma=function(){};Ma.prototype.number=function(Na){var Oa=this.evaluate(Na);if(Oa.isNodeSet)return Oa.number();return+Oa;};Ma.prototype.string=function(Pa){var Qa=this.evaluate(Pa);if(Qa.isNodeSet)return Qa.string();return ''+Qa;};Ma.prototype.bool=function(Ra){var Sa=this.evaluate(Ra);if(Sa.isNodeSet)return Sa.bool();return!!Sa;};var Ta=function(){};Ta.parsePredicates=function(Ua,Va){while(Ua.peek()=='['){Ua.next();if(Ua.empty()){throw Error('missing predicate expr');} var Wa=oa.parse(Ua);Va.predicate(Wa);if(Ua.empty()){throw Error('unclosed predicate expr');} if(Ua.next()!=']'){Ua.back();throw Error('bad token: '+Ua.next());}}};Ta.prototype=new Ma();Ta.prototype.evaluatePredicates=function(Xa,Ya){var Za,predicate,nodes,node,Xa,position,reverse;reverse=this.reverse;Za=this.predicates;Xa.sort();for(var i=Ya||0,l0=Za.length;i=l1;j--){Xa.del($a[j]);}} return Xa;};if(!window.BinaryExpr&&window.defaultConfig)window.BinaryExpr=null;oa=function(op,bb,cb,db){this.op=op;this.left=bb;this.right=cb;this.datatype=oa.ops[op][2];this.needContextPosition=bb.needContextPosition||cb.needContextPosition;this.needContextNode=bb.needContextNode||cb.needContextNode;if(this.op=='='){if(!cb.needContextNode&&!cb.needContextPosition&&cb.datatype!='nodeset'&&cb.datatype!='void'&&bb.quickAttr){this.quickAttr=true;this.attrName=bb.attrName;this.attrValueExpr=cb;} else if(!bb.needContextNode&&!bb.needContextPosition&&bb.datatype!='nodeset'&&bb.datatype!='void'&&cb.quickAttr){this.quickAttr=true;this.attrName=cb.attrName;this.attrValueExpr=bb;}}};oa.compare=function(op,eb,fb,gb,hb){var ib,lnodes,rnodes,nodes,nodeset,primitive;fb=fb.evaluate(hb);gb=gb.evaluate(hb);if(fb.isNodeSet&&gb.isNodeSet){lnodes=fb.list();rnodes=gb.list();for(var i=0,l0=lnodes.length;i':[4,function(Bb,Cb,Db){return oa.compare('>',function(a,b){return a>b},Bb,Cb,Db);},'boolean'],'<=':[4,function(Eb,Fb,Gb){return oa.compare('<=',function(a,b){return a<=b},Eb,Fb,Gb);},'boolean'],'>=':[4,function(Hb,Ib,Jb){return oa.compare('>=',function(a,b){return a>=b},Hb,Ib,Jb);},'boolean'],'=':[3,function(Kb,Lb,Mb){return oa.compare('=',function(a,b){return a==b},Kb,Lb,Mb);},'boolean'],'!=':[3,function(Nb,Ob,Pb){return oa.compare('!=',function(a,b){return a!=b},Nb,Ob,Pb);},'boolean'],'and':[2,function(Qb,Rb,Sb){return Qb.bool(Sb)&&Rb.bool(Sb);},'boolean'],'or':[1,function(Tb,Ub,Vb){return Tb.bool(Vb)||Ub.bool(Vb);},'boolean']};oa.parse=function(Wb){var op,precedence,info,expr,stack=[],index=Wb.index;while(true){if(Wb.empty()){throw Error('missing right expression');} expr=Aa.parse(Wb);op=Wb.next();if(!op){break;} info=this.ops[op];precedence=info&&info[0];if(!precedence){Wb.back();break;} while(stack.length&&precedence<=this.ops[stack[stack.length-1]][0]){expr=new oa(stack.pop(),stack.pop(),expr);} stack.push(expr,op);} while(stack.length){expr=new oa(stack.pop(),stack.pop(),expr);} return expr;};oa.prototype=new Ma();oa.prototype.evaluate=function(Xb){return oa.ops[this.op][1](this.left,this.right,Xb);};oa.prototype.show=function(Yb){Yb=Yb||'';var t='';t+=Yb+'binary: '+this.op+'\n';Yb+=' ';t+=this.left.show(Yb);t+=this.right.show(Yb);return t;};if(!window.UnaryExpr&&window.defaultConfig)window.UnaryExpr=null;Aa=function(op,Zb){this.op=op;this.expr=Zb;this.needContextPosition=Zb.needContextPosition;this.needContextNode=Zb.needContextNode;};Aa.ops={'-':1};Aa.parse=function($b){var ac;if(this.ops[$b.peek()])return new Aa($b.next(),Aa.parse($b));else return Ba.parse($b);};Aa.prototype=new Ma();Aa.prototype.datatype='number';Aa.prototype.evaluate=function(bc){return-this.expr.number(bc);};Aa.prototype.show=function(cc){cc=cc||'';var t='';t+=cc+'unary: '+this.op+'\n';cc+=' ';t+=this.expr.show(cc);return t;};if(!window.UnionExpr&&window.defaultConfig)window.UnionExpr=null;Ba=function(){this.paths=[];};Ba.ops={'|':1};Ba.parse=function(dc){var ec,expr;expr=ya.parse(dc);if(!this.ops[dc.peek()])return expr;ec=new Ba();ec.path(expr);while(true){if(!this.ops[dc.next()])break;if(dc.empty()){throw Error('missing next union location path');} ec.path(ya.parse(dc));} dc.back();return ec;};Ba.prototype=new Ma();Ba.prototype.datatype='nodeset';Ba.prototype.evaluate=function(fc){var gc=this.paths;var hc=new ta();for(var i=0,l=gc.length;ideep2){while(deep1--!=deep2)gf=gf.parentNode;if(gf==node2)return 1;} else if(deep2>deep1){while(deep2--!=deep1)node2=node2.parentNode;if(gf==node2)return -1;} while((ancestor1=gf.parentNode)!=(ancestor2=node2.parentNode)){gf=ancestor1;node2=ancestor2;} while(gf=gf.nextSibling)if(gf==node2)return -1;return 1;}});}}; /*@cc_on @if(@_jscript)ta.prototype.sourceOffset=1;ta.prototype.subOffset=2;ta.prototype.createWrapper=function(hf){var jf,child,attributes,attributesLength,sourceIndex,subIndex,attributeName;sourceIndex=hf.sourceIndex;if(typeof sourceIndex!='number'){type=hf.nodeType;switch(type){case 2:jf=hf.parentNode;sourceIndex=hf.parentSourceIndex;subIndex=-1;attributeName=hf.nodeName;break;case 9:subIndex=-2;sourceIndex=-1;break;default:child=hf;subIndex=0;do{subIndex++;sourceIndex=child.sourceIndex;if(sourceIndex){jf=child;child=child.lastChild;if(!child){child=jf;break;} subIndex++;}} while(child=child.previousSibling);if(!sourceIndex){sourceIndex=hf.parentNode.sourceIndex;} break;}} else{subIndex=-2;} sourceIndex+=this.sourceOffset;subIndex+=this.subOffset;return new Ue(hf,sourceIndex,subIndex,attributeName);};ta.prototype.reserveDelBySourceIndexAndSubIndex=function(kf,lf,mf,nf){var of=this.createIdIndexMap();var pf;if((of=of[kf])&&(pf=of[lf])){if(nf&&(this.length-mf-1)>pf||!nf&&mfuf||!sf&&rf=0x10000||subIndex>=0x10000){this.shortcut=false;} if(this._first||this.nodes.length==0){Pf=this._first;firstSourceIndex=this._firstSourceIndex;firstSubIndex=this._firstSubIndex;if(!Pf||firstSourceIndex>sourceIndex||(firstSourceIndex==sourceIndex&&firstSubIndex>subIndex)){this._first=Nf;this._firstSourceIndex=sourceIndex;this._firstSubIndex=subIndex}}@else @*/ var Qf=this.seen;var id=Ze.get(Nf);if(Qf[id])return true;Qf[id]=true;/*@end @*/ this.length++;if(Of)this.nodes.unshift(Nf);else this.nodes.push(Nf);};ta.prototype.unshift=function(Rf){if(!this.length){this.length++;this.only=Rf;return} if(this.only){var Sf=this.only;delete this.only;this.unshift(Sf);this.length--;} /*@cc_on Rf=this.createWrapper(Rf);@*/return this._add(Rf,true);};ta.prototype.push=function(Tf){if(!this.length){this.length++;this.only=Tf;return;} if(this.only){var Uf=this.only;delete this.only;this.push(Uf);this.length--;} /*@cc_on Tf=this.createWrapper(Tf);@*/return this._add(Tf);};ta.prototype.first=function(){if(this.only)return this.only; /*@cc_on if(this._first)return this._first.node;if(this.nodes.length>1)this.sort();var Vf=this.nodes[0];return Vf?Vf.node:ca;@*/if(this.nodes.length>1)this.sort();return this.nodes[0];};ta.prototype.list=function(){if(this.only)return[this.only];this.sort(); /*@cc_on var i,l,Mf,results;Mf=this.nodes;results=[];for(i=0,l=Mf.length;i %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n logs/mysvc-%d{yyyy-MM-dd}.%i.log 64 MB true %-5level %logger{36} - %msg%n INFO #+END_SRC * Code and tests ** The web service #+BEGIN_SRC clojure :tangle mysvc/src/mysvc/service.clj (ns mysvc.service (:require [io.pedestal.service.http :as bootstrap] [io.pedestal.service.http.route :as route] [io.pedestal.service.http.body-params :as body-params] [io.pedestal.service.http.route.definition :refer [defroutes]] [ring.util.response :as ring-resp])) (defn about-page [request] (ring-resp/response (format "Clojure %s" (clojure-version)))) (defn home-page [request] (ring-resp/response "Hello World!")) (defroutes routes [[["/" {:get home-page} ;; Set default interceptors for /about and any other paths under / ^:interceptors [(body-params/body-params) bootstrap/html-body] ["/about" {:get about-page}]]]]) ;; You can use this fn or a per-request fn via io.pedestal.service.http.route/url-for (def url-for (route/url-for-routes routes)) ;; Consumed by mysvc.server/create-server (def service {:env :prod ;; You can bring your own non-default interceptors. Make ;; sure you include routing and set it up right for ;; dev-mode. If you do, many other keys for configuring ;; default interceptors will be ignored. ;; :bootstrap/interceptors [] ::bootstrap/routes routes ;; Uncomment next line to enable CORS support, add ;; string(s) specifying scheme, host and port for ;; allowed source(s): ;; ;; "http://localhost:8080" ;; ;;::bootstrap/allowed-origins ["scheme://host:port"] ;; Root for resource interceptor that is available by default. ::bootstrap/resource-path "/public" ;; Either :jetty or :tomcat (see comments in project.clj ;; to enable Tomcat) ;;::bootstrap/host "localhost" ::bootstrap/type :jetty ::bootstrap/port 8080}) #+END_SRC *** Tests #+BEGIN_SRC clojure :tangle mysvc/test/mysvc/service_test.clj (ns mysvc.service-test (:require [clojure.test :refer :all] [io.pedestal.service.test :refer :all] [io.pedestal.service.http :as bootstrap] [mysvc.service :as service])) (def service (::bootstrap/service-fn (bootstrap/create-servlet service/service))) (deftest home-page-test (is (= (:body (response-for service :get "/")) "Hello World!")) (is (= (:headers (response-for service :get "/")) {"Content-Type" "text/html;charset=UTF-8"}))) (deftest about-page-test (is (.contains (:body (response-for service :get "/about")) "Clojure 1.5")) (is (= (:headers (response-for service :get "/about")) {"Content-Type" "text/html;charset=UTF-8"}))) #+END_SRC ** Server/servlet definition #+BEGIN_SRC clojure :tangle mysvc/src/mysvc/server.clj (ns mysvc.server (:gen-class) ; for -main method in uberjar (:require [io.pedestal.service-tools.server :as server] [mysvc.service :as service] [io.pedestal.service-tools.dev :as dev])) (defn run-dev "The entry-point for 'lein run-dev'" [& args] (dev/init service/service #'service/routes) (apply dev/-main args)) ;; To implement your own server, copy io.pedestal.service-tools.server and ;; customize it. (defn -main "The entry-point for 'lein run'" [& args] (server/init service/service) (apply server/-main args)) ;; Fns for use with io.pedestal.servlet.ClojureVarServlet (defn servlet-init [this config] (server/init service/service) (server/servlet-init this config)) (defn servlet-destroy [this] (server/servlet-destroy this)) (defn servlet-service [this servlet-req servlet-resp] (server/servlet-service this servlet-req servlet-resp)) #+END_SRC ================================================ FILE: 05-luminus-site/luminus-site-skeleton.org ================================================ #+TITLE: Luminus/Clojure Website Template, in Org/LP Format #+AUTHOR: Kai Wu #+EMAIL: k@limist.com #+LANGUAGE: en #+STARTUP: align hidestars lognotestate #+PROPERTY: mkdirp yes * Introduction This is the Emacs [[http://orgmode.org][Org mode]] literate-programming single file template for a web application using the Luminus "framework." For the structure/files below, I used the command, =lein new luminus mysite +cljs +site +http-kit= ** Useful Luminus information/links Taken from the Luminus-provided file at =mysite/resources/public/md/docs.md= 1. [[http://www.luminusweb.net/docs/html_templating.md][HTML templating]] 2. [[http://www.luminusweb.net/docs/database.md][Accessing the database]] 3. [[http://www.luminusweb.net/docs/static_resources.md][Serving static resources]] 4. [[http://www.luminusweb.net/docs/responses.md][Setting response types]] 5. [[http://www.luminusweb.net/docs/routes.md][Defining routes]] 6. [[http://www.luminusweb.net/docs/middleware.md][Adding middleware]] 7. [[http://www.luminusweb.net/docs/sessions_cookies.md][Sessions and cookies]] 8. [[http://www.luminusweb.net/docs/security.md][Security]] 9. [[http://www.luminusweb.net/docs/deployment.md][Deploying the application]] ** What files/directories are NOT included in this =.org= file? + Everything under =mysite/resources= such as CSS, fonts, images, JS. Since those files are not tangled/produced from here, you'll see the =resources/= subdir already tracked in the git repository. * Project meta ** Main configuration As of [2013-10-29 Tue], the starting =project.clj= produced by the Luminus template used an indentation style that's unorthodox; the version below differs with the Luminus one only in indentation. #+BEGIN_SRC clojure :tangle mysite/project.clj (defproject mysite "0.1.0-SNAPSHOT" :dependencies [[org.clojure/clojure "1.5.1"] [lib-noir "0.7.4"] [compojure "1.1.5"] [ring-server "0.3.0"] [selmer "0.5.1"] [com.taoensso/timbre "2.6.3"] [com.postspectacular/rotor "0.1.0"] [com.taoensso/tower "1.7.1"] [markdown-clj "0.9.33"] [com.h2database/h2 "1.3.173"] [korma "0.3.0-RC6"] [log4j "1.2.17" :exclusions [javax.mail/mail javax.jms/jms com.sun.jdmk/jmxtools com.sun.jmx/jmxri]] [http-kit "2.1.11"] [org.clojure/clojurescript "0.0-1934"] [domina "1.0.2"] [prismatic/dommy "0.1.2"] [cljs-ajax "0.2.0"]] :cljsbuild {:builds [{:source-paths ["src-cljs"], :compiler {:pretty-print false, :output-to "resources/public/js/site.js", :optimizations :advanced}}]} :ring {:handler mysite.handler/app, :init mysite.handler/init, :destroy mysite.handler/destroy} :profiles {:production {:ring {:open-browser? false, :stacktraces? false, :auto-reload? false}}, :dev {:dependencies [[ring-mock "0.1.5"] [ring/ring-devel "1.2.0"]]}} :url "http://example.com/FIXME" :aot all :main mysite.core :plugins [[lein-ring "0.8.7"] [lein-cljsbuild "0.3.3"]] :description "FIXME: write description" :min-lein-version "2.0.0") #+END_SRC ** Procfile, for Heroku deployment If you want to use this, change the =:tangle no= below to =:tangle mysite/Procfile= #+BEGIN_SRC text :tangle no web: lein with-profile production trampoline ring server #+END_SRC ** The README Or you could just do your documentation here in this Org file, which is superior to Markdown because Org has powerful structural editing. #+BEGIN_SRC markdown :tangle mysite/README.md # mysite FIXME ## Prerequisites You will need [Leiningen][1] 2.0 or above installed. [1]: https://github.com/technomancy/leiningen ## Running To start a web server for the application, run: lein ring server ## License Copyright © 2013 FIXME #+END_SRC ** =.gitignore= #+BEGIN_SRC shell :tangle mysite/.gitignore /target /lib /classes /checkouts pom.xml *.jar *.class /.lein-* /.env # Ignore all files tangled from this Org/LP file: project.clj README.md Procfile src/ src-cljs/ test/ #+END_SRC * Code and tests ** log4j configuration, for Korma Note that for application/Clojure-level logging we use [[https://github.com/ptaoussanis/timbre][Timbre]]. Note the header argument of =:padline no= for XML files is needed to avoid an error in XML parsing. #+BEGIN_SRC xml :tangle mysite/src/log4j.xml :padline no #+END_SRC ** Server-side *** The core: server definition #+BEGIN_SRC clojure :tangle mysite/src/mysite/core.clj (ns mysite.core (:require [mysite.handler :refer [app]] [ring.middleware.reload :as reload] [org.httpkit.server :as http-kit] [taoensso.timbre :as timbre]) (:gen-class)) (defn dev? [args] (some #{"-dev"} args)) (defn port [args] (if-let [port (first (remove #{"-dev"} args))] (Integer/parseInt port) 3000)) (defn -main [& args] (http-kit/run-server (if (dev? args) (reload/wrap-reload app) app) {:port (port args)}) (timbre/info "server started on port")) #+END_SRC **** Control the server from the REPL #+BEGIN_SRC clojure :tangle mysite/src/mysite/repl.clj (ns mysite.repl (:use mysite.handler ring.server.standalone [ring.middleware file-info file])) (defonce server (atom nil)) (defn get-handler [] ;; #'app expands to (var app) so that when we reload our code, ;; the server is forced to re-resolve the symbol in the var ;; rather than having its own copy. When the root binding ;; changes, the server picks it up without having to restart. (-> #'app ; Makes static assets in $PROJECT_DIR/resources/public/ available. (wrap-file "resources") ; Content-Type, Content-Length, and Last Modified headers for files in body (wrap-file-info))) (defn start-server "used for starting the server in development mode from REPL" [& [port]] (let [port (if port (Integer/parseInt port) 3000)] (reset! server (serve (get-handler) {:port port :init init :auto-reload? true :destroy destroy :join? false})) (println (str "You can view the site at http://localhost:" port)))) (defn stop-server [] (.stop @server) (reset! server nil)) #+END_SRC *** Handler: base routes, app-level config #+BEGIN_SRC clojure :tangle mysite/src/mysite/handler.clj (ns mysite.handler (:require [compojure.core :refer [defroutes]] [mysite.routes.home :refer [home-routes]] [noir.util.middleware :as middleware] [compojure.route :as route] [taoensso.timbre :as timbre] [com.postspectacular.rotor :as rotor] [mysite.routes.auth :refer [auth-routes]] [mysite.models.schema :as schema] [mysite.routes.cljsexample :refer [cljs-routes]])) (defroutes app-routes (route/resources "/") (route/not-found "Not Found")) (defn init "init will be called once when app is deployed as a servlet on an app server such as Tomcat put any initialization code here" [] (timbre/set-config! [:appenders :rotor] {:min-level :info, :enabled? true, :async? false, :max-message-per-msecs nil, :fn rotor/append}) (timbre/set-config! [:shared-appender-config :rotor] {:path "mysite.log", :max-size (* 512 1024), :backlog 10}) (if-not (schema/initialized?) (schema/create-tables)) (timbre/info "mysite started successfully")) (defn destroy "destroy will be called when your application shuts down, put any clean up code here" [] (timbre/info "mysite is shutting down...")) (def app (middleware/app-handler [cljs-routes auth-routes home-routes app-routes] :middleware [] :access-rules [] :formats [:json-kw :edn])) #+END_SRC **** Tests #+BEGIN_SRC clojure :tangle mysite/test/mysite/test/handler.clj (ns mysite.test.handler (:use clojure.test ring.mock.request mysite.handler)) (deftest test-app (testing "main route" (let [response (app (request :get "/"))] (is (= (:status response) 200)) (is (= (:body response) "\n \n Welcome to mysite\n \n \n \n
\n \n
\n \n
Copyright ...
\n \n\n\n\n")))) (testing "not-found route" (let [response (app (request :get "/invalid"))] (is (= (:status response) 404))))) #+END_SRC *** Models and persistence **** Database queries/functions #+BEGIN_SRC clojure :tangle mysite/src/mysite/models/db.clj (ns mysite.models.db (:use korma.core [korma.db :only (defdb)]) (:require [mysite.models.schema :as schema])) (defdb db schema/db-spec) (defentity users) (defn create-user [user] (insert users (values user))) (defn update-user [id first-name last-name email] (update users (set-fields {:first_name first-name :last_name last-name :email email}) (where {:id id}))) (defn get-user [id] (first (select users (where {:id id}) (limit 1)))) #+END_SRC **** Schema #+BEGIN_SRC clojure :tangle mysite/src/mysite/models/schema.clj (ns mysite.models.schema (:require [clojure.java.jdbc :as sql] [noir.io :as io])) (def db-store "site.db") (def db-spec {:classname "org.h2.Driver" :subprotocol "h2" :subname (str (io/resource-path) db-store) :user "sa" :password "" :naming {:keys clojure.string/lower-case :fields clojure.string/upper-case}}) (defn initialized? "checks to see if the database schema is present" [] (.exists (new java.io.File (str (io/resource-path) db-store ".h2.db")))) (defn create-users-table [] (sql/with-connection db-spec (sql/create-table :users [:id "varchar(20) PRIMARY KEY"] [:first_name "varchar(30)"] [:last_name "varchar(30)"] [:email "varchar(30)"] [:admin :boolean] [:last_login :time] [:is_active :boolean] [:pass "varchar(100)"]))) (defn create-tables "creates the database tables used by the application" [] (create-users-table)) #+END_SRC *** Routes: URLs/pages and workflows **** Authentication workflow #+BEGIN_SRC clojure :tangle mysite/src/mysite/routes/auth.clj (ns mysite.routes.auth (:use compojure.core) (:require [mysite.views.layout :as layout] [noir.session :as session] [noir.response :as resp] [noir.validation :as vali] [noir.util.crypt :as crypt] [mysite.models.db :as db])) (defn valid? [id pass pass1] (vali/rule (vali/has-value? id) [:id "user ID is required"]) (vali/rule (vali/min-length? pass 5) [:pass "password must be at least 5 characters"]) (vali/rule (= pass pass1) [:pass1 "entered passwords do not match"]) (not (vali/errors? :id :pass :pass1))) (defn register [& [id]] (layout/render "registration.html" {:id id :id-error (vali/on-error :id first) :pass-error (vali/on-error :pass first) :pass1-error (vali/on-error :pass1 first)})) (defn handle-registration [id pass pass1] (if (valid? id pass pass1) (try (do (db/create-user {:id id :pass (crypt/encrypt pass)}) (session/put! :user-id id) (resp/redirect "/")) (catch Exception ex (vali/rule false [:id (.getMessage ex)]) (register))) (register id))) (defn profile [] (layout/render "profile.html" {:user (db/get-user (session/get :user-id))})) (defn update-profile [{:keys [first-name last-name email]}] (db/update-user (session/get :user-id) first-name last-name email) (profile)) (defn handle-login [id pass] (let [user (db/get-user id)] (if (and user (crypt/compare pass (:pass user))) (session/put! :user-id id)) (resp/redirect "/"))) (defn logout [] (session/clear!) (resp/redirect "/")) (defroutes auth-routes (GET "/register" [] (register)) (POST "/register" [id pass pass1] (handle-registration id pass pass1)) (GET "/profile" [] (profile)) (POST "/update-profile" {params :params} (update-profile params)) (POST "/login" [id pass] (handle-login id pass)) (GET "/logout" [] (logout))) #+END_SRC **** Routes->pages: homepage, about #+BEGIN_SRC clojure :tangle mysite/src/mysite/routes/home.clj (ns mysite.routes.home (:use compojure.core) (:require [mysite.views.layout :as layout] [mysite.util :as util])) (defn home-page [] (layout/render "home.html" {:content (util/md->html "/md/docs.md")})) (defn about-page [] (layout/render "about.html")) (defroutes home-routes (GET "/" [] (home-page)) (GET "/about" [] (about-page))) #+END_SRC **** CLJS client app hosting #+BEGIN_SRC clojure :tangle mysite/src/mysite/routes/cljsexample.clj (ns mysite.routes.cljsexample (:require [compojure.core :refer :all] [noir.response :as response] [mysite.views.layout :as layout])) (def messages (atom [{:message "Hello world" :user "Foo"} {:message "Ajax is fun" :user "Bar"}])) (defroutes cljs-routes (GET "/cljsexample" [] (layout/render "cljsexample.html")) (GET "/messages" [] (response/edn @messages)) (POST "/add-message" [message user] (response/edn (swap! messages conj {:message message :user user})))) #+END_SRC *** Utility/helper functions In other words, useful code that doesn't fit elsewhere: #+BEGIN_SRC clojure :tangle mysite/src/mysite/util.clj (ns mysite.util (:require [noir.io :as io] [markdown.core :as md])) (defn md->html "reads a markdown file from public/md and returns an HTML string" [filename] (->> (io/slurp-resource filename) (md/md-to-html-string))) #+END_SRC *** Visuals and templates/layouts **** Layout #+BEGIN_SRC clojure :tangle mysite/src/mysite/views/layout.clj (ns mysite.views.layout (:require [selmer.parser :as parser] [clojure.string :as s] [ring.util.response :refer [content-type response]] [noir.session :as session]) (:import compojure.response.Renderable)) (def template-path "mysite/views/templates/") (deftype RenderableTemplate [template params] Renderable (render [this request] (content-type (->> (assoc params (keyword (s/replace template #".html" "-selected")) "active" :servlet-context (:context request) :user-id (session/get :user-id)) (parser/render-file (str template-path template)) response) "text/html; charset=utf-8"))) (defn render [template & [params]] (RenderableTemplate. template params)) #+END_SRC **** About page #+BEGIN_SRC html :tangle mysite/src/mysite/views/templates/about.html {% extends "mysite/views/templates/base.html" %} {% block content %}

this is the story of mysite... work in progress

{% endblock %} #+END_SRC **** Base page #+BEGIN_SRC html :tangle mysite/src/mysite/views/templates/base.html Welcome to mysite
{% block content %} {% endblock %}
{% style "/css/bootstrap-theme.min.css" %} {% style "/css/bootstrap.min.css" %} {% style "/css/screen.css" %} {% script "/js/bootstrap.min.js" %} #+END_SRC **** CLJS example #+BEGIN_SRC html :tangle mysite/src/mysite/views/templates/cljsexample.html {% extends "mysite/views/templates/base.html" %} {% block content %}


{% endblock %} #+END_SRC **** Homepage #+BEGIN_SRC html :tangle mysite/src/mysite/views/templates/home.html {% extends "mysite/views/templates/base.html" %} {% block content %}

Welcome to mysite

Time to start building your site!

Learn more »

{{content|safe}}
{% endblock %} #+END_SRC **** Menu template #+BEGIN_SRC html :tangle mysite/src/mysite/views/templates/menu.html #+END_SRC **** Profile template #+BEGIN_SRC html :tangle mysite/src/mysite/views/templates/profile.html {% extends "mysite/views/templates/base.html" %} {% block menu %} {% endblock %} {% block content %}

User details for {{user.id}}

{% endblock %} #+END_SRC **** Registration page #+BEGIN_SRC html :tangle mysite/src/mysite/views/templates/registration.html {% extends "mysite/views/templates/base.html" %} {% block menu %} {% endblock %} {% block content %}
{% if id-error %}
{{id-error}}
{% endif %}

{% if pass-error %}
{{pass-error}}
{% endif %}

{% if pass1-error %}
{{pass1-error}}
{% endif %}

{% endblock %} #+END_SRC ** Client-side *** ClojureScript #+BEGIN_SRC clojurescript :tangle mysite/src-cljs/main.cljs (ns mysite.main (:require [ajax.core :refer [GET POST]] [domina :refer [value by-id destroy-children! append!]] [domina.events :refer [listen!]] [dommy.template :as template])) (defn render-message [{:keys [message user]}] [:li [:p {:id user} message " - " user]]) (defn render-messages [messages] (let [messages-div (by-id "messages")] (destroy-children! messages-div) (->> messages (map render-message) (into [:ul]) template/node (append! messages-div)))) (defn add-message [_] (POST "/add-message" {:params {:message (value (by-id "message")) :user (value (by-id "user"))} :handler render-messages})) (defn ^:export init [] (GET "/messages" {:handler render-messages}) (listen! (by-id "send") :click add-message)) #+END_SRC ================================================ FILE: 05-luminus-site/mysite/resources/public/css/screen.css ================================================ html, body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; height: 100%; padding-top: 70px; } .error { color: red; } ================================================ FILE: 05-luminus-site/mysite/resources/public/md/docs.md ================================================ ### Here are some links to get started 1. [HTML templating](http://www.luminusweb.net/docs/html_templating.md) 2. [Accessing the database](http://www.luminusweb.net/docs/database.md) 3. [Serving static resources](http://www.luminusweb.net/docs/static_resources.md) 4. [Setting response types](http://www.luminusweb.net/docs/responses.md) 5. [Defining routes](http://www.luminusweb.net/docs/routes.md) 6. [Adding middleware](http://www.luminusweb.net/docs/middleware.md) 7. [Sessions and cookies](http://www.luminusweb.net/docs/sessions_cookies.md) 8. [Security](http://www.luminusweb.net/docs/security.md) 9. [Deploying the application](http://www.luminusweb.net/docs/deployment.md) ================================================ FILE: LICENSE ================================================ Eclipse Public License - v 1.0 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. "Contributor" means any person or entity that distributes the Program. "Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 3. REQUIREMENTS A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: a) it complies with the terms and conditions of this Agreement; and b) its license agreement: i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. When the Program is made available in source code form: a) it must be made available under this Agreement; and b) a copy of this Agreement must be included with each copy of the Program. Contributors may not remove or alter any copyright notices contained within the Program. Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. ================================================ FILE: README.md ================================================ Literate Programming Examples ============================= A literate program combines code and prose (documentation) in one file format. The term was coined by Donald Knuth in his September 1983 paper, *Literate Programming*, [available online](http://literateprogramming.com/knuthweb.pdf). Decades later, [Knuth asserts that literate programming](http://www.informit.com/articles/article.aspx?p=1193856): 1. Was the most important outcome of creating TeX. 2. Enables faster and more reliable creation of software. 3. Manages complexity better than any other methodology he's aware of. 4. Is a source of joy when programming. 5. Enables extraordinary achievements in creating software. Can these claims be made by non-Knuth programmers who adopt literate programming (LP) for themselves? I believe the time is ripe to find out, as we finally have a tool that is simple (plain text), flexible, and powerful, with mature LP capabilities: [Emacs Org mode](http://orgmode.org). Thus this repository is a collection of literate programming (LP) examples, using Emacs Org mode. These examples are intended to be directly usable (copy and start hacking), and/or to serve as educational literate programs. The long-term goal is to collect and organize a corpus of useful LP examples that point towards Knuth's vision of programs-as-literature. Prerequisites ============= 1. Install a recent version of Emacs, 24.3+. 2. Install both `org-mode` (older version should be included w/ Emacs 24+) and `clojure-mode`. Use Emacs ELPA as needed (it can also upgrade Emacs packages); you can invoke that from Emacs with `M-x package-list-packages` - Consider using an Emacs "starter package" that provides a good baseline configuration, like [Emacs Prelude](http://batsov.com/prelude/) or [Emacs Live](http://overtone.github.io/emacs-live/). Both those packages choose reasonable/good default configurations, and support Clojure (other languages too). 3. Update your `.emacs` file to support Org's LP features for Clojure (and possibly other languages). - The exact location and name varies, depending on whether you chose one of the starter packages mentioned; e.g. with Emacs Prelude you'd have a file like `~/.emacs.d/personal/yourUsername.el` - You'll need to add the following somewhere within the `.emacs`; note that the `emacs-lisp` line below is optional, it's just meant to show that supporting additional languages is easy: ```elisp (org-babel-do-load-languages 'org-babel-load-languages '((emacs-lisp . t) (clojure . t))) ;; Show syntax highlighting per language native mode in *.org (setq org-src-fontify-natively t) ;; For languages with significant whitespace like Python: (setq org-src-preserve-indentation t) ``` The benefits of LP using Emacs + Org ==================================== 1. Documentation matters, a lot. - For starters, do you judge a Github project by its README? - For all but small throwaway systems, you're likely keeping a separate file of development notes already; LP would integrate that. - No matter how clear the function and data names are, code itself rarely clarifies larger issues of architecture and design, and the decision histories therein. - With literate programming, documentation is integral to development, never an afterthought. 2. With one LP file, you avoid the incidental/inessential complexity of the filesystem, by avoiding the frequent context-switching overhead in moving between files. And you sidestep your language's imposed filesystem structure. 3. Org rocks for prose: - Org's plain-text markup is lightweight, yet more powerful than Markdown (which lacks hierarchical structuring), and cleaner than rST. - The structural editing provided by Org documents lets you organize your thoughts/writing/code very quickly. With good structure even major revisions are easy. - Org's exporter lets your write-once, express-many-times: you can export an Org file to HTML (e.g. for blogging) or LaTeX (for serious publishing) and PDF. - It's easy to version-control Org files; it's just plain-text. 4. Org rocks for code: - Each code block has flexible granularity: can be named and referred to; evaluated or not; have data sent in or exported; specify different REPL sessions; specify different target/tangled files (in arbitrary subdirectories). - Code blocks are syntax-highlighted. - Code blocks are ready to edit: jump to major-mode editing easily; edit/REPL as usual; changes will flow back to the containing Org file. - A single Org file can mix multiple languages together. 5. Meta-development, manage complexity from a coherent perspective: a unified, single-file approach encourages holistic software development and exposition, in a natural order, using structure to enhance understanding. LP is not just documentation and code together: it's a **process and abstraction unifying the development lifecycle**: requirements, architecture, design, code, tests, deployment, and maintenance - can all be bound coherently in one active format. More information ================ - Emacs: no flamebait here; I will simply say that having Org is sufficient reason to use the Eternal Editor. Just be sure to [remap your CapsLock to Control](http://www.emacswiki.org/emacs/MovingTheCtrlKey), for happy hands. - [Org documentation](http://orgmode.org/org.html), especially the section on [Working with source code](http://orgmode.org/org.html#Working-With-Source-Code) - The excellent paper by Schulte and Davison, [Active Documents with Org-Mode](http://www.cs.unm.edu/~eschulte/data/CISE-13-3-SciProg.pdf) - Pro-tip: when you want to "tangle" or export code blocks from an org file, =CTRL-c-v-t= is the keystroke combo to tangle all blocks. To tangle only ONE block, the current one your cursor is in, just use the Emacs prefix code first: =CTRL-u-c-v-t= For big org files, this saves time, as it's essentially instantaneous to tangle/export one block.