Creating Leiningen Templates

December 16, 2012

If you've used Leiningen before, you've already seen templates in action. When you create a project using lein new myproject, you end up with a project folder with a namespace called myproject and a core.clj inside it.

The templates are really useful if you need to setup some common boilerplate for your project. In the last post I referenced a template for Compojure, which creates a new batteries included project.

Leiningen uses the lein-newnew plugin for this task. All you have to do to create a new template is to run lein new template <template name>. In my case I created a template called compojure-app:

lein new template compojure-app

As all Leiningen projects, it will contain the project.clj, which will contain the description for our project:

(defproject compojure-app/lein-template "0.2.7"
  :description "Compojure project template for Leiningen"
  :url "https://github.com/yogthos/compojure-template"
  :eval-in-leiningen true
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[leinjacker "0.2.0"]])

It looks like a regular project file, except for the eval-in-leiningen key which causes Leiningen to launch a subprocess prevents Leiningen from launching a separate process for the given project during the build time.

The actual template resides under

src/compojure-template/leiningen/new/compojure_app.clj

It looks as follows:

(ns leiningen.new.compojure-app
  (:use [leiningen.new.templates :only [renderer sanitize year ->files]]
        [leinjacker.utils :only [lein-generation]]))

(def project-file
  (if (= (lein-generation) 2)
    "project_lein2.clj"
    "project_lein1.clj"))

(defn compojure-app
  "Create a new Compojure project"
  [name]
  (let [data {:name name
              :sanitized (sanitize name)
              :year (year)}
        render #((renderer "compojure_app") % data)]
    (println "Generating a lovely new Compojure project named" (str name "..."))
    (->files data
             [".gitignore"  (render "gitignore")]
             ["project.clj" (render project-file)]
             ["README.md"   (render "README.md")]
             ["src/{{sanitized}}/handler.clj"      (render "handler.clj")]
             ["src/{{sanitized}}/server.clj"       (render "server.clj")]
             ["src/{{sanitized}}/common.clj" (render "common.clj")]
             ["resources/public/css/screen.css" (render "screen.css")]
             "resources/public/js"
             "resources/public/img"
             "src/{{sanitized}}/models"
             ["test/{{sanitized}}/test/handler.clj" (render "handler_test.clj")])))

The compojure-app function is where all the fun happens, and it's what gets called when we run lein new compojure-app myapp to create an application using this template.

The function is mostly self explanatory. It uses the render function from leiningen.new.templates to take the template files and put them at the specified path. The {{sanitized}} tag ensures that the generated names for the package folders are valid.

Our template files live under

src/compojure-template/leiningen/new/compojure_app
and they don't need to have the same folder structure as the resulting project. As you can see above, we specify the resulting path explicitly in our template.

The template files look exactly like any regular Clojure source file, except for the {{name}} anchor. This will be replaced with the name of the application we specified when creating the project. Here's the common.clj template as an example:

(ns {{name}}.common
  (:use [hiccup.page :only [html5 include-css]]))
       
(defn layout [& body]
  (html5 
    [:head
     [:title "Welcome to {{name}}"]
     (include-css "/css/screen.css")]
    (into [:body] body)))

Every occurrence of {{name}} will be replaced with myapp instead and we'll have our namespace and greeting customized.

Once you've created your template, you'll need to install it using lein install and then add it as a plugin to your profile under ~/.lein/profiles.clj using the following format:

{:user
  {:plugins [[compojure-app/lein-template "0.2.7"]]}}

That's it, you can now use your new template and never have to write boilerplate for this kind of project again.

If you wish to make your template available to others you can publish it to Clojars by running lein deploy clojars from the console.

Any template published on Clojars can be used directly without needing to add it to your plugins in the profiles.clj as shown above.

The complete source for the template discussed in this post is available here.

Tags: clojure

Copyright © Dmitri Sotnikov

Powered by Cryogen