There are many possible blogging platforms out there to be used, yet we decided not to use any of them. It was a controversial decision even inside our own, small team. Before we started blogging there was a heated discussion whether we should use something that can quickly get you running so that when you feel in a mood for a blog post, there are no obstacles preventing you from writing. Or the alternative was to build something custom and have more control. We ended up using existing tools but put a little effort to combine them together into something that we like.

Nanoc as a starting platform

We are developers, we use console daily, feel comfortable using it and prefer text over fancy GUI. We like to be in control. But there is no need to reinvent the wheel so we started with nanoc. It even comes with built-in helpers for blogging. Built with Ruby so perfect fit for a company that started by creating Rails application. I won’t go into much details about Nanoc itself as you can read more about it on its website and see basic nanoc blog example on github. So Nanoc gives us the bricks to build a house, a basic infrastructure or framework for blogging as some would call it (because it seems that these days everything can be called a framework).

Pro:

  • developers friendly
  • markdown for writing blog posts
  • rsync for deployment to our server
  • written in Ruby (good for us, Ruby developers)

Cons:

  • written in Ruby (bad for non-technical people, but we do not have such in our team)

Responsive layout

About 20% of our traffic comes from mobile devices. I love reading blog posts on my mobile when moving around the city. Especially those that are comfortable, so no wonder that it was important for us to keep readers using mobile devices happy. We achieved it simply by using bourbon. You can see how our blog looks when using different devices using responsive.is (seriously, click the link to see it). Overall we are very happy with bourbon and started using it in many other projects.

However we’ve been hit by one bug when using it with nanoc. You can read more about how to properly use bourbon and nanoc on dutygeeks blog and see the issue on github if your are curious.

I really like lightboxes for displaying images from posts. They work great on desktops. However I have yet to see a lightbox that works great on desktop and mobile device. I tested dozen of them and every one had an issue on mobile. Is it with zooming, taking too much space, bad navigation, poor performance, lack of support for swipe, or too small button for closing the popup with image. So our solution is very simple. We use lightbox on desktop and turn it off for mobiles. Clicking an image on mobile will simply display the image itself using your mobile browser. And guess what… It works great. It is super fast, pinch-to-zoom works flawlessly, the image is displayed with proper zoom and when you want to close it you just press “Back” and browsers redirect you back to the previous page (the blog post) and position the screen at the place where you finished reading. Works way better than any lightbox that I have tested.

However if you think that I am wrong and there is lightbox of such high quality, please leave a comment. I would be more than happy to try it out.

Images

For every image we generate additional 2 versions.

Thumbnail that can be used when displaying multiple images in a gallery box:

And “fit” version that is as wide as it can be on a tablet in landscape position.

The code for doing that is pretty straightforward and uses ImageMagick convert binary:

class Thumbnailize < Nanoc::Filter
  identifier :thumbnailize
  type       :binary

  def run(filename, params={})
    system(
      'convert',
      '-resize',
      params[:width].to_s,
      filename,
      output_filename
    )
  end
end
compile '/assets/images/*/', rep: :thumbnail do
   filter :thumbnailize, :width => '160x160'
end

compile '/assets/images/*/', rep: :fit do
   filter :thumbnailize, :width => '530x530'
end

route '/assets/images/*', :rep => :thumbnail do
  item.identifier.chop + '-thumbnail.' + item[:extension]
end

route '/assets/images/*', :rep => :fit do
  item.identifier.chop + '-fit.' + item[:extension]
end

Other nice optimization are yet to be added such as:

Code

Nothing fancy here, we use good, old pygments with a ruby wrapper .

compile '/posts/*/' do
  filter :erb
  filter :redcarpet, options: {
    fenced_code_blocks: true, 
    autolink: true
  }
  filter :colorize_syntax, default_colorizer: :pygmentsrb
  layout 'post'
end

Does it look good ? You bet :) Here is an example:

class AviaryController < ApplicationController
  skip_before_filter :verify_authenticity_token, only: [:create]

  def create
    @user = User.last
    @user.remote_avatar_url = params[:url]
    @user.save!
    head :created
  end
end

Newsletter

If you ever read our blog before you might have noticed that we usually try to end our posts with call to action. That is in most situations invitation to one of our newsletters or link to chillout.io landing page. Fortunately we don’t need to add them manually. Instead we use a feature of nanoc which let’s you include metadata to every post.

At the end of post layout file we use custom #newsletter helper method to output proper code based on the metadata. If we ever decide to change it, we can do so in one place.

<section class='metadata'>
  <p class='date'>
    <time datetime="<%= post_date(item, :iso) %>" pubdate>
      <%= post_date(item) %>
    </time>
  </p>
  <p class='author'>
    by <%= item[:author] %>
  </p>
  <p class='tags'>
    <%= tags_for(item, none_text: "", base_url: "#") %>
  </p>
  <p class='comments'>
    <a href='#disqus_thread'></a>
  </p>
</section>

<%= yield %>

<%= newsletter(item[:newsletter]) %>

Here is the simplified version of the helper:

module NewsletterHelper
  Newsletters = {
    arkency: {
      url: "http://eepurl.com/mD-Hn",
      action: "Subscribe to our tech newsletter.",
      ad: "Sharpen the saw."
    },

    chillout: {
      url: "http://chillout.io",
      action: "Chill Out",
      ad: "And Impress Your Customers"
    },
  }

  def newsletter_link(type, newsletter)
    html = <<-HTML
      <a href="#{newsletter.url}" data-event-category="newsletter" data-event-action="#{type}" data-event-label="after post">#{newsletter.action}<br><strong>#{newsletter.ad}</strong></a>
    HTML
  end

  def newsletter(type)
    newsletter = OpenStruct.new Newsletters[type]
    inner_html = newsletter_link(type, newsletter)
    html = <<-HTML
      <section class="newsletter newsletter-#{type}">
        #{inner_html}
      </section>
    HTML
    html
  end
end

Thanks to custom google analytics events we can easily track user actions on our blog. We described this technique in our previous “Google Analytics for developers” post

Beta

Before we publish a new post we first ask for opinion our coworkers. We show them new entries by deploying to different internal host visible only for those connected via VPN. Nanoc supports multiple deployments natively in config.yaml :

deploy:
  default:
    kind: rsync
    dst:  "blog@production-blog.arkency:current/public"
    options: ['-gpPrtvz', '--delete-after']
  beta:
    kind: rsync
    dst:  "blog@staging-blog.arkency:current/public"
    options: ['-gpPrtvz', '--delete-after']

Hosting

We host our blog in our own infrastructure that we manage with chef for our customers. It is built around LXC containers and our blog requires only nginx for serving it. It works nicely even when you hit front page of Hacker News or Reddit. And if we decide to serve our blog using SPDY there is nothing to stop us.

You can expect more blog posts about the infrastructure and SPDY experiment soon.

Conclusion

You might be tempted to say that this is lot of effort just to have a company blog. Probably you are right, but on the other hand we have fun, knowledge and area for experiments that teach us valuable lessons and can be further used in other, commercial projects.

Blogging and sharing knowledge is one thing, having fun and learning new things is another. But nobody said we cannot mix those two. Blog is an important medium for us to communicate our ideas but also is a little toy project. Do you have a toy project in your company to test your crazy ideas in the wild ?

comments powered by Disqus