Skip to content

Instantly share code, notes, and snippets.

@fastdivision
Last active September 3, 2020 20:09
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save fastdivision/5890772 to your computer and use it in GitHub Desktop.
Save fastdivision/5890772 to your computer and use it in GitHub Desktop.
Jekyll Examples
<div class="byline author vcard">
<a class="photo" href="{% render_author url %}">
<img src="{% render_author avatar %}" alt="{{ page.author }}" title="{{ page.author }}">
</a>
<h2>{{ page.author }}</h2>
<div class="author-bio">
<p>{% render_author bio %}</p>
</div>
</div>
# If category is with questions...
if self.categories[index.data['category']].select { |post| post.categories[0] == 'questions' }.length != 0
# Specify a unique folder for /categories based on post type. In this case, /questions/categories
target_dir = '/' + GenerateCategories.category_dir(self.config['category_question_dir'], category)
# Use a unique layout file for this post type. In this case, question_category_index.html
index = CategoryIndex.new(self, self.source, target_dir, category, 'question_category_index.html')
# Add category page to queue for export
if index.render?
self.pages << index
end
end
def write_category_indexes
if self.layouts.key? 'blog_category_index'
self.categories.keys.each do |category|
next if RESTRICTED_CATEGORIES.include? category
self.write_category_index(category)
end
# Throw an exception if the layout couldn't be found.
else
throw "No 'category_index' layout found."
end
end
module Jekyll
class Post
alias_method :original_to_liquid, :to_liquid
def to_liquid
original_to_liquid.deep_merge({
'excerpt' => content.match('<!--more-->') ? content.split('<!--more-->').first : nil
})
end
end
module Filters
def mark_excerpt(content)
content.gsub('<!--more-->', '<p><span id="more"></span></p>')
end
end
end
module Jekyll
module PostAndCategoryFilter
RESTRICTED_CATEGORIES = ['blog', 'questions']
# Returns back all categories related to a primary category
# e.g. "blog" or "questions"
def filter_categories_by_primary_category(posts)
filtered = []
for post in posts
for post_category in post.categories
filtered.push(post_category) unless RESTRICTED_CATEGORIES.include? post_category
end
end
filtered.sort.uniq
end
end
end
Liquid::Template.register_filter(Jekyll::PostAndCategoryFilter)
module Jekyll
class Page
attr_accessor :dir
end
module Generators
class Pagination < Generator
# This generator is safe from arbitrary code execution.
safe true
# Generate paginated pages if necessary.
#
# site - The Site.
#
# Returns nothing.
def generate(site)
paginate(site, '/blog/index.html', 'blog')
paginate_categories(site, '/blog/categories', 'blog_category_index.html', site.categories['blog'])
paginate_categories(site, '/questions/categories', 'question_category_index.html', site.categories['questions'])
end
# Paginates the blog's posts. Renders the index.html file into paginated
# directories, e.g.: page2/index.html, page3/index.html, etc and adds more
# site-wide data.
#
# site - The Site.
# page - The index.html Page that requires pagination.
#
# {"paginator" => { "page" => <Number>,
# "per_page" => <Number>,
# "posts" => [<Post>],
# "total_posts" => <Number>,
# "total_pages" => <Number>,
# "previous_page" => <Number>,
# "next_page" => <Number> }}
def paginate(site, page_path, category)
all_posts = site.site_payload['site']['categories'][category]
page = site.pages.select do |page|
path = page.dir + "/" + page.name
path == page_path
end.first
pages = Pager.calculate_pages(all_posts, site.config['custom_paginate'].to_i)
(1..pages).each do |num_page|
pager = Pager.new(site, num_page, all_posts, pages, page)
if num_page > 1
newpage = Page.new(site, site.source, page.dir, page.name)
newpage.pager = pager
newpage.dir = File.join(page.dir, "page#{num_page}")
site.pages << newpage
else
page.pager = pager
end
end
end
def paginate_categories(site, category_path, category_layout, posts)
categories = []
restricted_categories = ['blog', 'questions']
for post in posts
for post_category in post.categories
categories.push(post_category) unless restricted_categories.include? post_category
end
end
categories.sort!.uniq!
for category in categories
all_posts = site.site_payload['site']['categories'][category]
page = site.pages.select do |page|
path = page.dir + "/" + page.name
path == category_path + "/" + category + "/index.html"
end.first
pages = Pager.calculate_pages(all_posts, site.config['custom_paginate'].to_i)
(1..pages).each do |num_page|
pager = Pager.new(site, num_page, all_posts, pages, page)
if num_page > 1
newpage = CategoryIndex.new(site, site.source, page.dir, category, category_layout)
newpage.pager = pager
newpage.dir = File.join(page.dir, "page#{num_page}")
site.pages << newpage
else
page.pager = pager
end
end
end
end
end
end
class Pager
attr_reader :page, :per_page, :posts, :total_posts, :total_pages,
:previous_page, :previous_page_path, :next_page, :next_page_path
# Calculate the number of pages.
#
# all_posts - The Array of all Posts.
# per_page - The Integer of entries per page.
#
# Returns the Integer number of pages.
def self.calculate_pages(all_posts, per_page)
(all_posts.size.to_f / per_page.to_i).ceil
end
# Determine if the subdirectories of the two paths are the same relative to source
#
# source - the site source
# page_dir - the directory of the Jekyll::Page
# paginate_path - the absolute paginate path (from root of FS)
#
# Returns whether the subdirectories are the same relative to source
def self.in_hierarchy(source, page_dir, paginate_path)
return false if paginate_path == File.dirname(paginate_path)
return false if paginate_path == Pathname.new(source).parent
page_dir == paginate_path ||
in_hierarchy(source, page_dir, File.dirname(paginate_path))
end
# Static: Return the pagination path of the page
#
# site - the Jekyll::Site object
# num_page - the pagination page number
# target_page - the page where pagination is occurring
#
# Returns the pagination path as a string
def self.paginate_path(site, num_page, target_page)
return nil if num_page.nil?
return target_page.url if num_page <= 1
format = site.config['paginate_path']
format = format.sub(':num', num_page.to_s)
ensure_leading_slash(format)
end
# Static: Return a String version of the input which has a leading slash.
# If the input already has a forward slash in position zero, it will be
# returned unchanged.
#
# path - a String path
#
# Returns the path with a leading slash
def self.ensure_leading_slash(path)
path[0..0] == "/" ? path : "/#{path}"
end
# Static: Return a String version of the input without a leading slash.
#
# path - a String path
#
# Returns the input without the leading slash
def self.remove_leading_slash(path)
ensure_leading_slash(path)[1..-1]
end
# Initialize a new Pager.
#
# config - The Hash configuration of the site.
# page - The Integer page number.
# all_posts - The Array of all the site's Posts.
# num_pages - The Integer number of pages or nil if you'd like the number
# of pages calculated.
def initialize(site, page, all_posts, num_pages = nil, target_page)
@page = page
@per_page = site.config['custom_paginate'].to_i
@total_pages = num_pages || Pager.calculate_pages(all_posts, @per_page)
if @page > @total_pages
raise RuntimeError, "page number can't be greater than total pages: #{@page} > #{@total_pages}"
end
init = (@page - 1) * @per_page
offset = (init + @per_page - 1) >= all_posts.size ? all_posts.size : (init + @per_page - 1)
@total_posts = all_posts.size
@posts = all_posts[init..offset]
@previous_page = @page != 1 ? @page - 1 : nil
@previous_page_path = Pager.paginate_path(site, @previous_page, target_page)
@next_page = @page != @total_pages ? @page + 1 : nil
@next_page_path = Pager.paginate_path(site, @next_page, target_page)
end
# Convert this Pager's data to a Hash suitable for use by Liquid.
#
# Returns the Hash representation of this Pager.
def to_liquid
{
'page' => page,
'per_page' => per_page,
'posts' => posts,
'total_posts' => total_posts,
'total_pages' => total_pages,
'previous_page' => previous_page,
'previous_page_path' => previous_page_path,
'next_page' => next_page,
'next_page_path' => next_page_path
}
end
end
end
def entry(path, date, attrs, site)
# Remove the trailing slash from the baseurl if it is present, for consistency.
baseurl = site.config['url']
baseurl = baseurl[0..-2] if baseurl=~/\/$/
entry = "\n\t<url>\n\t\t<loc>#{baseurl}#{path}</loc>\n"
entry += "\t\t<lastmod>#{date.strftime("%Y-%m-%d")}</lastmod>\n" unless date.nil?
entry += attrs.map { |k,v| "\t\t<#{k}>#{v}</#{k}>" }.join("\n") + "\n\t</url>\n"
end
module Jekyll
class AuthorTag < Liquid::Tag
def initialize(tag_name, attribute, tokens)
super
@attribute = attribute
end
def render(context)
authors = JSON.parse(IO.read('authors.json'))['authors']
author_name = context.environments.first['page']['author']
selected_author = authors.select { |author| author['name'] == author_name }
selected_author[0][@attribute.strip!]
end
end
end
Liquid::Template.register_tag('render_author', Jekyll::AuthorTag)
@heymarkreeves
Copy link

Hello! Any thoughts on the following errors for generate_pagination.rb?

generate_pagination.rb:132:in `paginate_path': undefined method `url' for nil:NilClass (NoMethodError)
generate_pagination.rb:87:in `block (2 levels) in paginate_categories': undefined method `pager=' for nil:NilClass (NoMethodError)

@danielschmid
Copy link

got the same error as @circa1977 undefined method pager=' for nil:NilClass

@josephrexme
Copy link

I'm using excerpts with the method. It was already provided by jekyll so I didn't have to use the plugin. Any ideas on how I may remove images from the excerpts?

@monecchi
Copy link

Hi there. I'm already using the plugin filter_posts_and_categories.rb and it works as intended. Thanks for sharing your plugin code.

Anyway, the generate_pagination.rb plugin seems to be very promising. However, I've tried to set it up but jekyll fails to serve and throws the following error:

generate_pagination.rb:87:in 'block (2 levels) in paginate_categories': undefined method 'pager=' for nil:NilClass (NoMethodError)

I've slightly modified the plugin so that it fits my site / blog set up.

So in config.yml I've place the following:

category_dir: "/blog/categories/"
category_portfolio_dir: "/projects/categories/" 

And here's the modified lines in the plugin's code

  def generate(site)
    paginate(site, '/blog/index.html', 'blog')
    paginate_categories(site, '/blog/categories', 'blog_category_index.html', site.categories['blog'])
    paginate_categories(site, '/portfolio/categories', 'portfolio_category_index.html', site.categories['projects'])
  end

Am I supposed to create blog_category_index.htmland portfolio_category_index.htmlor are these .html pages going to be generated by the plugin?

I'd really appreciate any thoughts regarding this issue. I would be great if Jekyll paginator could filter posts by type or category.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment