No More Octopress
I've been using Octopress for a while to serve my blog and as much as I like the fact that it handles a lot of things for me, I didn't like publishing with it. Posting an article took longer than it needed to because I had to:
- leave the browser
- open a terminal
- run a rake task (which means making sure I'm using the right Ruby version and gemset)
- open up my editor
- write my article
- run
rake preview
and check it in the browser to make sure it doesn't look like crap git add --all
git commit -m 'blah blah blah'
git push heroku master
It's a hassle to do this and if you wanted to edit an article later, you had to go through that entire process again. This is the reason I've mostly ignored the blog. I'll very likely be posting more often now that it can all be done from within the browser without all those context switches (editor, terminal, browser).
Powered by Perpetuity
The ORM used to manage all of my database queries is Perpetuity
, the Ruby Data Mapper-pattern ORM that I wrote. This was the fun part, actually. I got to play around and tease out a few patterns that might be useful. I even used the Postgres adapter I've been working on.
Notice the id formats of the articles: for this particular article the id is no-more-octopress
. This is the actual id of the article in the database and the article model itself doesn't even know about it. This was so simple to do with Perpetuity it made perfect sense to use here:
Perpetuity.generate_mapper_for Article do
id(String) { title.downcase.gsub(/\W+/, '-') }
attribute :title, type: String
# ...
end
The id(String) { ... }
DSL call lets the database know the generated value is a String
and will be generated by executing the given block against the object when it is inserted.
This way, I've got the SEO-friendly URLs without having to resort to putting slugs on the model. HTTP concerns shouldn't be on your model.
Markdown format
The format of the articles is Markdown, just like it was in Octopress, and they're converted to HTML on the fly. I also threw in a little caching so it's not running that conversion on every page load.
RSS/Atom Feed
To be honest, I didn't even check to see if there were any decent Ruby RSS-feed generators, but I figured that most of them would be built to work with ActiveRecord and similar libraries so I have my doubts that they'd work with PORO articles anyway. So I built my own. It wasn't all that difficult, really. It's just an object that you pass the values to and its to_xml
method spits out the XML.
I'll probably extract it at some point but I just wanted to get a feed working. My blog has a few RSS subscribers and I didn't want to break that for them, although the articles will probably appear to have been updated.
Maybe I'm wrong and existing RSS generators would work, but I'm not all that concerned with it. This was probably one of the most fun parts of building this blog engine (even though it was XML) because it's something I'd never done before.
Payload size
On Octopress, my blog index payload was about 350kB. Now, I'm at around 50-60kB, including assets. This is a HUGE win for viewing on mobile devices (though I don't have mobile-friendly styles yet; I'm working on that). I did this by:
- Removing social buttons and replacing them with links
- Writing only the CSS styles I needed on semantic markup
- Using only jQuery, Rails UJS, and Highlight.js for syntax highlighting on code examples
- Serving minified and gzipped assets
- Running the HTML through
Rack::Deflater
to reduce the HTML payload
Moved off of Heroku
This doesn't have anything to do with abandoning Octopress, but I also moved off of Heroku. This decision was primarily because the startup time of a Rails app on a sleepy Heroku dyno would have a severe impact on SEO. Adding a second dyno would keep it from going to sleep, but I'm not going to pay $35/month to host a blog. It's entirely possible that RSS subscribers would hit it often enough to keep it from going to sleep at all, but I wanted to try setting up a VPS to handle this anyway. It's been a while since I've had to do any ops stuff.
This blog is now running on my $5/month DigitalOcean droplet. This was a droplet I already had running, so the monthly fee isn't additional. My benchmarks show that this droplet far outperforms a Heroku dyno, too, so hopefully this improves SEO a bit — especially considering the payload benefits I mentioned above.
Le Fin
All in all, this was fun. I'd built several toy blog apps before and even Perpetuity's spec suite uses an Article
object in damn near every example (they have a diverse range of attribute types: String
title/body, Fixnum
views, Time
timestamps, etc), but actually deploying an app that uses Perpetuity's Postgres adapter was awesome to see.
I've still got some styling work to do, too, so that'll be fun. I'm not a designer, so if someone wants to contribute a design (even just a concept graphic), I'd be forever grateful. :-)