Web Application Development with Clojure – Part 3
by Vijay Kiran
This post is part of the Web Application Development with Clojure tutorial. You might want to read the previous posts before this post for continuity’s sake.
- Part 1: Project Setup
- Part 2: Data model definition with Lobos and Korma
- Part 3: Loading Fixtures with clj-yaml and HTML Templating with Enlive
- Part 4: Adding CSS, Post detail page and Simple Authentication
- Part 5: Blog Administration Area
Introduction
In the previous part of this tutorial we created a couple of tables using migrations – authors table and posts table. In this part first I’ll describe how to load some test data (a.k.a ‘fixtures‘) into our database tables. We’ll use YAML to define a couple of authors and 3 blog posts and we’ll parse the YAML file and load the data using the clj-yaml library.
Also we’ll create our first template and render the blog’s home page with the sample data from the database.
Source code on github
The code for this series is now available on github and the source code is tagged with part names. If you want to checkout the code for a specific part of this tutorial you can do so using the following command:
git clone git://github.com/vijaykiran/clog.git cd clog git checkout part3 |
In the previous while creating the tables using migrations, I overlooked and made a mistake in naming the tables. If you are using PostgreSQL then the word “user” is a reserved word, so you should rename the table to authors – I’ve updated the code accordingly. Fixtures are used to populate the database with some initial data. Though fixtures are mostly used for loading test data we can also use fixtures to bootstrap the content. First let us create a file called fixtures.yml under resources folder. If you don’t have a resources folder in your project, create it under the root directory so that will be at the same level as src folder. Edit the fixtures.yml and add the following: Now, fire up the Clojure REPL in IntelliJ IDE using Tools -> Start Clojure Console. We’ll load the clj-yaml library and parse the yaml file. If everything went well, you should see the map with keys :authors and :posts in the console as shown below: Now to add the authors to the database all you need is the following couple of lines: The second line in the above code snippet reads the fixtures file using slurp, parses the yaml using parse-string which returns a map with keys :authors and :posts. We use clojure’s maps-as-functions-of-keys features to get the list of authors and pass them to Korma’s insert function. Once this is executed you can see the data is now inserted into the Users table. Now that we have our sample data populated in the database, it is time to get started with our first template. Clojure has a couple of libraries that can help you with dealing with HTML viz, Hiccup and Enlive. Hiccup is a DSL for generating HTML and Enlive is a selector based templating engine. I’m a bit biased towards Enlive – since I have more freedom of styling and HTML stays HTML. So in this tutorial I’m using Enlive. Enlive provides a macro called deftemplate to define template functions. The macro is defined as follows: The forms you pass in are actually used to transform the html. I think it will be easier to understand with an example that you can follow along. First let us create the home.html under resources folder. By convention the templates are loaded from the resources directory. We will place all our template definitions in clog.templates namespace which lives in cleverly named templates.clj In the above snippet we are using deftemplate macro to create a template function called home-page which takes the file home.html and transforms the file using the forms, in our case only one which is: This says find out the tag :title in the home.html and replace its contents with “Clog – the clojure blog engine!”. To test this out you can load the namespace in repl and run the home-page function. That will print out the transformed template as shown below. Now this template function can be used to generate the transformed html we need to send to the client. To connect the these views to the requests, we will use simple controller functions. These functions/handlers are then used to transform the templates using the request parameter and data from the database. For example we want our blog to respond to something like http://host:port/posts/1 by sending out the html for the post with id 1. This logic is wrapped inside a controller function. We’ll put our controller functions in the clog.controller namespace. Here’s the initial version of the controller which has handler for the index page. Now to see this function in action, we need to update the routes of the application in core.clj to call clog.controller/index. Here’s the complete core.clj after the required modifications: Start the server as shown below, and you should see the home page with title in the window title bar “Clog – the clojure blog engine!”. The home is working, but it is rather boring and doesn’t show any real data. Let us try to show the two posts we added in the database. First we need to update the home.html to create a place holder for a post. Now let us update the template and add another transformation. In this case we want to “clone” the div with class post and replace the content of divs with classes title and content. Here’s the updated deftemplate’s code that does exactly the same. We are using clone-for macro and using the forms to transform the sub elements. Now we need to make sure that our controller function sends the required data to the template. We are selecting all the posts using Korma select function and passing it on to home-page function. The result of the home-page template function – which is the html with posts populated is passed on to response. After making these changes, you should see the list of posts in the home page. In this part we saw how to glue things together and get the content from database, render it in a page using templates. In the next part we’ll finish by updating the home template and adding another template to display the post. Also, in the next part we’ll start working in creating an admin area with authentication. Make sure you subscribe to the RSS feed or follow me on Twitter to get notified.Loading Fixtures
authors:
- id: 1
username: "john"
password: "clojure-blogger"
email: "john@clojure-blog.org"
- id: 2
username: "jane"
password: "clojure-blogher"
email: "jane@clojure-blog.org"
posts:
- title: "Hello World!"
text: "<em>Welcome to Clog, the World's most advanced Clojure Blog Engine!</em>"
status: true
author: 1
- title: "Hello World!, again!"
text: "Sayin 't 'gain! <em>Welcome to Clog, the World's most advanced Clojure Blog Engine!</em>"
status: true
author: 2;; load clj-yaml
(use 'clj-yaml.core)
(parse-string (slurp "./resources/fixtures.yml"))
{:authors
({:id 1, :username "john", :password "clojure-blogger", :email "john@clojure-blog.org"}
{:id 2, :username "jane", :password "clojure-blogher", :email "jane@clojure-blog.org"}),
:posts
({:title "Hello World!",
:text "<i>Welcome to Clog, the World's most advanced Clojure Blog Engine!</i>",
:status true, :author 1}
{:title "Hello World!, again!",
:text "Sayin 't 'gain! <i>Welcome to Clog, the World's most advanced Clojure Blog Engine!</i>",
:status true, :author 2})}
;;load korma library and models
(use 'korma.db 'korma.core 'clog.models)
(insert authors (values (:authors (parse-string (slurp "./resources/fixtures.yml")))))
(select authors)
;;output
[{:id 1, :username "john", :password "clojure-blogger", :email "john@clojure-blog.org"}
{:id 2, :username "jane", :password "clojure-blogher", :email "jane@clojure-blog.org"}]
(select posts)
....
Templates using Enlive
Defining Home Page Template
(defmacro deftemplate
"Defines a template as a function that returns a seq of strings."
[name source args & forms]
`(def ~name (template ~source ~args ~@forms)))
<!DOCTYPE html>
<html>
<head>
<title>Title Here</title>
</head>
<body>
Clog - Home Page!
</body>
</html>(ns clog.templates
(:use [net.cgrand.enlive-html]))
(deftemplate home-page "home.html" []
[:title] (content "Clog - the clojure blog engine!"))
[:title] (content "Clog - the clojure blog engine!")
(use 'clog.templates)
nil
user=> (home-page)
("<!DOCTYPE html>\n" "<html>" "\n" "<head>" "\n " "<title>" "Clog - the clojure blog engine!" "</title>" "\n" "</head>" "\n" "<body>\nClog - Home Page!\n</body>" "\n\n\n" "</html>")
A simple Controller
(ns clog.controller
(:use clog.templates
clog.models
ring.util.response))
(defn index
"Index page handler"
[req]
(->> (home-page) response)) ;; A sexier way to write (response (home-page))
(ns clog.core
(:use ring.adapter.jetty
ring.middleware.resource
ring.middleware.reload
ring.util.response
net.cgrand.moustache
clog.controller))
;; Routes definition
(def routes
(app
[""] (delegate index)))
;;; start function for starting jetty
(defn start [port]
(run-jetty #'routes {:port (or port 8080) :join? false}))
(defn -main []
(let [port (Integer/parseInt (System/getenv "PORT"))]
(start port)))
(use 'clog.core)
(start 9000)
List of Posts
<!DOCTYPE html>
<html>
<head>
<title>Title Here</title>
</head>
<body>
<div class="post">
<div class="title">Title of the Post</div>
<div class="content">Post Content</div>
</div>
</body>
</html>(deftemplate home-page "home.html" [posts]
[:title] (content "Clog - the clojure blog engine!")
[:div.post] (clone-for [post posts]
[:div.title] (content (:title post))
[:div.content (content (:content post))]))
(ns clog.controller
(:use clog.templates
clog.models
ring.util.response
korma.core))
(defn index
"Index page handler"
[req]
(->> (select posts) home-page response)) ;; Equivalent to (response (home-page (select posts))
Conclusion


Pingback: VijayKiran.com – Web Application Development with Clojure – Part 2
Pingback: VijayKiran.com – Web Application Development with Clojure – Part 1