Jamie Gaskins

Ruby/Rails developer, coffee addict

No More Octopress

Published Feb 16, 2014

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
  # ...

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. :-)