{"id":17057030,"url":"https://github.com/swannodette/enlive-tutorial","last_synced_at":"2025-04-12T21:24:30.432Z","repository":{"id":791469,"uuid":"488800","full_name":"swannodette/enlive-tutorial","owner":"swannodette","description":"An Easy Introduction to Enlive","archived":false,"fork":false,"pushed_at":"2019-01-02T02:15:15.000Z","size":365,"stargazers_count":615,"open_issues_count":20,"forks_count":80,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-04-04T00:45:29.291Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/swannodette.png","metadata":{"files":{"readme":"readme.textile","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2010-01-26T05:32:55.000Z","updated_at":"2025-01-24T09:20:36.000Z","dependencies_parsed_at":"2022-07-14T18:47:14.302Z","dependency_job_id":null,"html_url":"https://github.com/swannodette/enlive-tutorial","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swannodette%2Fenlive-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swannodette%2Fenlive-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swannodette%2Fenlive-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swannodette%2Fenlive-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/swannodette","download_url":"https://codeload.github.com/swannodette/enlive-tutorial/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248633039,"owners_count":21136794,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T10:26:16.003Z","updated_at":"2025-04-12T21:24:30.407Z","avatar_url":"https://github.com/swannodette.png","language":"Clojure","readme":"h1. An Introduction to Enlive\n\nThough \"Christophe Grand's Enlive\":http://github.com/cgrand/enlive has been around for sometime now the Clojure community has been slow to embrace this useful library. I believe this is due simply to the lack of good introductory documentation based on real examples. Please let me know if you find this tutorial useful or helpful in any way. Feel free to suggest additions, corrections, and improvements (even better fork the repo and send me a patch).\n\nh2. What You need to Know\n\nNot much. This tutorial assumes little about your exposure to Clojure. At the very least you'll need to have the Java Virtual Machine (JVM) installed. I won't go into great detail about Clojure's features but I'll try to explain any concepts which may impede your understanding of Enlive's functionality.\n\nAs the tutorials progress they will be more useful to you if you have some experience with a modern webframework that ships with a HTML templating library. If you do know Clojure the tutorials are fairly amenable to skimming. My only real assumption is that you have some experience at the command line.\n\nEnlive has many features and I can't possibly cover them all here. Fortunately the Clojure community is vibrant and helpful. If you run into issues or have questions join the \"Enlive mailing list\":http://groups.google.com/group/enlive-clj or jump on the #clojure channel on irc.freenode.net.\n\nh2. HTML Templating\n\nThere are two real camps in HTML templating. The first which almost everyone is familiar with is the PHP style template:\n\n\u003cpre\u003e\n\u003c?php\nfor($i = 0; $i \u003c $len; $i++) {\n?\u003e\u003cp\u003eFoo \u003c?echo $i?\u003e\u003c/p\u003e\u003c?\n}\n?\u003e\n\u003c/pre\u003e\n\nThis is of course enough to drive anyone insane and some templating solutions like the one in Django have made marginal improvements:\n\n\u003cpre\u003e\n{% for i in foo %}\n\u003cp\u003eFoo {{ i }}\u003c/p\u003e\n{% endfor %}\n\u003c/pre\u003e\n\nThis is a bit easier on the eyes. But this isn't very composable and you're stuck with a limited subset of your programming language. By not composable I mean that building pages is largely a copy and paste affair even when templating solutions support inheritance.\n\nThis has driven many programmers to discard templating DSLs and to generate markup directly in code. While this is fast and flexible as you now have the power of function composition, it also means that you're putting quite a bit of distance between yourself and a designer comfortable writing HTML and CSS.\n\nThere are a few existing solutions that have a novel approach to this problem such as \"Pure\":http://beebole.com/pure/.\n\nWhich brings us to Enlive. Enlive gives you the advantages of designer accessible templates (since they're just HTML) without losing the power of function composition. As a result, your designer can create all the various widgets for your website using only HTML and CSS and you can compose your pages from any combination of their designs.\n\nh2. Why Enlive?\n\nEnlive presents a different approach from the more popular templating solutions:\n\n** Code and markup are completely separate.\n** You get to use CSS like syntax to manipulate HTML.\n** Template inheritance isn't some fancy trick, it's just function composition.\n** You have access to the full power of Clojure to manipulate your templates (yes, macros!).\n\nWhen working with the standard templating solutions you generally need to answer one of two questions, either \"What type of text am I going to generate?\" or \"What type of HTML emitting functions should I write?\"\n\nIn contrast, with Enlive you usually break down the problem thus:\n\n# Determine which selectors match the part of the HTML document that you care about.\n# Determine which templates and snippets you need to write to compose your pages.\n\nh2. What We'll Cover\n\nThere are six examples in total.\n\nThe first one covers grabbing the headlines and points from Hackers News. The next one shows how to make the code less redundant. The third scrapes the New York Times front page since that presents more challenges than Hacker News. The fourth example shows how to use Ring and Enlive together. The fifth example shows how things like looping are achieved without writing any code into the markup. The sixth example shows that Enlive can do all the fancy template inheritance magic you might be used to if you're coming from Django or some other popular modern webframework.\n\nh2. Clone This Repo\n\nThe usual:\n\n\u003cpre class=\"console\"\u003e\ngit clone git://github.com/swannodette/enlive-tutorial.git\n\u003c/pre\u003e\n\nh2. Install Leiningen\n\nIn order to start playing around as fast possible you should use Leiningen. It'll take only a couple minutes to get through the instructions \"here\":http://github.com/technomancy/leiningen. Leiningen is the easy_install (Python) and gems (Ruby) of the Clojure world. Phil Hagelberg and Co. have done a considerable amount of excellent work to make dependency management simple. I truly envy the new Clojurians who do not know the dark times before *lein repl* and *lein swank* :)\n\nOnce you have Leiningen installed, switch into this repository's directory. From there run the following command:\n\n\u003cpre class=\"console\"\u003e\nlein deps\n\u003c/pre\u003e\n\nThis will install all of the dependencies required for getting through the tutorial. This might take a minute and and will probably generate \u003ci\u003ea lot\u003c/i\u003e of output. While this may seem disconcerting, this means you'll have a fully functioning Clojure setup without needing to bother with installing Emacs, configuring VIM, or an mucking around with an IDE. Once the the dependencies are installed enter the following command at your terminal:\n\n\u003cpre class=\"console\"\u003e\nlein repl\n\u003c/pre\u003e\n\nThis will launch a Clojure REPL (Read-Eval-Print-Loop) that has the classpath set properly. Be very thankful if you don't know what the last sentence means. Managing the classpath is one of the few real annoyances when programming Clojure and it's largely Java's fault.\n\nh2. Your First Scrape with Enlive - Hacker News\n\nEnlive is fantastic for scraping the content of webpages. It allows you to scrape content by using a syntax very similar to CSS selectors. In the REPL type the following lines (note that *user=\u003e* is the REPL prompt, not something you type in):\n\n\u003cpre class=\"console\"\u003e\nuser=\u003e (load \"tutorial/scrape1\")\nnil\nuser=\u003e (in-ns 'tutorial.scrape1)\nnil\ntutorial.scrape1=\u003e *base-url*\n\"https://news.ycombinator.com/\"\n\u003c/pre\u003e\n\nThe first line loads the file. Note that we need to specify that the *scrape1* file is to be found in the *tutorial* directory which is under the *src* directory of the repo. *src* is put onto the classpath by *lein* so we don't need to specify it. Also take care to note that we left off the *.clj* extension. Unlike many scripting languages, loading a file actually \u003ci\u003ecompiles\u003c/i\u003e it. Clojure is not interpreted.\n\nWe then use \u003ccode\u003ein-ns\u003c/code\u003e to put ourselves into that tutorial's namespace. By switching into the tutorial namespace we can use functions defined in the tutorial without having to qualify them. It's much easier to type \u003ccode\u003e*base-url*\u003c/code\u003e than \u003ccode\u003etutorial.scrape1/*base-url*\u003c/code\u003e.\n\nLet's see what's in that file. Open up \"*scrape1.clj*\":src/tutorial/scrape1.clj with your favorite text editor (you can find it in *your-tutorial-clone/src/tutorial/*). You'll see it's a fairly short program. \n\nAt the top of this file is the namespace declaration. This keeps your code from clashing with other people's code when they try to use your library. The namespace declaration also includes another library, Enlive, via \u003ccode\u003e:require\u003c/code\u003e. In this case we are generating an alias so we don't have to type the very long namespace for Enlive.\n\nThe function \u003ccode\u003efetch-url\u003c/code\u003e grabs the contents of a url synchronously. \u003ccode\u003efetch-url\u003c/code\u003e uses \u003ccode\u003ehtml/html-resource\u003c/code\u003e (remember we aliased *net.cgrand.enlive-html* to *html* for convenience) another handy function defined in the Enlive library. It takes raw HTML and converts it into a nested data structure (think DOM minus tediousness).\n\nNote that the function \u003ccode\u003ehn-headlines\u003c/code\u003e uses \u003ccode\u003efetch-url\u003c/code\u003e. But it's also surrounded by a lot of funny stuff. You might have noticed \u003ccode\u003ehtml/select\u003c/code\u003e; \u003ccode\u003ehtml/select\u003c/code\u003e takes parsed html content and selects the nodes specified by a Clojure vector that looks very similar to a CSS selector. \n\n\u003cpre\u003e\n[:td.title :a]\n\u003c/pre\u003e\n\nNow that looks kind of weird. But if you squint a little it might remind you of this:\n\n\u003cpre\u003e\ntd.title a\n\u003c/pre\u003e\n\nThis is a CSS selector for matching all links inside of table elements that have the CSS class \"title\". If you're a Javascript hacker you should know this stuff by heart.\n\nSo let's break this down. \u003ccode\u003efetch-url\u003c/code\u003e grabs the contents of the url and parses it into a data structure. \u003ccode\u003ehtml/select\u003c/code\u003e takes it and extracts only those nodes that match the selector - it always returns a vector of nodes. We then use Clojure's \u003ccode\u003emap\u003c/code\u003e function to iterate over the vector's elements applying a function to extract each nodes' text-node, in this case \u003ccode\u003ehtml/text\u003c/code\u003e (\u003ccode\u003emap\u003c/code\u003e is actually lazy, but we're not going to get into what that means in this Enlive tutorial).\n\nBelieve it or not, these 10 lines of code are enough to extract all of the headlines from the Hacker News front page. Let's try it out at the REPL now.\n\n\u003cpre class=\"console\"\u003e\ntutorial.scrape1=\u003e (hn-headlines)\n(\"A 'lorem ipsum' for images.\" \"Google Reader Can Now Track Changes to Any Website - Even Without a Feed\" \"jQuery 1.4.1 Released\" ... \"More\")\n\u003c/pre\u003e\n\nNice. After this the next function \u003ccode\u003ehn-points\u003c/code\u003e should make a lot more sense. It does the same thing but we grab the score from a different place in the markup. Try to run this function as well.\n\n\u003cpre class=\"console\"\u003e\ntutorial.scrape1=\u003e (hn-points)\n... output ...\n\u003c/pre\u003e\n\nThe last function takes the output of the two different functions and prints out the headline and score for each item on Hackers News. \n\n\u003cpre class=\"console\"\u003e\ntutorial.scrape1=\u003e (print-headlines-and-points)\n... output ...\n\u003c/pre\u003e\n\n*print-headlines-and-points* looks like a doozy doesn't it?\n\n\u003cpre\u003e\n(defn print-headlines-and-points []\n  (doseq [line (map #(str %1 \" (\" %2 \")\") (hn-headlines) (hn-points))]\n    (println line)))\n\u003c/pre\u003e\n\nLet's break it down. Again we have \u003ccode\u003emap\u003c/code\u003e. We know that it maps a function over a vector to return a new vector of elements with that function applied.\n\n\u003cpre\u003e\n#(str %1 \" (\" %2 \")\") ; is just shorthand for\n(fn [arg1 arg2] (str arg1 \" (\" arg2 \")\")\n\u003c/pre\u003e\n\nThis is an anonymous function. I'm not going to explain that here, they're pretty popular these days. \u003ccode\u003estr\u003c/code\u003e is a built in function for doing string concatenation.\n\nOddly this \u003ccode\u003emap\u003c/code\u003e is accepting not one list of things, but two! Check this out:\n\n\u003cpre class=\"console\"\u003e\ntutorial.scrape1=\u003e (map + [1 2 3] [4 5 6])\n(5 7 9)\n\u003c/pre\u003e\n\nWow you can map two different vectors into one! Finally we have \u003ccode\u003edoseq\u003c/code\u003e. \u003ccode\u003edoseq\u003c/code\u003e is just a convenient way to work with lists when you're dealing with *side effects* like printing to the REPL. I'm not going to get into that here. All it does is say take a list of things, assign each thing one at time to a variable, and then execute the following expressions (hopefully you're actually doing something with that variable!)\n\nNot bad for 17 lines of code. One obvious problem here is that we make two separate requests for the Hacker News front page. Let's fix this now.\n\nh2. Your Second Scrape - Improvements\n\nTake a look at \"*scrape2.clj*\":src/tutorial/scrape2.clj. It's also about 17 lines of code and it looks pretty much the same except that we no longer have one function to grab headlines and another for article points.\n\n\u003cpre\u003e\n(defn hn-headlines-and-points []\n  (map html/text\n       (html/select (fetch-url *base-url*)\n                    #{[:td.title :a] [:td.subtext html/first-child]})))\n\u003c/pre\u003e\n\nThis select grabs what we're interested at the same time.\n\n\u003cpre\u003e\n#{[:td.title :a] [:td.subtext first-child]}\n\u003c/pre\u003e\n\nIt's pretty much the same as:\n\n\u003cpre\u003e\ntd.title a, td.subtext:first-child\n\u003c/pre\u003e\n\nLets try out the functions. Start up the REPL with *lein repl* if you've shut it down and run the following.\n\n\u003cpre class=\"console\"\u003e\ntutorial.scrape1=\u003e (load \"scrape2\")\nnil\ntutorial.scrape1=\u003e (in-ns 'tutorial.scrape2)\nnil\ntutorial.scrape2=\u003e (hn-headlines-and-points)\n... output ...\n\u003c/pre\u003e\n\nThe above assumes you've continued from the first tutorial. If you're starting from scratch you'll need to be more specific about your loading, use \u003ccode\u003e(load \"tutorial/scrape2\")\u003c/code\u003e instead.\n\nThe results are interleaved so we can use Clojure's partition function to pair them up and output them just like we did in the previous scrape. The map looks a little bit different:\n\n\u003cpre\u003e\n(defn print-headlines-and-points []\n  (doseq [line (map (fn [[h s]] (str h \" (\" s \")\"))\n                    (partition 2 (hn-headlines-and-points)))]\n    (println line)))\n\u003c/pre\u003e\n\nTo get a sense of what partition does let's use the REPL again:\n\n\u003cpre class=\"console\"\u003e\ntutorial.scrape2=\u003e (partition 2 [1 2 3 4 5 6 7 8 9 0])\n((1 2) (3 4) (5 6) (7 8) (9 0))\n\u003c/pre\u003e\n\nNeat, it lets us pair things together. Exactly what we need.\n\nBut what's up with the *fn* this time?\n\n\u003cpre\u003e\n(fn [[h s]] (str h \" (\" s \")\"))\n\u003c/pre\u003e\n\nSay hello to destructuring. A lot of popular languages allow you to destructure but probably not as ubiquitously as Clojure does. Here we know that we are going to receive a two element vector for each item in the vector we're mapping over. So we're just saying that we want to assign the first element of that pair to the local variable *h* and the other to *s*.\n\nThe rest of the function should be clear from the last tutorial.\n\nh2. Your Third Scrape - The New York Times\n\nOur third scrape tackles the New York Times whose front page structure is considerably more complicated than Hacker News. Now to be clear this not that useful since the New York Times provides a fairly comprehensive list of RSS feeds.\n\nTake a look at \"*scrape3.clj*\":src/tutorial/scrape3.clj. This is a bit longer. Before we dive in let's see how it works. Start up the Clojure REPL if it's not already up and running.\n\n\u003cpre class=\"console\"\u003e\ntutorial.scrape2=\u003e (load \"scrape3\")\nnil\ntutorial.scrape2=\u003e (in-ns 'tutorial.scrape3)\nnil\ntutorial.scrape3=\u003e (print-stories)\n... output ...\n\u003c/pre\u003e\n\nIf you're not continuing from a previous tutorial you'll need to more specific about your loading, using \u003ccode\u003e(load \"tutorial/scrape3\")\u003c/code\u003e instead.\n\nNow this isn't perfect for a variety of reasons but it works well enough for the purposes of demonstration. Let's look at the code. At the top of the file we see that we have a variety of selectors.\n\n\u003cpre\u003e\n(def ^:dynamic *story-selector*\n     [[:div.story\n       (html/but :.advertisement)\n       (html/but :.autosStory)\n       (html/but :.adCreative)]])\n\u003c/pre\u003e\n\nHere we are matching any div with the CSS class story that does not also have any of the other classes specified.\n\n\u003cpre\u003e\n(def ^:dynamic *headline-selector*\n     #{[html/root :\u003e :h2 :a],\n       [html/root :\u003e :h3 :a]\n       [html/root :\u003e :h5 :a]}))\n\u003c/pre\u003e\n\nHere we know from looking at the markup of the page that headlines might match any of these three selectors. The selectors will only match headline tags that are children of the root element. We do this because there are story divs on the New York Times webpage that actually have multiple headlines underneath. The byline and story summary selectors are pretty much the same.\n\n\u003cpre\u003e\n(defn extract [node]\n  (let [headline (first (html/select [node] *headline-selector*))\n        byline   (first (html/select [node] *byline-selector*))\n        summary  (first (html/select [node] *summary-selector*))\n        result   (map html/text [headline byline summary])]\n    (zipmap [:headline :byline :summary] (map #(str/replace %  #\"\\n\" \"\") result))))\n\u003c/pre\u003e\n\nHere we take a node and extract the match. Note that we have to call first on the result of \u003ccode\u003ehtml/select\u003c/code\u003e because \u003ccode\u003ehtml/select\u003c/code\u003e always returns a sequence of nodes and not a single node. \u003ccode\u003ezipmap\u003c/code\u003e is a handy function, it allows us to take two sequences and zip them up into a hash-map. So here we take only the text nodes from the matches and remove any newline characters before we finally zip it up into a tidy hash-map.\n\nBecause this scrape is not comprehensive we might match empty stories, so we define a function \u003ccode\u003eempty-story?\u003c/code\u003e that checks for that. We use this to filter out any empty stories:\n\n\u003cpre\u003e\n(defn print-stories []\n  (doseq [story (remove empty-story? (map extract (stories)))]\n    (print-story story)))\n\u003c/pre\u003e\n\nHopefully by this point you can begin to make sense of the last few functions. If one of the functions seems unclear I suggest calling that function at the REPL with some dummy input to get a better sense of what it does.\n\nSo that's it for scraping. It's time to move on to how Enlive is useful for building your own pages.\n\nh2. Your First Template - The Basics\n\nThis is where things begin to get really interesting. We're going to use Ring, an ultralight HTTP framework. If you're familiar with Rack or CherryPy you will feel right at home.\n\nLet's get started. If you aren't running a REPL be sure to start one up from the repo directory with *lein repl*.\n\nOnce you see the REPL prompt type the following:\n\n\u003cpre class=\"console\"\u003e\ntutorial.scrape3=\u003e (load \"template1\")\nnil\ntutorial.scrape3=\u003e (in-ns 'tutorial.template1)\nnil\n\u003c/pre\u003e\n\nIf you're not continuing from a previous tutorial you'll need to be more specific about your loading, use \u003ccode\u003e(load \"tutorial/template1\")\u003c/code\u003e instead.\n\nYou should see some output that lets you know that Ring is starting up a webserver on port 8080. Point your browser at *http://localhost:8080*. You should see a very boring page. Point your browser at *http://localhost:8080/change*. You should see something slightly different.\n\nFirst open \"*template1.html*\":src/tutorial/template1.html and take a look at it. If you're used to other templating solutions the most shocking thing should be that there is absolutely no Clojure code in this file. And there never will be. Period.\n\nNow let's take a look at the code in \"*template1.clj*\":src/tutorial/template1.clj. By now the namespace part should be familiar so we'll skip over that. After the namespace declaration we'll see our first template definition:\n\n\u003cpre\u003e\n(html/deftemplate index \"tutorial/template1.html\"\n  [ctxt]\n  [:p#message] (html/content (:message ctxt)))\n\u003c/pre\u003e\n\nEvery template has the argument list \u003ccode\u003e[name source args \u0026 forms]\u003c/code\u003e. An Enlive template is a macro that when compiled will create a function with the same name. This function will have the same signature as defined by *args*. *forms* consists of pairs of Enlive selectors and a function to execute for each node that matches the selector.\n\nHere our template will find all *p* elements with the CSS id *message*. CSS ids should be unique so ideally this will only match a single element. Then we have the function which will receive this matching element.\n\n\u003cpre\u003e\n(html/content (:message ctxt))\n\u003c/pre\u003e\n\nThis means we'll replace the content of any matching node with the value for the key \u003ccode\u003e:message\u003c/code\u003e in the \u003ccode\u003ectxt\u003c/code\u003e hash-map that was passed as parameter to this template. The important thing to grasp here is that \u003ccode\u003ehtml/content\u003c/code\u003e is a function which \u003ci\u003ereturns\u003c/i\u003e a function which will receive the matched element.\n\nFor example what if we want a default message if there is no value for \u003ccode\u003e:message\u003c/code\u003e in \u003ccode\u003ectxt\u003c/code\u003e? It would look something like this:\n\n\u003cpre\u003e\n(html/deftemplate index \"tutorial/template1.html\"\n  [ctxt]\n  [:p#message] (fn [match]\n                 (if-let [msg (:message ctxt)]\n                   ((html/content msg) match)\n                   ((html/content \"Nothing to see here!\") match))))\n\u003c/pre\u003e\n\nIt should be clear that \u003ccode\u003ehtml/content\u003c/code\u003e returns a function which will receive the matching element and modify it. This could be made slightly less verbose like so:\n\n\u003cpre\u003e\n(html/deftemplate index \"tutorial/template1.html\"\n  [ctxt]\n  [:p#message] (html/content (get ctxt :message \"Nothing to see here\"))\n\u003c/pre\u003e\n\nThis is a considerable improvement and shows off a couple nice Clojure features. However even this is kinda meh. Why? Because what we really want is not just a way to specify a default. Honestly the default value will probably be in the markup itself! It would be much cooler to leave the content of the node \u003ci\u003eunchanged\u003c/i\u003e if we for some reason hand it nil for it's content value. This would allow us to easily implement template inheritance which we'll talk about later (grin).\n\nWhile Enlive does not have a great shortcut for expressing this pattern of \"change the content of this node only if given a non-nil value\", since Clojure is a competent Lisp, it's easy to write macros to remove the boilerplate. I've included a handy macro called \u003ccode\u003emaybe-content\u003c/code\u003e which allows us to write the following instead:\n\n\u003cpre\u003e\n(html/deftemplate index \"tutorial/template1.html\"\n  [ctxt]\n  [:p#message] (maybe-content (:message ctxt) \"Nothing to see here!\"))\n\u003c/pre\u003e\n\nPretty slick eh? ;) We get the terseness of the \u003ccode\u003eget\u003c/code\u003e as well as the plumbing for template inheritance. While macros are too advanced of a topic to delve into here, having them around when you're templating HTML is incredibly powerful.\n\nThe remainder of \"*template1.clj*\":src/tutorial/template1.clj is specific to Ring and Moustache, a routing library. We're not going to get too deep into that because these tutorials are about Enlive, not Ring and Moustache.\n\n\u003cpre\u003e\n(def routes\n  (app\n   [\"\"]       (fn [req] (render-to-response\n                         (index {})))\n   [\"change\"] (fn [req] (render-to-response\n                         (index {:message \"We changed the message!\"})))\n   [\u0026]        {:status 404\n               :body \"Page Not Found\"}))\n \n(defonce ^:dynamic *server* (run-server routes))\n\u003c/pre\u003e\n\nThis is the Moustache route defining syntax. A couple things to note \u003ccode\u003erender-to-response\u003c/code\u003e is not a function of Enlive, it's something I added via \"*utils.clj*\":src/tutorial/utils.clj in the repository. \u003ccode\u003erender-to-response\u003c/code\u003e isn't magic it's just a function that looks like this:\n\n\u003cpre\u003e\n(defn render [t]\n  (apply str t))\n\n(def render-to-response\n     (comp response render))\n\u003c/pre\u003e\n\nAll this does is take a list of strings, concatenates them into a single string, and serve back a proper Ring response. This is because when an Enlive template function is called it returns a list of strings.\n\nAlso note that our template function \u003ccode\u003eindex\u003c/code\u003e must be called with at least one parameter. The last bit of \"*template1.clj*\":src/tutorial/template1.clj is just boilerplate for starting and stopping the server.\n\nWell that's about it! You've seen your first Enlive template. While it may not seem like much yet, \u003ci\u003e*there was absolutely no mixing of code and HTML*\u003c/i\u003e. If you bear with me till the third template tutorial, I think you'll see just some how powerful this can be.\n\nh2. Your Second Template - Looping\n\nA common operation when generating web pages is looping over some piece of HTML because you need to present a list of items to the user. People just love lists. How can Enlive create lists of HTML when there's no code in the template?! We'll get into this in this tutorial.\n\nIf you don't have a Clojure REPL running start a new one with *lein repl* at the commandline from the tutorial repo's directory. Enter the following (if you're continuing from the previous tutorial you should should stop the Ring app for that tutorial first):\n\n\u003cpre class=\"console\"\u003e\ntutorial.template1=\u003e (.stop *server*)\nnil\ntutorial.template1=\u003e (load \"template2\")\nnil\ntutorial.template1=\u003e (in-ns 'tutorial.template2)\nnil\n\u003c/pre\u003e\n\nIf you're not continuing from a previous tutorial you can ignore \u003ccode\u003e(.stop *server*)\u003c/code\u003e and you'll need to be more specific about your loading, use \u003ccode\u003e(load \"tutorial/template2\")\u003c/code\u003e instead.\n\nOpen up the file \"*template2.html*\":src/tutorial/template2.html in your text editor and give it a quick look over. Then open the file *template2.html* in your favorite web browser. It's just page with a list of links, not that special. Point your browser at *http://localhost:8080/*. You should see pretty much the same thing except that we've dynamically inserted links.\n\nHow did we do that if we have no inline code to define the loop? Let's get into the code. Open up \"*template2.clj*\":src/tutorial/template2.clj in your favorite text editor. At the top of the file you should see the by now familiar namespace declaration. One thing we've changed is how we import Enlive functionality.\n\n\u003cpre\u003e\n(:use [net.cgrand.enlive-html\n         :only [deftemplate defsnippet content clone-for\n                nth-of-type first-child do-\u003e set-attr sniptest at emit*]]\n        [net.cgrand.moustache :only [app]]\n        [tutorial.utils :only [run-server render-to-response page-not-found]])\n\u003c/pre\u003e\n\nIn this tutorial we'd rather use the Enlive functions without having to qualify them. So we import them using \u003ccode\u003e:use\u003c/code\u003e and specify that we only want to import a specific set of definitions.\n\nAfter that we declare a variable for holding a dummy context which we're going to pass to our template.\n\n\u003cpre\u003e\n(def ^:dynamic *dummy-context*\n     {:title \"Enlive Template2 Tutorial\"\n      :sections [{:title \"Clojure\"\n                  :links [{:text \"Macros\"\n                           :href \"http://www.clojure.org/macros\"}\n                          {:text \"Multimethods \u0026 Hierarchies\"\n                           :href \"http://www.clojure.org/multimethods\"}]}\n                 {:title \"Compojure\"\n                  :links [{:text \"Requests\"\n                           :href \"http://www.compojure.org/docs/requests\"}\n                          {:text \"Middleware\"\n                           :href \"http://www.compojure.org/docs/middleware\"}]}\n                 {:title \"Clojars\"\n                  :links [{:text \"Clutch\"\n                           :href \"http://clojars.org/org.clojars.ato/clutch\"}\n                          {:text \"JOGL2\"\n                           :href \"http://clojars.org/jogl2\"}]}\n                 {:title \"Enlive\"\n                  :links [{:text \"Getting Started\"\n                           :href \"http://wiki.github.com/cgrand/enlive/getting-started\"}\n                          {:text \"Syntax\"\n                           :href \"http://enlive.cgrand.net/syntax.html\"}]}]})\n\u003c/pre\u003e\n\nThis of course would be something that we probably would have read out of a database. The take away here is that Clojure makes it easy to define nested data structures. \u003ccode\u003e*dummy-context*\u003c/code\u003e is just a hash-map (aka dictionary, aka associative array) of two key-value pairs. The first pair is for the title of the page. The second pair is the list of sections. Each section also has a title as well as a list of links. Each link has some text and url. If you're used to building up JSON data structures from database results this should be pretty familiar to you.\n\nh3. Figuring out your selectors\n\nUsing Enlive for templating usually involve two steps. The first step is figuring out which part of the markup you want to make into a component. Each component will become a snippet. A snippet is a reusable mini-template that you can use when constructing larger templates. In order to create a working snippet you need to determine the CSS selector which will allow you to match exactly that part of the document.\n\nConsider our situation. Our designer has handed us some nice markup and some CSS. To better convey the final result they have included some dummy content. With a traditional templating solution this is a big no no. With Enlive, working around it requires a minimal amount of effort. So the key here is to identify the \"model\" element.\n\nIn our case we have two distinct models, the first is the pair of the section title and the links for that section. The second is the individual link. In a templating DSL we would probably do something like the following:\n\n\u003cpre\u003e\n{% for section in sections %}\n\u003ch2 class=\"title\"\u003e{{ section.title }}\u003c/h2\u003e\n\u003cul class=\"content\"\u003e\n {% for link in section.links %} \u003c!-- Inner Loop --\u003e\n \u003ca target=\"new\" href=\"{{ link.href }}\"\u003e{{ link.text }}\u003c/a\u003e\n {% endfor %}\n\u003c/ul\u003e\n{% endfor %}\n\u003c/pre\u003e\n\nFirst, we want to be able handle the inner loop. On one level, as you're about see, there's a little more typing involved upfront when using Enlive. But you will end up with something that's considerably more reusable. In the traditional template the inner link loop and the outer section loop are hopelessly interwined. You may have many pages on your site that use the same section pattern but not the internal link pattern. But since these can't be separated you'll have to do some copy and paste. Not so with Enlive.\n\nSo let's define our link component. We don't want the dummy content so we really only want to match the very first link that satisfies our need, the selector looks something like this:\n\n\u003cpre\u003e\n(def ^:dynamic *link-sel* [[:.content (nth-of-type 1)] :\u003e first-child])\n\u003c/pre\u003e\n\nWe only want to match the first ul element that we find that has the content class and only the very first child inside that. This is the selector that gets the job done. It's analogous to:\n\n\u003cpre\u003e\n.content:nth-of-type(1) \u003e *:first-child\n\u003c/pre\u003e\n\nIt's important to note that using \u003ccode\u003enth-of-type\u003c/code\u003e requires an extra pair of brackets around the element that matches \u003ccode\u003e:.content\u003c/code\u003e. This extra pair of brackets is easy to forget. Whenever you want to be more specific about what type of element you want to match (beyond matching on CSS id or class) you'll need an extra pair of brackets.\n\nNow that we have our selector \u003ccode\u003edefsnippet\u003c/code\u003e will look like the following:\n\n\u003cpre\u003e\n(defsnippet link-model \"tutorial/template2.html\" *link-sel*\n  [{text :text href :href}] \n  [:a] (do-\u003e \n        (content text) \n        (set-attr :href href)))\n\u003c/pre\u003e\n\nSnippets are like templates with two main differences. First, snippets take a selector. This means that they can match only specific parts of an HTML document. The function produced by a \u003ccode\u003edefsnippet\u003c/code\u003e returns transformed content, \u003ci\u003enot\u003c/i\u003e a list of strings the way \u003ccode\u003edeftemplate\u003c/code\u003e does. This snippet destructures it's first argument (a hash-map) to extract the value of the keys \u003ccode\u003e:text\u003c/code\u003e and \u003ccode\u003e:href\u003c/code\u003e. We're also introduced to \u003ccode\u003edo-\u003e\u003c/code\u003e. This is a convenience, we often want to take the matched element and apply a series of transformations to it. In this case we want to set the content of the node as well as its href attribute.\n\nLet's try out our snippet to see that it worked:\n\n\u003cpre class=\"console\"\u003e\ntutorial.template2\u003e (render (emit* (link-model {:href \"bar\" :text \"foo\"})))\n\u003c/pre\u003e\n\nHere we have to use \u003ccode\u003eemit*\u003c/code\u003e because snippets return a sequence of nodes not strings the way templates do. \u003ccode\u003erender\u003c/code\u003e is just a utility function for taking a number of strings and creating a single string.\n\nOkay now we want to loop over the sections. A section is a *h2* tag followed by a *ul* tag. Again we need to figure out the correct selector. This time we're trying to emulate the following popular pattern for the outer loop:\n\n\u003cpre\u003e\n{% for section in sections %} \u003c!-- OUTER LOOP --\u003e\n\u003ch2\u003e{{ section.title }}\u003c/h2\u003e\n\u003cul\u003e\n  {% for link in section.links %}\n  \u003ca target=\"new\" href=\"{{ link.href }}\u003e{{ x.text }}\u003c/a\u003e\n  {% endfor %}\n\u003c/ul\u003e\n{% endfor %}\n\u003c/pre\u003e\n\nNote that unlike the previous example what we're looping over has no \"container\". That is, there is no surrounding element for the adjacent *h2* and *ul* tags. Enlive recently added support for \"ranges\" making it simple to express this pattern with Enlive templates.\n\nAgain our HTML has some dummy content again. We only care about the first range of *h2* and *ul* tags, we don't want to match any more than that. We can define a selector to do this like so:\n\n\u003cpre\u003e\n(def ^:dynamic *section-sel* {[:.title] [[:.content (nth-of-type 1)]]})\n\u003c/pre\u003e\n\nThere is no CSS selector that can represent this. Again take care to note that since we want to select only the first *ul* that we find, we need an extra pair of brackets around \u003ccode\u003e:.content\u003c/code\u003e. This is a common mistake to leave these out.\n\nNow that we have our selector we can define our section snippet like so. Pretty straightforward. Remember \u003ccode\u003edefsnippet\u003c/code\u003e just creates a function which can take whichever arguments you specify and returns the transformed markup. We're creating links using \u003ccode\u003elink-model\u003c/code\u003e and putting those links inside of the *ul* in the section.\n\n\u003cpre\u003e\n(defsnippet section-model \"tutorial/template2.html\" *section-sel*\n  [{:keys [title data]} model]\n  [:.title]   (content title)\n  [:.content] (content (map model data)))\n\u003c/pre\u003e\n\nNow let's look at the template to see how we put this all together:\n\n\u003cpre\u003e\n(deftemplate index \"tutorial/template2.html\"\n  [{:keys [title sections]}]\n  [:#title] (content title)\n  [:body]   (content (map #(section-model % link-model) sections)))\n\u003c/pre\u003e\n\nAs you can see it looks really similar to \u003ccode\u003esection-model\u003c/code\u003e. Again the main difference is that templates don't take selectors and the function they define returns a list of strings.\n\nThat's it. While we've seen some interesting features and while HTML and code separation is cool, so far you may think Enlive involves \u003ci\u003emore\u003c/i\u003e work then it actually saves. That's because we're showing a very trivial example. In the third example we'll demonstrate just how much time Enlive can save you when building something a little more real world.\n\nh2. Your Third Template - Template Inheritance\n\nWe now have a basic working idea of how templates work in Enlive. Templates are simply functions. Now it's still unclear if there is any real advantages to the Enlive way. Hopefully in this tutorial we can prove it's immense power.\n\nStart a REPL if you don't already have one running with *lein repl*. Type the following:\n\n\u003cpre class=\"console\"\u003e\ntutorial.template2=\u003e (.stop *server*)\nnil\ntutorial.template2=\u003e (load \"template3\")\nnil\ntutorial.template2=\u003e (in-ns 'tutorial.template3)\nnil\n\u003c/pre\u003e\n\nIf you're not continuing from a previous tutorial you should ignore \u003ccode\u003e(stop-app)\u003c/code\u003e and you'll need to be more specific about your loading, use \u003ccode\u003e(load \"tutorial/template3\")\u003c/code\u003e instead.\n\nPoint your favorite web browser to *http://localhost:8080/base.html*. You should see a fairly plain page. This is not a template. You can try opening up \"*base.html*\":src/tutorial/base.html as a file in your browser and see that it's identical to what is being served by Ring. Now point your browser at *http://localhost:8080/3col.html*. You should see another page that has a 3 column layout. Now point your browser at *http://localhost:8080/a*. The code required to do this follows:\n\n\u003cpre\u003e\n(defn viewa []\n  (base {:title \"View A\"\n         :main (three-col {})}))\n\u003c/pre\u003e\n\nIf you look at the markup for \"*base.html*\":src/tutorial/base.html and \"*3col.html*\":src/tutorial/3col.html you will see that there is not one line of code! So how did we magically put these two things together with so little code! Once you understand what's going, you'll see that template inheritance in Enlive is nothing more than combining some functions.\n\nTake a look at *http://localhost:8080/navs.html*. You should see some truly ugly nav bars ;) Now point your browser at *http://localhost:8080/b*. You can see it's easy to define a site wide layout, a 3 column middle main layout, and customize the contents of each column. Again there's absolute no code in the markup, only the following code is needed to construct this page:\n\n\u003cpre\u003e\n(defn viewb []\n  (let [navl (nav1)\n        navr (nav2)]\n   (base {:title \"View B\"\n          :main (three-col {:left  navl\n                            :right navr})})))\n\u003c/pre\u003e\n\nPretty slick. Templating with Enlive is just writing some Clojure code. This is different from even the good HTML templating solutions out there- few give you the full power of the language.\n\nOne last live example before we dive into the code. Point your browser at *http://localhost:8080/c/*. Huh, looks pretty much like b. Point your browser at *http://localhost:8080/c/reverse*. Notice something different?\n\nWe just flipped the two navs! How complicated is doing something like this?\n\n\u003cpre\u003e\n(defn viewc\n  ([] (viewc nil))\n  ([action]\n     (let [navs [(nav1) (nav2)]\n           [navl navr] (if (= action \"reverse\") (reverse navs) navs)]\n       (base {:title \"View C\"\n              :main (three-col {:left  navl\n                                :right navr})}))))\n\u003c/pre\u003e\n\nNothing more complicated than reversing a vector ;)\n\nSo how does this actually work? Open up \"*template3.clj*\":src/tutorial/template3.clj in your favorite text editor.\n\nh3. The Templates and Snippets\n\nThe first thing to look at is the *base* template.\n\n\u003cpre\u003e\n(html/deftemplate base \"tutorial/base.html\"\n  [{:keys [title header main footer]}]\n  [:#title]  (maybe-content title)\n  [:#header] (maybe-substitute header)\n  [:#main]   (maybe-substitute main)\n  [:#footer] (maybe-substitute footer))\n\u003c/pre\u003e\n\nRemember, \u003ccode\u003emaybe-content\u003c/code\u003e and \u003ccode\u003emaybe-substitute\u003c/code\u003e are not Enlive functions. They are two simple macros I've written for the purposes of this tutorial. \u003ccode\u003emaybe-content\u003c/code\u003e will only set the content of its node if its argument is not nil. \u003ccode\u003emaybe-substitute\u003c/code\u003e will only substitute its node if its argument is not nil.\n\nWe do this because we want the ability to handle template inheritance. Base represents the most basic template, and we can then \"inherit\" from it, overriding only specific elements. Note that this template uses \"*base.html*\":src/tutorial/base.html. You should look at this file now.\n\nNext is the \u003ccode\u003ethree-col\u003c/code\u003e snippet. It should be pretty obvious that this is a snippet for doing three column layout. Note that it uses \"*3col.html*\":src/tutorial/3col.html, you should take a look at this file.\n\nThe last bits are the various nav snippets and they are loaded from \"*navs.html*\":src/tutorial/navs.html. Again you should go over this file.\n\nh3. The Pages\n\nNow for the fun part. The pages are just functions no more and no less. The first page \u003ccode\u003eviewa\u003c/code\u003e is just rendering the base template with the title \"View A\" and setting the main block of the page to the 3 column snippet.\n\nThe page \u003ccode\u003eviewb\u003c/code\u003e does pretty much the same thing but this time we've added some navs for flair. Notice how much this function looks like \u003ccode\u003eviewb\u003c/code\u003e.\n\n\u003ccode\u003eviewc\u003c/code\u003e does pretty much the same thing but it checks to see if there is a parameter for reversing the navs. If present, the order of the navs is reversed.\n\nIt should be far more clear now what Enlive brings to the table over traditional templating solutions. While preparing your templates and snippets takes a little more work up front, building different pages from these templates and snippets is very, very fast and making changes is just moving a couple of values around in your functions, not mucking around with a crippled DSL. Your designer can create all the various widgets for your website using pure HTML and CSS and you can compose your pages from any combination of their designs.\n\nh2. Common Mistakes \u0026 Caveats\n\nh3. Converting Numbers\n\nWhen outputting numbers you need to convert them with \u003ccode\u003estr\u003c/code\u003e.\n\n\u003cpre\u003e\n[:div.foobar] (content (str 1))\n\u003c/pre\u003e\n\nSince snippets take a selector sometimes you might not have set this value correctly. This is usually the case if you're not seeing any output at all from a snippet. It's really easy to test a snippet - they're just functions.\n\nh3. Template out of date\n\nYour templates do not automatically reload. When you make edits to your HTML or your template code I recommend running the following at the REPL:\n\n\u003cpre\u003e\n(load \"your-library-name\")\n\u003c/pre\u003e\n\nIt's a minor annoyance for all the benefits you reap. It also wouldn't be too hard to create a system that reloaded templates (at least while in development mode) upon page refresh.\n\nBe careful, \u003ci\u003e*do not include the .clj extension*\u003c/i\u003e. Also \u003ci\u003e*do not use -'s in your file name*\u003c/i\u003e. If you want dashes you need to name the actual file using underscores.\n","funding_links":[],"categories":["\u003ca name=\"Clojure\"\u003e\u003c/a\u003eClojure"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswannodette%2Fenlive-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fswannodette%2Fenlive-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswannodette%2Fenlive-tutorial/lists"}