Updates and Architecture
It's been about two weeks since I released Cryogen to the public and the feedback has been great so I'd like to go over some of the changes that have been made thus far and a how everything fits together behind Cryogen.
Updates
Someone pointed out to me that posts or pages that were deleted from resources/templates
would have to be manually deleted from resources/public
in order for the sitemap and RSS feed to be updated correctly since these two files are generated based on the contents of the public
folder.
To remedy this, I updated the compiler to wipe out the public
folder and then generate all of its contents again with each compile. I was actually worried that this would increase compile time but Yogthos's blog has actually moved to Cryogen and found no problems with performance even with his 60+ existing posts.
Previously, any asset folders like css and javascript were stored under public
but then have now been moved to templates
. Everything in the public folder is now generated by the compiler. I've added the :resources
key to the config that lets you specify which asset folders you would like to copy over from templates
to public
.
:resources ["css" "js" "img"]
The copying is done by this simple function in src/cryogen/io.clj
using fs.
(defn copy-resources [{:keys [blog-prefix resources]}]
(doseq [resource resources]
(fs/copy-dir
(str "resources/templates/" resource)
(str public blog-prefix "/" resource))))
Cryogen also has Disqus support now. Simply register your blog on Disqus, add your disqus-shortname
to the config and set disqus?
to true
and Selmer will inject the info into the following script in the post template.
{% if disqus-shortname %}
<div id="disqus_thread"></div>
<script type="text/javascript">
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//{{disqus-shortname}}.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
{% endif %}
For long posts or pages with numerous headings, you can now generate a table of contents. Just add :toc true
to the metadata of the post or page where you want to generate a toc. For example:
{:title "Updates and Architecture"
:layout :post
:tags [:cryogen :clojure]
:toc true}
Some other updates:
- Can now customize the number of recent posts shown
- Sass support by turbopape
- Archive sorting fixed for users with a locale that isn't "en"
Architecture
Sometimes things are obvious to you when you design them but not necessarily to others. I've received a few questions regarding templating on the Cryogen repo so I thought I'd go over the basic architecture here.
The reason I created Cryogen was because I wanted a site generator (in Clojure!) that had a clear separation between content and layout. The idea is that you create HTML templates under resources/templates/html/layouts
with whatever layout/theme you want and the content of your posts and pages from resources/templates/md
gets injected by the compiler through Selmer.
Post/Page Layouts
Each markdown file representing a post or page must contain metadata about the layout in the :layout
key that corresponds to an HTML file under html/layouts
. For example, this post's metadata is as follows:
{:title "Updates and Architecture"
:layout :post
:tags [:cryogen :clojure]}
When the site gets built, this is what puts the post together:
(spit (str public (:uri post))
(render-file (str "templates/html/layouts/" (:layout post))
(merge default-params
{:servlet-context "../"
:post post
:disqus-shortname disqus-shortname})))
Selmer will render the post and the compiler will spit it out into the public
folder.
The second argument passed into render-file
is a map of items that Selmer will inject into the specified layout. In this case, that will be post.html
.
<div id="post">
...
<h2>{{post.title}}</h2>
...
<div>
{{post.content|safe}}
</div>
...
</div>
Here, post
is another map of items so {{post.title}}
and {{post.content}}
will render the values of :title
and :content
from post
. Pages and tags are compiled in a similar fashion.
Templates
This segues into how inheritance works in the templates. In the layouts
folder, base.html
contains the header, sidebar and footer of the site. These are the things that remain constant no matter what page or post you are viewing. The page/post content gets injected into the main reading pane with {% block content %}
:
<body>
<!--header-->
<!--sidebar-->
...
<div id="content">
{% block content %}
{% endblock %}
</div>
...
<!--footer-->
</body>
To inherit the base template, all the other html files in the layouts
folder start with a line of code that specifies the path of the base layout it should inherit followed by the layout for that page or post wrapped in {% block content %}
.
A short example is the layout for pages.
{% extends "templates/html/layouts/base.html" %}
{% block content %}
<div id="custom-page">
<div id="page-header">
<h2>{{page.title}}</h2>
</div>
{% if page.toc %}{{page.toc|safe}}{% endif %}
{{page.content|safe}}
</div>
{% endblock %}
And that's it! If you want to change the header, sidebar or footer - change it in base.html
. If you want to add more post or page layouts, create a new html file under resources/templates/html/layouts
and follow the above structure. If you want to add different filters to your content, please check out the Selmer docs.