Sothr's Corner

Automatic Updated Dates on Posts With Octopress 2.x

One of the things I miss about my previous non-static blog was that it would automatically display the time of the last update. Since I store my site documents in a way that preserves file modified dates and timestamps across the replication I started to poke around how Octopress handles the concept of updates to existing posts. A little bit of digging revealed that including “updated: ” in the YAML front matter would render a modified date time in the footer of the blog post. Armed with this knowledge I back tracked the process of populating that information. After multiple dead ends and learning a bit about the structure of Octopress, Jekyll and Liquid I hit upon a solution. In Octopress the date and updated html are provided from the post object as ‘date_time_html’ and ‘date_time_updated_html’ to Liquid for usage in the templates. These are provided to liquid in an _includes snippet that can be found in ‘source/_includes/post/date.html’.

With a little digging, it was obvious that neither of those keys were being generated in the original Jekyll source or by Octopress plugins (in the plugin directory). A little bit of Google foo, however revealed the octopress-date-format gem and exposed how octopress was creating those pieces of data. The gem that was being loaded for me was in the 2.x branch as I am running Octopress 2.x (This was a major pain point for me as I was pulling my hair out trying to resolve why the modules I was looking for didn’t exist or were different. It turned out that Octopress 3 is in development and I was looking at the source of the plugin for that. Oops! Doh!). This Octopress date-time plugin works by attaching to the init hooks for pages and posts and dynamically generating the the html content for both the original date and updated fields.

Because the plugin works by attaching to the init hooks and I could find no way to guarantee an order to what is called by the hooks, I had to scrap the plan to also attach to init and simply populate the field if it didn’t exist. Instead I went the route of creating a generator that retroactively visits all the posts, evaluates them for an existing updated field. If the updated field doesn’t exist it then evaluates the modified time on the source file and uses that as the updated value. Now, becuase all of the init hooks have already run, we need to finally trigger an update so that the octopress-date-time plugin does it’s magic (Otherwise nothing will get displayed, because the ‘date_time_updated_html’ field won’t exist). Instead of looking into how to trigger a specific hook, I simply looked into the source code for the date-time plugin and made the ‘hack_date’ call against the post to update those fields. The major negative for approaching updated values this way is that the evaluation for populating the fields produced by the date-time plugin will be run twice on every post that doesn’t have an updated field already in the YAML front matter.

Below is the UpdatedGenerator.rb source code that automatically adds an updated date to all my posts dynamically.

UpdatedGenerator.rb Plugin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
require 'octopress-date-format'

module Jekyll
  # Auto set the updated YAML front matter if it isn't set and outside of the grace period
  class UpdatedGenerator < Generator
    def generate(site)
      # Loop through all posts
      site.posts.each do |post|
        # Only work on posts without an explicit modified date set
        unless post.data.key? 'updated'
          # Insert the file modified time
          updated = File.mtime(File.join(site.source,post.path))
          post.data['updated'] = updated
          #Fix the date formatting for the page
          Octopress::PageDate.hack_date(post)
        end
      end
    end
  end
end

If you use storage that maintains the modified time stamps on your source files, this will also work for you. (Hint! Git repositories do not maintain the modifed file timestamps and using this with them will cause indeterminite behavior.)

Comments