Ognjen Regoje bio photo

MY NAME IS
Ognjen Regoje
BUT YOU CAN CALL ME OGGY


I make things that run on the web (mostly).
More /ABOUT me.

me@ognjen.io Twitter LinkedIn Instagram Github

Jekyll Combining an External Folder into Posts

I use markdown to take notes. Previously, I wanted make a link to that folder and publish only some of those notes using Jekyll.

Then I realized that I could also do that for posts.

First, I tried linking the folder, filtering it, and then combining it into the posts collection. This didn’t work. The post was not being rendered.

I found that I had to filter the linked collection, then loop through it and add the posts one by one to the posts collection.

This is the end result:

# _plugins/filter.rb
module Filter
  def self.process(site, payload)
    site.collections['linked'].docs.select!{|x| x.data['slug'].include? 'blog.linked'}
    now = Time.now

    site.collections['linked'].docs.sort_by{|x| Time.parse(x.data['publish_on'])}.each do |x|
      t = Time.parse(x.data['publish_on'])
      if t <= now or site.config['future']
        new_doc = Jekyll::Document.new(
          x.path,
          {site: site, collection: site.collections['posts']}
        )

        new_doc.read


        new_doc.data['date'] = t
        new_doc.data['draft'] = false
        new_doc.data['categories'] = x.data['categories']
        new_doc.data['description'] = x.data['desc']
        x.data.delete 'desc'
        new_doc.data['layout'] = 'post'
        new_doc.data['slug'] = x.data['slug'].gsub("blog.linked.", "").split('.').last
        new_doc.data['__coll'] = "posts"

        site.collections['posts'].docs << new_doc
      end
    end
  end
end

Jekyll::Hooks.register :site, :post_read do |site, payload|
  Filter.process(site, payload)
end

What it does

I decided on blog.linked as the prefix for the notes that I wanted to publish. This removes any files that don’t follow that.

site.collections['linked'].docs.select!{|x| x.data['slug'].include? 'blog.linked'}

Then I loop through all the remaining posts. They need to be added in the order of their publishing date.

site.collections['linked'].docs.sort_by{|x| Time.parse(x.data['publish_on'])}.each do |x|

This check is to not pull in posts that are schedule in future. This could be improved to listen for the --future switch. site.config['future'] is the setting whether future posts should be published and can be set in the config.yml or in the command line using the --future.

t = Time.parse(x.data['publish_on'])
if t <= now or site.config['future']

Create a Jekyll::Document that references the site and sets the correct collection.

new_doc = Jekyll::Document.new(
  x.path,
  {site: site, collection: site.collections['posts']}
)

read the document contents:

new_doc.read

Then clean up the front matter.

new_doc.data['draft'] = false
new_doc.data['categories'] = x.data['categories']
new_doc.data['description'] = x.data['desc']
x.data.delete 'desc'
new_doc.data['layout'] = 'post'
new_doc.data['date'] = Time.parse(x.data['publish_on'])
new_doc.data['slug'] = x.data['slug'].gsub("blog.linked.", "").split('.').last
new_doc.data['__coll'] = "posts"

With the document created, it’s added into the posts collection.

site.collections['posts'].docs << new_doc

Finally register the hook to run after the files have been loaded but before render. It needs to be run on post_read but before render so that it runs before pagination.

Jekyll::Hooks.register :site, :post_read do |site, payload|
  Filter.process(site, payload)
end

This way I don’t have to keep the notes in sync and any notes that I add will be automatically added to the site.

Update

I’ve also added the ability to fix links between files as well as correctly embed images.

new_doc.content.gsub!("", "")
new_doc.content.gsub!(/\[(.+)\]\(..\/(.+)\/(.+)\.md\)/, '[\1](/\3)')

I do still have to copy the images to the blog. At the moment I think this is a good thing because I make them smaller for the blog but the notes use the full resolution ones.

Another update

I’ve also added the ability to have parts of the markdown file excluded from being generated in Jekyll:

new_doc.content = new_doc.content.split("< !-- exclude -->").map{|x| x.split("< !-- include -->")}.select{|x| x.size > 1}.map{|x| x[1]}.join("")

Note the space in <!-- exclude-->. This is due to because the syntaxt highlighter plugin renders each term within a span which the interprets the rest of <!-- exclude--> as a comment.

#jekyll