I decided to drop tags and categories during the migration from WordPress to Jekyll, but it was not the best decision as it turned out.

I decided to use topics to organize entries as it feels just right because I can quickly separate a group of entries and automatically provide an RSS feed.

How to define one or more topics inside the blog post?

Use topics key to list topics.

---
layout: post
title:  Jekyll - How to use custom taxonomy or post parameters
topics:
 - Jekyll
 - Plugins
---
<p>contents</p>
Always treat topics key as an array. Even when it contains only one topic.

The origin

I will use the generator code available at the Jekyll’s plugins page and just modify it slightly each time to display topics, list posts, and create an RSS feed.

module Jekyll

  class CategoryPage < Page
    def initialize(site, base, dir, category)
      @site = site
      @base = base
      @dir = dir
      @name = 'index.html'

      self.process(@name)
      self.read_yaml(File.join(base, '_layouts'), 'category_index.html')
      self.data['category'] = category

      category_title_prefix = site.config['category_title_prefix'] || 'Category: '
      self.data['title'] = "#{category_title_prefix}#{category}"
    end
  end

  class CategoryPageGenerator < Generator
    safe true

    def generate(site)
      if site.layouts.key? 'category_index'
        dir = site.config['category_dir'] || 'categories'
        site.categories.keys.each do |category|
          site.pages << CategoryPage.new(site, site.source, File.join(dir, category), category)
        end
      end
    end
  end

end

List of the topics

To create a list of the topics (/topics/index.html file), look at the following plugin source code, which uses topic_list.html layout.

module Jekyll
  class TopicList < Page    
    def initialize(site, base, dir, topics)
      @site = site
      @base = base
      @dir = dir
      @name = 'index.html'

      self.process(@name)
      self.read_yaml(File.join(base, '_layouts'), 'topic_list.html')
      self.data['topics'] = topics
      self.data['title'] = "Topics"
    end
  end

  class TopicListGenerator < Generator
    safe true
    
    def generate(site)
      if site.layouts.key? 'topic_list'
        dir = 'topics'

        # get list of the unique topics
        topics = [];
        site.posts.each do |p|
          p.data['topics'].each do |t|
            topics << t if !topics.include? t
          end if p.data['topics']
        end

        write_topic_list(site, dir, topics)
      end
    end
  
    def write_topic_list(site, dir, topics)
      index = TopicList.new(site, site.source, dir, topics)
      index.render(site.layouts, site.site_payload)
      index.write(site.dest)
      site.pages << index
    end
  end
end

Notice that generate function inside TopicListGenerator class uses a topics array to pass further the list of the unique topics.

Use page.topics inside topic_list.html layout to access the above-mentioned topic list.

---
layout: index
---
{% for topic in page.topics %}
  <div class="topic">
    <h2>
      <a href="/topic/{{ topic | downcase }}/">{{ topic }}</a>
    </h2>
    <div>
      <small>
        <a href="/topic/{{ topic | downcase }}/atom.xml">RSS</a>
      </small>
    </div>
  </div>
{% endfor %}

Index pages

To create index pages for each topic (/topic/jekyll/index.html file and so on) containing only selected posts, look at the following plugin.

Notice that we pass further the topic and posts belonging to this topic.

class TopicIndex < Page    
 
    def initialize(site, base, dir, topic)
      @site = site
      @base = base
      @dir = dir
      @name = 'index.html'

      # get topic's posts
      topic_posts = []
      site.posts.each do |p|
        ((topic_posts << p) if p.data['topics'].include? topic) if p.data['topics']
      end

      self.process(@name)
      self.read_yaml(File.join(base, '_layouts'), 'topic_index.html')
      self.data['posts'] = topic_posts.reverse
      self.data['topic'] = topic
      self.data['title'] = "Topic \""+topic+"\""
    end
  end

  class TopicIndexGenerator < Generator
    safe true
    
    def generate(site)
      if site.layouts.key? 'topic_index'
        dir = 'topic'
        
        # get list of the unique topics
        topics = [];
        site.posts.each do |p|
          p.data['topics'].each do |t|
            topics << t if !topics.include? t
          end if p.data['topics']
        end
        
        # create page for each topic
        topics.each do |topic|
          write_topic_index(site, File.join(dir, topic.downcase), topic)
        end
      end
    end
  
    def write_topic_index(site, dir, topic)
      index = TopicIndex.new(site, site.source, dir, topic)
      index.render(site.layouts, site.site_payload)
      index.write(site.dest)
      site.pages << index
    end
  end
end

topic_index.html layout doesn’t contain anything new.

---
layout: index
---
<h1 class="page-title">{{page.title}}</h1>
{% for post in page.posts %}
	{% include content-on-the-list.html %}
{% endfor %}

Atom feeds

It is almost identical to the above plugin as I just changed names and layout (/topic/jekyll/atom.xml file and so on).

class TopicAtom < Page    
  
    def initialize(site, base, dir, topic)
      @site = site
      @base = base
      @dir = dir
      @name = 'atom.xml'

      # get topic's posts
      topic_posts = []
      site.posts.each do |p|
        ((topic_posts << p) if p.data['topics'].include? topic) if p.data['topics']
      end
      
      self.process(@name)
      self.read_yaml(File.join(base, '_layouts'), 'topic_atom.xml')
      self.data['posts'] = topic_posts.reverse
      self.data['topic'] = topic
      self.data['title'] = "Topic \""+topic+"\""
    end
  end

  class TopicAtomGenerator < Generator
    safe true
    
    def generate(site)
      if site.layouts.key? 'topic_atom'
        dir = 'topic'
        topics = [];
        
        # get list of the unique topics
        site.posts.each do |p|
          p.data['topics'].each do |t|
            topics << t if !topics.include? t
          end if p.data['topics']
        end
        
        # create RSS feed for each topic
        topics.each do |topic|
          write_topic_atom(site, File.join(dir, topic.downcase), topic)
        end
      end
    end
  
    def write_topic_atom(site, dir, topic)
      index = TopicAtom.new(site, site.source, dir, topic)
      index.render(site.layouts, site.site_payload)
      index.write(site.dest)
      site.pages << index
    end
  end
end

topic_atom.xml does not use any other layout as it is just a simple Atom feed.

---
layout: nil
---
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>sleeplessbeastie's notes - {{ page.topic }}</title>
 <link href="{{ site.production_url }}/topic/{{ page.topic | downcase }}/atom.xml" rel="self" />
 <link href="{{ site.production_url }}/topic/{{ page.topic | downcase }}/" />
 <updated>{{ page.posts.first.date | date_to_xmlschema }}</updated>
 <id>{{ site.production_url }}/</id>
 <author>
   <name>Milosz Galazka</name>
 </author>
 {% for post in page.posts offset: 0 limit: 10 %}
 <entry>
   <title>{{ post.title }}</title>
   <link href="{{ site.production_url }}{{ post.url }}/" />
   <updated>{{ post.date | date_to_xmlschema }}</updated>
   <id>{{ site.production_url }}{{ post.url }}/</id>
   <content type="html">{{ post.content | postmorefilter: post.url | xml_escape }}</content>
 </entry>
 {% endfor %}
</feed>