Related to: Accessing Custom Attributes in Liquid
So I have tried to abstract the logic in the related post by creating a custom Generator and Page classes. The main content of the page using it doesn’t load and the pages don’t render correctly for categories or tags using the same layout template.
The custom Generator:
module Generators
class RandomSectionGenerator < Jekyll::Generator
safe true
priority :lowest
def generate(site)
#homepage
site.pages << SectionedPage.new(site,"/","Home","default.html",site.posts)
#iterate through the categories and their posts
category_path = (!site.config['categories'] || !site.config['categories']['basedir'] ? "/" : site.config['categories']['basedir'])
category_layout = (!site.config['categories'] || !site.config['categories']['layout'] ? "default.html" : site.config['categories']['layout'])
site.categories.each do |category, posts|
site.pages << SectionedPage.new(site,File.join(category_path,Jekyll::Utils.slugify(category)),category,category_layout,posts)
end
#iterate through the tags and their posts
tag_path = (!site.config['tags'] || !site.config['tags']['basedir'] ? "/" : site.config['tags']['basedir'])
tag_layout = (!site.config['tags'] || !site.config['tags']['layout'] ? "default.html" : site.config['tags']['layout'])
site.tags.each do |tag, posts|
site.pages << SectionedPage.new(site,File.join(tag_path,Jekyll::Utils.slugify(tag)),tag,tag_layout,posts)
end
end
end
end
The custom Page:
class SectionedPage < Jekyll::Page
attr_accessor :collection, :sections
ATTRIBUTES_FOR_LIQUID = %w(
collection
sections
content
dir
name
path
url
)
def initialize(site,path,collection,layout,posts)
@site = site
@base = site.source
@dir = path
@collection = collection
@basename = "index"
@ext = ".html"
@name = "index.html"
@data = {
"linked_docs" => posts
}
#initialize layout defaulting to 'default'
@layout = File.join("_layouts",layout)
@layout = File.exist?(File.join(@base,@layout)) ? @layout : File.join("_layouts","default.html")
#Execute super "constructor"
super(@site,@base,"",@layout)
#create sections data
config_sections
#final initialization
self.read_yaml(@base, @layout)
self.data.merge!('title' => @collection)
end
#set up posts data in random layout
def config_sections
#initialize
@boxes = !@site.config['sections'] || !@site.config['sections']['boxes'] ? {'single' => 1 } : site.config['sections']['boxes']
@placements = !@site.config['sections'] || !@site.config['sections']['placement'] ? ['left','center','right'] : site.config['sections']['placement']
@section = nil
@placement_idx = 0
@sections = []
#initialize posts for the page
@posts = []
#copy posts to another Hash for popping
@docs = (@data["linked_docs"].nil? || @data["linked_docs"].empty?) ? {} : @data["linked_docs"]
@docs.each { |p| @posts << p.dup }
#do till there are no posts left
while @posts.size > 0
Jekyll.logger.debug "#{@posts.size} posts are left for page #{@dir}"
#are all the placements in a section filled
if @placement_idx > 2
@placement_idx = 0
end
if @placement_idx == 0
#if there was already a section object, put into site configuration
if @section != nil
Jekyll.logger.debug "Completed Section: #{@section} for #{@dir}"
@sections.push(@section)
end
#create a new section object
@section = Section.new()
end
#add posts to the randomly selected placement
@tmp = @boxes.to_a.sample
@section.add_posts(@placements[@placement_idx], @tmp[0], @posts.pop(@tmp[1]))
#increment the placement counter
@placement_idx += 1
end
#add to page sections data in the site
@data["sections"] = @sections
end
#alias to get the page's sections
def sections
@data["sections"]
end
end
And the custom Section class
class Section
attr_reader :data, :collection
def initialize
@data = {}
end
def add_posts(place, template, posts)
@data[place] = {"template" => template, "posts" => posts}
end
def get_placement(place)
@data[place] ? @data[place] : {}
end
def to_liquid
@data
end
end
If we focus on a portion of the custom Generator generate code:
#iterate through the categories and their posts
category_path = (!site.config['categories'] || !site.config['categories']['basedir'] ? "/" : site.config['categories']['basedir'])
category_layout = (!site.config['categories'] || !site.config['categories']['layout'] ? "default.html" : site.config['categories']['layout'])
site.categories.each do |category, posts|
site.pages << SectionedPage.new(site,File.join(category_path,Jekyll::Utils.slugify(category)),category,category_layout,posts)
end
This code is creating a Page for every site categories and using a template _layouts/collection.html which is below
---
layout: default
permalink: /:collection/
---
{% for section in sections %}
<section class="section">
<div class="container-fuild">
<div class="masonry-blog clearfix">
{% for placement in section %}
{% assign include_file = placement[1].template | prepend: "posts/" | append: ".html" %}
{% assign include_posts = placement[1].posts %}
<div class="{{ placement[0] }}-side">
{% include {{ include_file }} posts=include_posts %}
</div>
{% endfor %}
</div>
</div>
</section>
{% endfor %}
In the Jekyll logging output, I see the following once for each category
Layout source: site
Rendering: _layouts/collection.html
Pre-Render Hooks: _layouts/collection.html
Rendering Liquid: _layouts/collection.html
Rendering Markup: _layouts/collection.html
Rendering Layout: _layouts/collection.html
…
Writing: /srv/jekyll/_site/:collection/index.html
When I run Jekyll, these pages don’t get generated exactly. A file in _site/:collection/index.html is generated. It is rendered in the default layout, but there is no main content (a.k.a. where the posts should render). So my two questions are:
- Why isn’t this generating a separate file for each category as an index.html (e.g. _site/process/index.html)?
- How do I make sure that the layout see the data my custom generator put into the Page.data[“sections”] when rendering and get that template to render to the expected unique URL (in question 1)?
Thanks in advance!!!