Permalink
165 lines (125 sloc) 5.96 KB

Table of Contents generated with DocToc

Writing Templates

Suppose you've written a fabulously popular library, used the world over by adoring fans. For the purposes of this document, let's say this library is called "liquid-cool". If using liquid-cool takes a bit of setup, or if you'd just like to give your users a little guidance on how one might best create a new project which uses liquid-cool, you might want to provide a template for it (just like how lein already provides built-in templates for "app", "plugin", and so on).

Let's assume your library's project dir is ~/dev/liquid-cool. Create a template for it like so:

cd ~/dev
lein new template liquid-cool --to-dir liquid-cool-template

Your new template would look like:

liquid-cool-template
├── LICENSE
├── project.clj
├── README.md
├── resources
|   └── leiningen
|       └── new
|           └── liquid_cool
|               └── foo.clj
└── src
    └── leiningen
        └── new
            └── liquid_cool.clj

Note that you'll now have a new and separate project named "liquid-cool-template". It will have a group-id of "liquid-cool", and an artifact-id of "lein-template".

All lein templates have an artifact-id of "lein-template", and are differentiated by their group-id, which always should match the project for which they provide a template.

Structure

The files that your template will provide to users are in resources/leiningen/new/liquid_cool. The template generator starts you off with just one, named "foo.clj". You can see it referenced in src/leiningen/new/liquid_cool.clj, right underneath the ->files data line.

You can delete foo.clj if you like (and its corresponding line in liquid_cool.clj), and start populating that resources/leiningen/new/liquid_cool directory with the files you wish to be part of your template. For everything you add, make sure the liquid_cool.clj file receives corresponding entries in that ->files call. For examples to follow, have a look inside the *.clj files for the built-in templates.

Testing Your Template

While developing a template, if you're in the template project directory, leiningen will pick it up and you'll be able to test it. e.g. from the liquid-cool-template dir:

$ lein new liquid-cool myproject

will create a directory called myproject, built from your template. Alternately, if you want to test your template from another directory on your system (without publishing your template to clojars yet), just run:

$ lein install

You should then be able to run lein new liquid-cool myproject from any directory on your system.

Templating System

The default generated template uses stencil for templating, which implements the language-agnostic templating system Mustache. All the available tag types can be found in the Mustache manual; we will only go through the most common tag type here.

Suppose we want to add in a standard markdown readme file where the input name is the main header of the file. To be able to do so, we must do two things: Ensure that the input name is contained within the data mapped to the key X, and that we have a template file which looks up the key X by wrapping it in double mustaches like so: {{X}}. As for our input name, data already contains the line :name name, which means we can lookup the input name by writing {{name}} in the template file. To try it out, save the following contents in the file resources/leiningen/new/liquid_cool/README.md:

# {{name}}

This is our readme!

And add the following line right underneath the ->files data line:

["README.md" (render "README.md" data)]

Now, if we for instance say lein new liquid-cool liquid-cool-app, the newly generated project will contain a file named README.md where the header is liquid-cool-app.

A warning about Mustache tag delimiters

Clojure syntax can conflict with the default mustache tag delimiter. For example, when destructuring a nested map:

(let [{{:keys [a b]} :ab} some-map]
  (do-something a b))

Stencil will interpret the {{ as the start of a mustache tag, but since the contents are not valid mustache, the render fails. To get around this, we can change the mustache delimiter temporarily, like so:

{{! Change mustache delimiter to <% and %> }}
{{=<% %>=}}

(let [{{:keys [a b]} :ab} some-map]
  (do-something a b))

<%! Reset mustache delimiter %>
<%={{ }}=%>

Distributing your Template

Templates are just maven artifacts. Particularly, they need only be on the classpath when lein new is called. So, as a side-effect, you can just put your templates in a jar and toss them on clojars and have people install them like normal Leiningen plugins.

In Leiningen 2.x, templates get dynamically fetched if they're not found. So for instance lein new heroku myproject will find the latest version of the heroku/lein-template project from Clojars and use that.

Users of Leiningen 1.x (1.6.2 or later) can also use the template if they install the lein-newnew plugin:

$ lein plugin install lein-newnew 0.3.6
$ lein new foo
$ lein new plugin lein-foo