Find Blogue at https://github.com/maxim/blogue.
Ok, let’s be totally frank, I decided that I’m not a big fan of static site generators for personal blogging. Moreover, now that I’ve moved to Blogue, I feel like I should migrate our company blog from Jekyll as well. It would be easy, since Blogue can seamlessly become a part of any Rails app.
The thing is, I like to have an actual dynamic back-end behind my website. I am attracted to proper URI redirects, not having to wait for compilation, not having to always rely on 3rd parties for things like comments and search, and generally being able to program dynamic things that don’t rely on exponentially multiplying html files. Plus, it’s not like hosting is an issue, a free Heroku plan is all you need. That, and perhaps the free Memcachier addon. Let me use a fancy code block to demonstrate how this works out financially.
$0 + $0 = $0
Most of all, I like static files. I like my blog posts forever preserved in clean markdown for our descendants to discover a thousand years from now as the basis of their new religion. In fact I like them so much, I get angry when some static site generators restrict me from keeping markdown clean. They force me to insert custom HTML as soon as I want to do anything mildly interesting, even if I have an obvious convention going on.
My goal with Blogue was essentially to be able to have plain markdown posts rendered as a resource inside a Rails app, while having them parsed and preprocessed in any way I like, with the full power of Rails and rubygems ecosystem. I’d like to think that Blogue emphasizes making your own conventions when blogging, and gives you the tools to parse your personal style for the needs of HTML presentation. In essence, it solves some of the more annoying markdown restrictions that would force you to resort to writing straight HTML. So let me just demonstrate a few tricks.
By having your model inherit from Blogue::Post
you immediately plunge into a
colorful world of possibilities, which paints your otherwise boring, gray
reality1.
app/models/post.rb
class Post < Blogue::Post
# strawberry fields
end
With this in place you can do some interesting things with your markdown files.
Let’s say having followed the
README and having setup
the Rails app, you decided to create a first post at app/posts/first-post.md
.
~/dev/blog » touch app/posts/first-post.md
This alone will be enough to get a minimal post with some properties already
present on the @post
object.
>> Post.all.first.title
=> "First post"
>> Post.all.first.time
=> 2013-10-29 03:07:57 -0400
>> Post.all.first.author_name
=> "max"
Not bad for an empty file, you get a legit title, time, and name. You probably
can guess where they came from. Here are their definitions in Blogue::Post
.
def title
meta_title || parsed_title || filename_title
end
def time
meta_time || file_ctime
end
def author_name
meta_author_name || config_author_name
end
So it’s filename_title
, file_ctime
, and config_author_name
(which actually
comes from whoami
by default) respectively. Notice that they all try looking up
meta first. This refers to the very simple meta syntax I made up for placing
directly into markdown posts. It looks like this.
<!--meta
foo: bar
-->
This is similar to the front matter of jekyll posts, in case you’re familiar.
You can stick this anywhere in your post, and it’ll be parsed as YAML and made
available via Post#meta
. So as you can guess from the code, having either of
those values set in meta would override the default ones.
<!--meta
date: "Tue, 02 Dec 2008 13:07:00 +0000"
-->
FYI: I made it use the key date
because I think it sounds more natural.
Let me also quickly mention the parsed_title
, because it’s kind of nice. If
you start your blog post with an h1 level header, it will automatically become
the title. Like this.
# Welcome to my blog
...
>> Post.all.first.title
=> "Welcome to my blog"
I added this bit of magic because it’s my personal convention, it only works
with this exact title format (first line, starts with a single number sign). You
can easily change this convention by overriding parsed_title
method.
Sep 27, 2015: Updated to reflect changes introduced in Blogue 0.2.0.
Take a look at the table of contents at the top of this post. It’s dynamically generated, thanks to Kramdown, which has the TOC feature built-in. However, it requires you to add a weird looking special syntax into your markdown file.
* some list, presumably your table of contents
{:toc}
The intention is probably to have the TOC written-out by hand in markdown, and turned dynamic in HTML. Well, I didn’t want to have a TOC in my markdown, so I made a preprocessor that injects it only when rendering html.
I started by defining a class method preprocess
on the Post
as follows.
app/models/post.rb
class Post < Blogue::Post
class << self
def preprocess(template)
template.source.prepend("* toc\n{:toc}\n\n")
end
end
end
This takes the ActionView::Template
object and prepends the table of contents
declaration to the top. This method is not yet hooked up, but Blogue has a
convenient config property called Blogue.markdown_template_preprocessor
just for this
kind of thing. An initializer is a good place for it.
config/initializers/blogue.rb
Blogue.markdown_template_preprocessor = -> template {
Post.preprocess(template)
}
So now Blogue will run this piece of code before it processes the markdown.
Suddenly, table of contents. Well, perhaps I don’t want it on every post. No
biggie. I’ll just alter the preprocess
method like this.
def preprocess
if load_meta(template.source)['toc']
template.source.prepend("* toc\n{:toc}\n\n")
end
end
Suddenly table of contents became opt-in via meta flag toc: true
. Isn’t that
nice!
It’s actually extra nice that you get a whole ActionView::Template
object in
the Post.preprocess
, here’s why.
Let’s say you are rendering the post as part of an atom feed, and you don’t want a table of contents in there. Most likely your atom feed is generated in an .atom file similar to this.
app/views/posts/index.atom.builder
atom_feed do |feed|
feed.title "My Awesome Blog"
Post.all.each do |post|
feed.entry post, published: post.time do |entry|
entry.title post.title
entry.content( raw render(file: post.path) )
entry.author do |author|
author.name post.author_name
end
end
end
end
In this case, the template you get in your Post.preprocess
will have :atom
in its formats
array, letting you change the post based on this fact.
def preprocess
if template.formats.exclude?(:atom) && load_meta(template.source)['toc']
template.source.prepend("* toc\n{:toc}\n\n")
end
end
I think that is pretty great.
Now, the perceptive among you (or those who give a damn about any of this) may have noticed that prepending table of contents to the top isn’t going to work, because it would appear before the title. Well, in my blog setup I handle this by actually removing title, and rendering it together with the author and date in a separate partial. How do I remove title? In the same preprocessor.
def preprocess
if template.source.starts_with?('# ')
template.source.replace(template.source.lines[1..-1].join)
end
if template.formats.exclude?(:atom) && load_meta(template.source)['toc']
template.source.prepend("* toc\n{:toc}\n\n")
end
end
Now hopefully it all makes sense. This preprocessing trick really gives you infinite possibilities on the shape of your final html, while keeping your markdown posts intact.
Subject to psychedelic drug use. ↩