1
votes

This problem is bothering me for a while now.

The generated Jekyll site does show the right collections and all it's info on the desired pages. But all collections other than the (default) posts collection can't be used in a ruby plugin.

Case:

We have a blog site using Jekyll. To enrich the blog posts with author info and vice-versa there are some custom plugins using Jekyll::Hooks.register

The blog posts are written in markdown language and all contain some info in YAML format:

---
layout: post
authors: [author]
title: 'title'
intro: a short intro text
image: /path/to/image.png
tags: [jekyll, collections]
category: Programming
comments: true
---

This is the code for enriching the post with author information:

Jekyll::Hooks.register :site, :post_read do |site|
    puts 'Enriching posts with link to authors'

    site.posts.docs.each { |post|
        post.data['authors'].collect! { |author_username|
            site.data['authors'][author_username]
        }.compact!
    }
end

Next step:

I updated the site with two more collections since we also want to show the training courses and talks that will be given.

Both collections contain a few files in markdown format with a same kind of YAML info at the top of the file.

---                               ---
layout: talk                      layout: training
speakers: [author]                trainers: [author]
title: Title                      title: Title
subtitle: Subtitle                subtitle: Subtitle
duration: 120 minutes             duration: 180 minutes
level: beginner                   level: intermediate
image: /path/to/image.png         image: /path/to/image.png
tags: []                          tags: []
category: Talks                   category: Training
comments: false                   comments: false
---                               ---

And I added the collections to the _config.yml

collections:
  training:
      output: true
      permalink: /training/:path/
  talks:
      output: true
      permalink: /talks/:path/

defaults:
  - scope:
      path: "training"
      type: training
    values:
      layout: training
  - scope:
      path: "talks"
      type: talks
    values:
      layout: talk

Desired implementation:

Now I want to enrich the talks and training courses with the authors as well.

Since the posts, talks and training files are all in a collection, I assume that changing the posts object to talks or training should obtain the desired objects and loop through them.

So I changed the custom plugin to this:

Jekyll::Hooks.register :site, :post_read do |site|
    puts 'Enriching posts with link to authors'

    site.posts.docs.each { |post|
        post.data['authors'].collect! { |author_username|
            site.data['authors'][author_username]
        }.compact!
    }

    site.talks.docs.each { |talk|
        talk.data['authors'].collect! { |author_username|
            site.data['authors'][author_username]
        }.compact!
    }
end

But then I get this error:

jekyll 3.7.3 | Error:  undefined method `talks' for #<Jekyll::Site:0x00007f9325268e90>

The thing that is bothering me is that when I log the collection contents I get the following result: (formatted to json structure for better readability)

{
  "posts"=>
    #<Jekyll::Collection @label=posts docs=[
      #<Jekyll::Document _posts/2017-02-01-page1.md collection=posts>, 
      #<Jekyll::Document _posts/2017-02-02-page2.md collection=posts>,
      #<Jekyll::Document _posts/2018-04-04-page3.md collection=posts>
    ]>, 
  "training"=>
    #<Jekyll::Collection @label=training docs=[
      #<Jekyll::Document _training/page1.md collection=training>, 
      #<Jekyll::Document _training/page2.md collection=training>, 
      #<Jekyll::Document _training/page3.md collection=training>
    ]>, 
  "talks"=>
    #<Jekyll::Collection @label=talks docs=[
      #<Jekyll::Document _talks/page1.md collection=talks>, 
      #<Jekyll::Document _talks/page2.md collection=talks>, 
      #<Jekyll::Document _talks/page3.md collection=talks>
    ]>
}

As far as I can tell, both the talks and the training collection are in the same level and data structure as the posts collection.

Question:

How can I accomplish to obtain the talks/training collection in my custom plugin so I can enrich the data with the author information.

I reduced the files and output for readability. Full codebase is available at GitHub

2

2 Answers

1
votes

You have to look into site.collections.

This can do the trick :

Jekyll::Hooks.register :site, :post_read do |site|

    ['posts', 'talks'].each do collection{
        site.collections[collections].docs.each { |talk|
            talk.data['authors'].collect! { |author_username|
                site.data['authors'][author_username]
            }.compact!
        }
    }

end
0
votes

David pointed me in the right direction.

I had to loop over the collections

site.collections.each do |collection|
  collection.each do |item|
    if item.respond_to?("each")
      item.docs.each do |post|
        if post.data['authors']
          post.data['authors'].collect! { |author_username|
            site.data['authors'][author_username]
          }.compact!
        end
      end
    end
  end
end

I still find it strange that the other collections aren't available on the site object.

But this did the trick for me.