Mount Jekyll into a Rails App

I decided to use Jekyll as bloging solution for my new site.
It's a little ruby tools that turns markdown into a complete blog made of static assets.
But what if you need to add dynamic features over it ?
You can mount it into a Rails application.

I tryed to use the Bloggy gem but it didn't bahaved as wanted.
However it gave a me a great idea of you to do it.
Let's see how to handle this.


Make Jekyll to output into Rails public folder

I assume you already know Jekyll and Rails and you have a ready blog called my_blog.

 # Create a new rails application
 $>rails new my_app

 # Copy the blog sources into a jekyll folder
 $>cp -rf my_blog my_app/jekyll

 # Remove the existing _site folder
 $>rm -rf my_blog/jekyll/_site

Then just add destination: ../public/my_blog into jekyll/_config.yml. If like me you want jekyll not to be nested but served from the root, remove the /my_blog from the config line. However, be aware that jekyll may replace existing public file without noticing you, so act carefully if you have important file into your public directory.

Check your Rails environement files and turn the static file service on: config.serve_static_assets = true.

To check if the fresh configuration works as expected:

 # build the site/blog
 $>cd my_app/jekyll
 $>jekyll build
 # Every static files should go into my_app/public instead of my_app/jekyll/_site

 # run the rails app
 $>cd .. && rails s
 # open localhost:3000 to check if everythings is served correctly

Make it more conveniant

I wanted the blog manipulation more direct. What about some rake tasks to make our life easier ?

Create my_app/lib/tasks/jekyll.rake:

namespace :jekyll do
  desc 'Generate a new post and open it with textmate'
  task :new_post, [:title, :date] do |t, args|
    title = args[:title]
    if args[:date]
      date = DateTime.parse params[:date]
    else
      date = DateTime.now
    end

    short_date = date.strftime '%Y-%m-%d'
    post_path = "./jekyll/_posts/#{short_date}-#{title}.md"

    File.open('./config/jekyll/post_template.md', 'r') do |source|
      content = source.read.
        gsub('TITLE', title).
        gsub 'DATE', date.strftime('%Y-%m-%d %H:%M:%S')

      File.open(post_path, 'w') do |new_post|
        new_post.write content
      end
    end

    `mate #{post_path}`
  end

  desc 'Build jekyll assets'
  task :build do
    puts "Build jekyll assets"
    `cd jekyll && jekyll build`
  end

  desc 'Watch jekyll'
  task :watch do
    puts "Watch jekyll"
    `cd jekyll && jekyll build --watch`
  end
end

To complete this step just create a template for your posts config/jekyll/post_template.md. TITLE and DATE will be replaced by the given task call values. If date is not provided, then now is used by default.

  ---
  layout: post
  title:  "TITLE"
  date:   DATE
  categories: Misc
  ---

  `WRITE ME`

Now we can:

 $>rake jekyll:new_post[my-super-post,2014-06-01]
 # Create a 2014-06-01-my-super-post.md and open it with textmate
 $>rake jekyll:new_post[my-super-post]
 # Create a my-super-post.md with now as date and open it with textmate
 $>rake jekyll:build
 # Build assets
 $>rake jekyll:watch
 # Launch a jekyll watcher to update assets

If you want to play clean with your git (or whatever) repository, you should ignore public/* and hook rake jekyll:build on your deployement scripts. This way, only sources in the jekyll folder are tracked, generated files remaining local. This prevents future version mess up ... you know what I mean.

Performances ?

Do you remind we switched on the static assets service ?
Well, we should better not. Ruby is slow and so should not be used for such purpose.
However, how many billion of user per seconds gonna read our three first blog posts ? The question was both sarcastic and retoric.

If you really have to take care of performances, then you have to tune your web services (Nginx, Passenger, Puma, Apache, or whatever madness engine you like ...) to take care of those files directly.

Done

If you enjoyed this post, if you have questions, or disagree with something, yell in the comments below.