Published 2023-02-12.
    Last modified 2025-09-16.
            
Time to read: 16 minutes.
 
jekyll_plugins collection.
  After writing over two dozen Jekyll plugins, I distilled the common code into
  jekyll_plugin_support.
  This F/OSS Ruby gem facilitates writing and testing Jekyll plugins
  and handles the standard housekeeping that every Jekyll inline and block tag plugin requires.
  Logging, parsing arguments, obtaining references to
  site and page objects, etc. are all handled.
  The result is faster Jekyll plugin development with fewer bugs.
  Jekyll_plugin_support can be used to create plugins
  in the _plugins/ directory of a Jekyll project,
  and it can also be used as the foundation of gem-based Jekyll plugins.
At present, only Jekyll tags and blocks are supported.
  Public plugins that use jekyll_plugin_support include:
jekyll_badgejekyll_draftjekyll_emojijekyll_flexible_includejekyll_google_translatejekyll_hrefjekyll_imgjekyll_outlinejekyll_prejekyll_quote
  ... and also the demonstration plugins in
  jekyll_plugin_support
About jekyll_plugin_support
  This Jekyll plugin includes a generator, triggered by a high-priority hook,
  and a block tag called all_collections.
  JekyllSupport:: and JekyllSupport::
  provide support for
  Jekyll tag block plugins and
  Jekyll inline tag plugins, respectively.
  They are very similar in construction and usage.
  Instead of subclassing your custom Jekyll block tag class from Liquid::,
  subclass from JekyllSupport::.
  Similarly, instead of subclassing your custom Jekyll tag class from Liquid::,
  subclass from JekyllSupport::.
  Both JekyllSupport classes instantiate new instances of
  PluginMetaLogger
  (called @logger) and
  JekyllPluginHelper (called @helper).
  JekyllPluginHelper defines a generic initialize method,
  and your tag or block tag class should not need to override it.
  Also, your tag or block tag class should not define a method called render
  because jekyll_plugin_support defines one.
  Instead, define a method called render_impl.
  For inline tags, render_impl does not accept any parameters.
  For block tags, a single parameter is required,
  which contains text passed from your block in the page.
  Your implementation of render_impl can parse parameters passed to
  your tag, as described in Explanations and Examples of Jekyll Plugins.
  In addition, within render_impl,
  the arguments passed to the tag will have been tokenized and parsed,
  with Jekyll and Liquid variables substituted for their values,
  and all the public Jekyll variables will be available as instance variables.
  Error handling will also have been set up,
  and access to your tag's entry within _config.yml will have been set up.
Features
  Jekyll plugin tags created from jekyll_plugin_support framework automatically
  inherit the following features:
Boilerplate code is eliminated, so plugin developers can focus on the required logic and output generation.
Plugin parameters are automatically parsed as keywords and name/value pairs.
  Plugin users can use single or double quotes to delimit argument values when
  calling plugins built with jekyll_plugin_support.
  Quotes can be omitted for single-token arguments.
  Configuration information for specific jekyll_plugin_support plugins is
  automatically recognized, parsed, and made available to the appropriate plugins.
Important variables are predefined so Jekyll programmers do not have to dig deep into documentation or Jekyll source code to figure out how to obtain useful values and settings.
Error handling is standardized, and the standard error handling can be extended or over-ridden. Messages can be logged to web pages and/or the console.
  A dedicated CSS class is automatically created to style error messages from each
  jekyll_plugin_support plugin.
  Jekyll and Liquid variables, such as layout,
  page and include variables,
  are evaluated when passed as tag parameters, and embedded in the body of block tags.
  The parsing and evaluation is performed automatically,
  without the plugin developer having to do anything special.
Plugin registration is integrated, and important configuration details are reported during registration.
A custom logger is created for each tag, independent of the default Jekyll logger.
  Variables can be defined in _config.yml,
  and optionally have different values for debug mode, production mode and test mode.
An attribution message is available.
Draft pages are automatically detected and are displayed slightly differently so they can easily be recognized. Differences include a watermark and specially formatted links.
Links to internal pages of the Jekyll website can automatically be checked and error messages can be logged to indicate broken links. When pages are moved the links can automatically be updated.
Plugins can be subclassed.
  Nugem
  creates new Ruby projects containing scaffolding for plugins built using
  jekyll_.
  The following features are included in each new project:
- 
    A demonstration website is created for easy testing of 
jekyll_plugin_supportplugins. - 
    Visual Studio Code debugging is set up for the plugin code, including RSpec, and the demo website,
    with run configurations for 
productionanddevelopmentmodes. 
  Four new attributes are added to
  site:
all_collectionsincludes all documents in all collections.- 
    
all_documentsincludesall_collectionsplus all standalone pages. - 
    
everythingincludesall_documentsplus all static files. - 
    
sorted_lru_filesis used by a new binary search lookup for matching page suffixes. Thejekyll_hrefandjekyll_draftplugins use this feature. 
  A new Ruby class, JekyllSupport::APage,
  which abstracts all URL-addressable entities in a Jekyll website.
Requirements
  All the pages in the Jekyll website must have an implicit date
  (for example, all posts are automatically assigned this property by Jekyll),
  or an explicit date set in front matter, like this:
--- date: 2022-01-01 ---
  If a front matter variable called last_modified or
  last_modified_at exists,
  its value will be converted to a Ruby Date:
--- last_modified: 2023-01-01 ---
Or:
--- last_modified_at: 2023-01-01 ---
  Otherwise, if last_modified or last_modified_at
  is not present in the front matter for a page,
  the date value will be used last modified date value.
Installation
If You Need Windows Environment Variable Expansion
This only works if Jekyll is running on a Windows / WSL machine. If you have a Mac, ignore the rest of this section, however be sure not to try to include a file whose name contains two percent characters.
  If a %WindowsStyleEnvironmentVariable% is detected in the url parameter,
  wslvar is called.
  If your WSL installation is old it might not have the wslvar command
  The wslvar utility is part of the wslu package, a collection of useful utilities that come with WSL.
  The most straightforward fix is to reinstall it.
$ sudo apt update
$ sudo apt install wslu
For A Jekyll Website
  Jekyll_plugin_support is packaged as a Ruby gem.
  If you want to write a custom Jekyll plugin that will reside in a
  Jekyll project’s _plugins directory,
  add the following line to your Jekyll plugin’s Gemfile.
group :jekyll_plugins do ... gem 'jekyll_plugin_support', '>= 3.1.0' ... end
  Run the standard jekyll_plugin_support setup procedure:
$ bin/setup
As a Gem Dependency
  If your custom plugin will be packaged into a gem,
  add the following to your plugin’s .gemspec:
Gem::Specification.new do |spec| ... spec.add_dependency 'jekyll_plugin_support', '>= 3.1.0' ... end
  Install the jekyll_plugin_support gem in the usual manner:
$ bundle
  Copy the CSS classes from
  demo/assets/css/jekyll_plugin_support.css
  to your Jekyll project’s CSS file.
General Usage
  Please see the demo/ project
  for a well-documented set of demonstration Jekyll plugins built from jekyll_plugin_support.
  Additional information is available Explanations and Examples of Jekyll Plugins and the
  jekyll_plugin_support GitHub project.
  JekyllSupport::JekyllBlock
  and JekyllSupport::JekyllTag
  provide support for Jekyll block tags and Jekyll inline tags, respectively. They are similar in construction and usage.
  Instead of subclassing your Jekyll block tag class from Liquid::Block, subclass from JekyllSupport::JekyllBlock instead.
  Likewise, instead of subclassing your Jekyll inline tag class from Liquid::Tag, subclass from JekyllSupport::JekyllTag instead.
  Both JekyllSupport classes instantiate new instances of PluginMetaLogger
  (called @logger)
  and JekyllPluginHelper (called @helper).
Inline and Block Tag Plugin Implementation
  Both JekyllSupport classes define a generic initialize method,
  and your inline tag or block tag class should not override it.
  Also, your inline tag or block tag class should not define a method called render,
  because both JekyllSupport classes define this method.
  Instead, define a method called render_impl.
  For inline tags, render_impl does not accept any parameters.
  For block tags, a single parameter is required, which receives any text enclosed within your block
  by the website author.
New Site Attributes
  No explicit initialization or setup is required.
  Jekyll plugins can access the value of site.all_collections,
  site.all_documents and site.everything;
  however, Liquid code in Jekyll pages and documents cannot.
Excluding Files
There are two ways to exclude files from the new site attributes.
- 
    The 
excludeentry in_config.ymlcan be used as usual. - Adding the following entry to a page’s front matter causes that page to be excluded from the collection created by this plugin:
 
--- exclude_from_all: true ---
Predefined Plugin Variables
  Jekyll_plugin_support defines the following Ruby variables that you can use in your plugin’s render_impl method:
- 
    
@argument_stringUnparsed markup passed as a parameter to your block tag and inline tag. - 
    
@argvreturns any remaining tokens afterparameter_specified?has been invoked. - 
    
@attributionAttribution markup. - 
    
@configThe YAML Jekyll site configuration file. - 
    
@helperJekyllPluginHelperinstance for your plugin. - 
    
@layoutLayout information - 
    
@loggerjekyll_plugin_loggerinstance for your Jekyll plugin. - 
    
@modeIndicatesproduction,testordevelopmentmode. - 
    
@pageJekyll::Pagevariables. - 
    
@paginatorPagination variables. - 
    
@scopesSee thejekyll_plugin_supportdemo project. - 
    
@siteSite variables. - 
    
@tag_configContents of the section of_config.ymlnamed after your plugin. - 
    
@tag_nameName of your Jekyll block tag or inline tag plugin. - 
    
@themeTheme variables. - 
    
textContent provided to your block tag. 
Argument Parsing
  Tag arguments can be obtained within render_impl.
  Both keyword options and name/value parameters are supported.
  Both JekyllTag and JekyllBlock use the standard Ruby mechanism for parsing command-line options:
  Shellwords and
  key_value_parser (now part of Ruby).
  All your code has to do is specify the keywords to search for in the string
  passed from the HTML page that your tag is embedded in.
  The included demo website has examples;
  both demo/_plugins/demo_inline_tag.rb and
  demo/_plugins/demo_block_tag.rb
  contain the following:
@keyword1 = @helper.parameter_specified? 'keyword1' @keyword2 = @helper.parameter_specified? 'keyword2' @name1 = @helper.parameter_specified? 'name1' @name2 = @helper.parameter_specified? 'name2'
If an argument has a variable reference in it, the value of the variable is substituted for the reference. For example, given:
_layouts/default.htmldefines a variable calledvar_layoutin its front matter.index.htmldefines a variable calledvar_pagein its front matter.index.htmlassigns a variable calledxvia the liquidassignstatement.
... then the following references in a page will be substituted for their values in arguments and in block tag bodies:
{% my_block_tag
  param1="x={{x}}"
  param2="var_page={{page.var_page}}"
  param3="var_layout={{layout.var_layout}}"
%}
Assigned variables do not need a namespace: x={{x}}
Page variables must be qualified with the 'page' namespace:
  var_page={{page.var_page}}
Layout variables must be qualified with the 'layout' namespace:
  var_layout={{layout.var_layout}}
{% endmy_block_tag %}
  You can see similar code in demo/demo_inline_tag.html.
  The page['excerpt'] and page['output'] key/value pairs are removed from processing because of recursion issues.
  You cannot look up those values from a jekyll_plugin_support plugin.
Keyword Options
  For all keyword options, values specified in the document may be provided.
  If a value is not provided, the value true is assumed.
  Otherwise, if a value is provided, it must be wrapped in single or double quotes.
Examples
  The following examples use the die_if_error keyword option for the
  pre and
  exec tags from
  the jekyll_pre plugin.
  Both of these plugins subclass jekyll_plugin_support classes.
Specifying Tag Option Values
  The following sets die_if_error true:
{% pre die_if_error %} ... {% endpre %}
The above is the same as writing:
{% pre die_if_error='true' %} ... {% endpre %}
Or writing:
{% pre die_if_error="true" %} ... {% endpre %}
  Neglecting to provide surrounding quotes around the provided value causes the parser to not recognize the option.
  Instead, what you had intended to be the keyword/value pair will be parsed as part of the command.
  For the pre tag,
  this means the erroneous string becomes part of the label value, unless label is explicitly specified.
  For the exec tag, this means the erroneous string becomes part of the command to execute.
  The following demonstrates the error.
{% pre die_if_error=false %} ... {% endpre %}
  The above causes the label to be die_if_error=false.
{% exec die_if_error=false ls %} ... {% endpre %}
  The above causes the command to be executed to be die_if_error=false ls
  instead of ls.
Quoting
Parameter values can be quoted.
If the value consists of only one token, then quoting is optional. The following name/value parameters all have the same result:
pay_tuesday="true"pay_tuesday='true'pay_tuesday=truepay_tuesday
If the values consist of more than one token, quotes must be used. The following examples both yield the same result:
pay_tuesday="maybe not"pay_tuesday='maybe not'
Remaining Markup
  After your plugin has parsed all the keyword options and name/value parameters,
  call @helper.remaining_markup to obtain the remaining markup that was passed to your plugin.
Configuration Variables
 jekyll_plugin_support provides support for
  Liquid variables
  to be defined in _config.yml, in a section called liquid_vars.
  These variables behave exactly like Liquid variables defined by assign and capture expressions,
  except they are global in scope; these variables are available in every Jekyll web page.
  The following _config.yml fragment defines 3 configuration variables called var1,
  var2 and var3:
liquid_vars: var1: value1 var2: 'value 2' var3: value3
Liquid variables defined in this manner are intended to be embedded in web pages. They are can be used like any other Liquid variable.
Variable Expansion
  Jekyll expands Liquid variable references during the page rendering process.
  Jekyll does not expand Liquid variable references passes as parameters to tag and block plugins, however.
  However, plugins made from jekyll_plugin_support automatically
  expand variable references passed as parameters and in block tag bodies.
    Jekyll_plugin_support tag and block plugins expand the following types of variables:
 
  In the following example web page, Jekyll expands the var1 reference within the <p> tag,
  but not the var1 or var2 references passed to my_plugin.
---
---
<p>This is the value of var1: {{var1}}.</p>
{% my_plugin param1="{{var1}}" param2="{{var2}}" %}
  Assuming that my_plugin was written as a jekyll_plugin_support plugin,
  all variable references in its parameters are expanded.
  Thus, the above is interpreted as follows when my_plugin is evaluated during the Jekyll rendering process:
<p>This is the value of var1: value1.</p>
{% my_plugin param1="value1" param2="value 2" %}
  Jekyll_plugin_support expands all but one of the
  plugin variables described above,
  replacing Liquid variable references with their values.
  The exception is @argument_string, which is not expanded.
Liquid Variable Values Specific To Production, Development and Test Modes
 jekyll_plugin_support allows Liquid variables defined in _config.yml to have different values
  when Jekyll is running in development, production and test modes.
  When injecting variables into your Jekyll website, Jekyll_plugin_support
  refers to definitions specific to the current environment
  and then refers to other definitions that are not overridden.
Here is an example:
liquid_vars:
  development:
    var1: 'http://localhost:4444/demo_block_tag.html'
    var2: 'http://localhost:4444/demo_inline_tag.html'
  production:
    var1: 'https://github.com/django/django/blob/3.1.7'
    var2: 'https://github.com/django-oscar/django-oscar/blob/3.0.2'
  var3: 'https://github.com/mslinn'
  For the above, the following variable values are set in development mode:
var1http://localhost:4444/demo_block_tag.htmlvar2http://localhost:4444/demo_inline_tag.htmlvar3https://github.com/mslinn
  ... and the following variable values are set in production and test modes:
var1https://github.com/django/django/blob/3.1.7var2https://github.com/django-oscar/django-oscar/blob/3.0.2var3https://github.com/mslinn
Evaluating Include Variables
This information is only useful if a plugin might be executed from within an included file.
  While Liquid handles regular variables, Jekyll has special handling for variables defined by include parameters.
  For example, the following defines a variable in the include scope called var1
  when processing the body of an included file:
{% include myfile.html var1='value1' %}
  You can obtain the value of this variable from the render_impl method of a
  JekyllSupport::JekyllTag or JekyllSupport::JekyllBlock subclass as follows:
@var1 = @scopes.first['include']&.[]('var1')
Liquid Variables in jekyll_plugin_support Subclasses
  You can define additional Liquid variables in plugins built using jekyll_plugin_support.
  To achieve this, make entries in _config.yml under a key named after the value of @tag_name.
  For example, let’s imagine you create a plugin using jekyll_plugin_support,
  and you register it with the name phonetic_alphabet.
  You could define Liquid variables that would be made available to content pages in web applications that
  incorporate the phonetic_alphabet plugin.
  The following section in _config.yml defines variables called x, y and z,
  with values xray, yankee and zulu, respectively:
phonetic_alphabet: x: xray y: yankee z: zulu
The above definitions allow you to write content pages that use those variables, like the following:
---
layout: default
title: Variable demo
---
The letter x is pronounced {{x}}.
Similarly, the letters y and z are pronounced {{y}} and {{z}}.
... which expands to:
The letter x is pronounced xray. Similarly, the letters y and z are pronounced yankee and zulu.
Automatically Created Error Classes
  JekyllSupport::JekyllBlock and JekyllSupport::JekyllTag subclasses
  automatically create error classes, named after the subclass.
  For example, if you create a JekyllSupport::JekyllBlock subclass called DemoBlockTag,
  the automatically generated error class will be called DemoBlockTagError.
  Although you could use it as you would any other error class,
  JekyllPluginSupport provides additional helper methods.
  These methods fill in the page path and line number that caused the error, shorten the stack trace,
  log an error message, and can be used to return an HTML-friendly version of the message to the web page.
  The following example is a shortened version of
  demo/_plugins/demo_block_tag.rb.
  You might want to write similar code in your rescue blocks.
class DemoBlock < JekyllSupport::JekyllBlock
  VERSION = '0.1.2'.freeze
  def render_impl(text)
    raise DemoBlockTagError, 'Fall down, go boom.'
  rescue DemoBlockTagError => e
    @logger.error { e.logger_message }
    exit! 1 if @die_on_demo_block_error
    e.html_message
  end
end
Error class methods have been provided for standardized and convenient error handling:
- 
    
logger_message- The error message is constructed from the string provided when the error was raised, with the page path and line number added. - 
    
html_message- The same aslogger_message, but constructed with HTML. - 
    
shorten_backtrace- most of the lines that spew from a Jekyll backtrace are uninteresting and unhelpful. This method is called bylogger_message. 
No_arg_parsing Optimization
  If your tag or block plugin only needs access to the raw arguments passed from the web page
  without tokenization, and you expect that the plugin might be invoked with large amounts of text,
  derive your plugin from JekyllBlockNoArgParsing or JekyllTagNoArgParsing.
  See the demo plugins for an example.
  This feature is used by the
  select tag
  in the jekyll_pre plugin.
Self-Reporting Upon Registration
When each tag is registered, it self-reports, for example:
INFO PluginMetaLogger: Loaded plugin demo_inline_tag v0.1.2. It has:
  Error class: DemoTagError
  CSS class for error messages: demo_tag_error
  _config.yml contains the following configuration for this plugin:
    {"die_on_demo_tag_error"=>false, "die_on_standard_error"=>false}
INFO PluginMetaLogger: Loaded plugin demo_inline_tag_no_arg v0.1.0. It has:
  Error class: DemoTagNoArgsError
  CSS class for error messages: demo_tag_no_args_error
  _config.yml does not contain configuration information for this plugin.
  You could add a section containing default values by specifying a section for the tag name,
  and an entry whose name starts with `die_on_`, followed by a snake_case version of the error name.
    demo_inline_tag_no_arg:
      die_on_demo_tag_no_args_error: false
  JekyllPluginHelper::register accepts a quiet option
  to suppress the signon message.
  Use it like this:
JekyllPluginHelper.register(self, 'tag_name', quiet: true)
Interfaces
Collection Management
  Jekyll provides inconsistent attributes for
  site.,
  site. and
  site..
- 
    While the 
urlattribute of items insite.postsandsite.pagesstart with a slash (/),site.static_filesitems do not have aurlattribute. - 
    Static files have a 
relative_pathattribute, which starts with a slash (/), but although that attribute is also provided insite.postsandsite.pages, those values do not start with a slash. - 
    Paths ending with a slash (
/) imply that a file calledindex.htmlshould be fetched. - 
    HTML redirect files created by the
    
jekyll-redirect-fromJekyll plugin, which are included in site.static_files, should be ignored. 
These inconsistencies mean that combining the standard three collections of files provided as site attributes will create a new collection that is difficult to process consistently:
# This pseudocode creates `oops`, which is problematic to process consistently oops = site.all_collections + site.pages + site.static_files
  Oops, above, is difficult to process because of inconsistencies in the provided attributes
  and how the attributes are constructed.
Normalizing Inconsistencies
  Plugins based on jekyll_plugin_support normalizes the aforementioned
  inconsistencies by utilizing the
  JekyllSupport:: class
  and filtering out HTML redirect files.
  JekyllSupport:: is used by plugins
  based on jekyll_, in particular
  jekyll_href and
  jekyll_outline.
  The all_collections collection contains APage
  representations of site..
  The all_documents collection contains APage representations of site..
  The everything collection contains APage representations of:
site.collections + site.pages + site.static_files - HTML_redirect_files
JekyllSupport::APage Class
  JekyllSupport::APage instances can be used to
  automate content generation and reporting tasks.
  
  
  To save repetitive typing,
  I will just write APage instead of JekyllSupport::APage.
  The APage Ruby class
  abstracts all URL-addressable entities in a Jekyll website.
  An APage can be constructed from Jekyll:Documents and Jekyll::Pages,
  Jekyll static files, and Ruby objects.
  In addition to the
  standard Ruby constructor,
  two more constructors are provided:
  JekyllSupport. and
  JekyllSupport.
  The site., site and site
  attributes consist of arrays of APage instances.
  The APage class has the following attributes:
categoriescollection_namecontent(HTML or Markdown)data(array)date(RubyTime)descriptiondestinationdraft(Boolean)excerptext/extname- 
    
hrefalways starts with a slash. This value is consistent witha hrefvalues in website HTML. Paths ending with a slash (/) haveindex.htmlappended so the path specifies an actual file. labellast_modified(RubyDate)last_modified_fieldindicates the name of the field that originally contained the value forlast_modified(last_modifiedorlast_modified_at)layoutloggerhelpful for error handlingname- 
    
originindicates the original source of the item. Possible values arecollection,individual_pageandstatic_file. Knowing theoriginof each item allows code to process each type of item appropriately. pathrelative_pathtagstitletypeurl
All_collections Block Tag
  The all_collections block tag creates a formatted listing of Jekyll files.
  The ordering is configurable; by default, the listing is sorted by date, newest to oldest.
  The all_collections tag has a data_source parameter that specifies which new property to report on
  (all_collections, all_documents, or everything).
  For an example of how to use this Jekyll tag in conjunction with the
  jekyll_outline tag,
  see the outline_tabs
  section of the jekyll_outline documentation.
all_collections Plugin Usage
  Jekyll generators and tags receive an enhanced version of the Jekyll site variable.
From a Custom Plugin
  In the following example of how to use the all_collections plugin in a custom plugin,
  the do_something_with function processes all Jekyll::,
  Jekyll:, and static files.
@site.everything.each do |apage| do_something_with apage end
Using the Block Tag
The general form of the Jekyll tag, including all options, is:
{% all_collections
  collection_name='posts'
  data_selector='all_collections'
  date_column='date|last_modified'
  heading='All Posts'
  id='post_outline'
  sort_by='SORT_KEYS'
%}
Each of these attributes are explained below.
Collection_name Attribute
  Name of the collection that this APage belongs to, or nil.
Data_selector Attribute
  Name of the data collection to work from, defaults to all_collections.
  Other allowable values are all_documents and everything.
Date_column Attribute
  One of two date columns can be displayed in the generated HTML:
  either date (when the article was originally written),
  or last_modified.
  The default value for the date_column attribute is date.
Heading Attribute
  If no heading attribute is specified,
  a heading will automatically be generated, which contains the
  sort_by values, for example:
{% all_collections id='abcdef' sort_by="last_modified" %}
Generates a heading like:
<h2 id="abcdef">All Posts Sorted By last_modified</h2>
  To suppress both a h2 heading (and the enclosed id) from being generated,
  specify an empty string for the value of heading:
{% all_collections heading='' %}
Id Attribute
  If your Jekyll layout employs
  jekyll-toc,
  then id attributes are important.
  The jekyll-toc include checks for id attributes in
  h2 ... h6 HTML tags,
  and if found, and if the attribute value is enclosed in double quotes
  (id="my_id", not id='my_id'),
  then the heading is included in the table of contents.
  To suppress an id from being generated,
  and thereby preventing the heading from appearing in the
  automatically generated table of contents from jekyll-toc,
  specify an empty string for the value of id, like this:
{% all_collections id='' %}
SORT_KEYS Values
  SORT_KEYS specifies how to sort the collection.
  Values can include one or more of the following attributes:
  date, destination, draft,
  label, last_modified, last_modified_at, path,
  relative_path, title, type, and url.
  Ascending sorts are the default; however, a descending sort can be achieved by prepending
  - before an attribute.
  To specify more than one sort key, provide a comma-delimited string of values.
  Included spaces are ignored.
  For example, specify the primary sort key as draft,
  the secondary sort key as last_modified,
  and the tertiary key as label:
{% all_collections
  date_column='last_modified'
  heading='All Posts'
  id='asdf'
  sort_by='draft, last_modified, label'
%}
Liquid Usage Examples
Here is a short Jekyll page, including front matter, demonstrating this plugin being invoked with all default attribute values:
---
all_collection_exclusion: true
description: "
  Dump of all collections, sorted by date originally written, newest to oldest.
  The heading text will be <code>All Posts Sorted By -date</code>
"
layout: default
order: 0
title: Testing, 1, 2, 3
---
{% all_collections %}
Following are examples of how to specify the sort parameters.
  Explicitly express the default sort
  (sort by the date originally written, newest to oldest):
{% all_collections sort_by="-date" %}
Sort by date, from oldest to newest:
{% all_collections sort_by="date" %}
Sort by the date last modified, oldest to newest:
{% all_collections sort_by="last_modified" %}
Sort by the date last modified, newest to oldest:
{% all_collections sort_by="-last_modified" %}
Several attributes can be specified as sort criteria
  by passing them as a comma-delimited string.
  Included spaces are ignored:
{% all_collections sort_by="-last_modified, -date" %}
{% all_collections sort_by="-last_modified, title" %}
{% all_collections sort_by="-last_modified, -date, title" %}
The following two examples produce the same output:
{% all_collections sort_by="-last_modified,-date" %}
{% all_collections sort_by="-last_modified, -date" %}
Writing Plugins
  The following minimal examples define VERSION,
  which is important because JekyllPluginHelper.register
  logs that value when registering the plugin.
  This is how you would define plugins in the _plugins directory:
require 'jekyll_plugin_support'
module Jekyll
  class DemoTag < JekyllSupport::JekyllTag
    VERSION = '0.1.0'.freeze
    def render_impl
      @helper.gem_file __FILE__ # Enables attribution; only works when plugin is a gem
      # Your Jekyll plugin logic goes here
    end
    JekyllPluginHelper.register(self, 'demo_tag')
  end
end
require 'jekyll_plugin_support'
module Jekyll
  class DemoBlock < JekyllSupport::JekyllBlock
    VERSION = '0.1.0'.freeze
    def render_impl(text)
      @helper.gem_file __FILE__ # Enables attribution; only works when plugin is a gem
      # Your Jekyll plugin logic goes here
    end
    JekyllPluginHelper.register(self, 'demo_block')
  end
end
  If your plugin is packaged as a gem, then you might need to include version.rb
  into the plugin class.
  For example, if your version module looks like this:
module MyPluginVersion VERSION = '0.5.0'.freeze end
  Then your plugin can incorporate the VERSION constant into your plugin like this:
require 'jekyll_plugin_support' require_relative 'my_plugin/version'
module Jekyll class MyBlock < JekyllSupport::JekyllBlock include MyPluginVersion
def render_impl(text) @helper.gem_file __FILE__ # Enables attribution; only works when plugin is a gem # Your code here end
JekyllPluginHelper.register(self, 'demo_block') end end
Subclassing Plugins
  Jekyll plugins created using jekyll_plugin_support
  are implemented as Ruby classes.
  If you would like to create a version of an existing Jekyll plugin,
  you will need to subclass the plugin.
  In order to do that, you will need to override the plugin name and version,
  which are defined as constants.
  Jekyll_plugin_support provides a method that allows
  a constant to be redefined, called redef_without_warning.
  Use it in a subclass like this:
redef_without_warning :PLUGIN_NAME, 'my_plugin'.freeze redef_without_warning :VERSION, '0.1.0'.freeze
Attribution
  JekyllTag and JekyllBlock subclasses of jekyll_plugin_support
  can utilize the attribution option if they are published as gems.
  JekyllTagNoArgParsing and JekyllBlockNoArgParsing subclasses cannot.
  This feature is usually only desired for JekyllBlock subclasses.
- When used as a keyword option, a default value is used for the attribution string.
 - When used as a name/value option, the attribution string can be specified.
 
Using the attribution option causes subclasses to replace their usual output with HTML that looks like:
<div id="jps_attribute_12345" class="jps_attribute">
  <a href="https://github.com/mslinn/jekyll_outline">
    <b>Generated by <code>jekyll_outline</code>.
  </a>
</div>
The id attribute in the sample HTML above is randomized, so more than one attribution can appear on a page.
Attribution Generation
  You can decide where you want the attribution string for your Jekyll tag to appear by invoking
  @helper.attribute.
  For example, this is how the
  jekyll_outline tag generates output:
<<~HEREDOC
  <div class="outer_posts">
  #{make_entries(collection)&.join("\n")}
  </div>
  #{@helper.attribute if @helper.attribution}
HEREDOC
Usage
Typical usage for the attribution tag is:
{% my_block_tag attribution %}
  Content of my_block_tag.
{% endmy_block_tag %}
  The normal processing of my_tag is augmented by interpolating the attribution format string,
  which is a Ruby-compatible interpolated string.
The default attribution format string is:
"Generated by the #{name} #{version} Jekyll plugin, written by #{author} #{date}."
  Because jekyll_plugin_suppprt subclasses are gems,
  their gemfiles define values for name, version, homepage,
  and authors, as well as many other properties.
  The date property is obtained from the plugin/gem publishing date.
An alternative attribution string can be specified using any of the above properties:
{% my_tag attribution="Generated by the #{name} #{version} Jekyll plugin, written by #{author} #{date}" %}
Development
  After checking out the jekyll_plugin_suppprt repository, run bin/setup to install dependencies.
  bin/console provides an interactive prompt that allows you to experiment.
To build and install this gem on your local machine, run:
$ bundle exec rake install jekyll_plugin_support 0.1.0 built to pkg/jekyll_plugin_support-0.1.0.gem. jekyll_plugin_support (0.1.0) installed.
Examine the newly built gem:
$ gem info jekyll_plugin_support
*** LOCAL GEMS ***
jekyll_plugin_support (0.1.0) Author: Mike Slinn Homepage: https://github.com/mslinn/jekyll_plugin_support License: MIT Installed at: /home/mslinn/.gems
Provides a framework for writing and testing Jekyll plugins.
Build and Install Locally
To build and install this gem onto your local machine, run:
$ bundle exec rake install jekyll_plugin_support 3.0.0 built to pkg/jekyll_plugin_support-3.0.0.gem. jekyll_plugin_support (3.0.0) installed.
Examine the newly built gem:
$ gem info jekyll_plugin_support *** LOCAL GEMS ***
jekyll_plugin_support (3.0.0, 1.1.0, 1.0.3, 1.0.0) Author: Mike Slinn Homepage: https://www.mslinn.com/jekyll_plugins/jekyll_plugin_support.html License: MIT Installed at (3.0.0): /home/mslinn/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0 (1.1.0): /home/mslinn/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0 (1.0.3): /home/mslinn/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0 (1.0.0): /home/mslinn/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0
Provides a framework for writing and testing Jekyll plugins
Build and Push to RubyGems
To release a new version:
- Update the version number in 
version.rb. - Add an entry in 
CHANGELOG.mddescribing the changes since the last release. - Commit all changes to git; if you don't the next step might fail with a confusing error message.
 - 
    Run the following:
   Shell
$ bundle exec rake release
The above creates a git tag for the version, commits the created tag, and pushes the new.gemfile to RubyGems.org. 
Specs Unit Tests
This project provides Visual Studio Code launch configurations for running unit tests. A menu item is provided for running the tests in the current file, and another menu item runs all tests in the project.
A bash script is provided that can run all your Specs tests:
$ bin/rspec
  The script can also run one of your specific test files.
  For example, to run the tests in spec/template_spec.rb, type:
$ bin/rspec spec/template_spec.rb
This is the script:
#!/bin/bash
# See https://mslinn.com/jekyll_plugins/jekyll_plugin_support.html#testing
DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
cd "$DIR/../" || exit
if [ "$1" ]; then # Just test a specific file
  bundle exec rspec \
    -I ../lib \
    -I ../spec \
    "$1"
else # Run all tests
  bundle exec rspec \
    -I ../lib \
    -I ../spec \
    -P ../spec/**/*_spec.rb
fi
Debugging
  You can control the verbosity of log output by adding the following to _config.yml in your Jekyll project:
plugin_loggers: AllCollectionsTag::AllCollectionsTag: warn
- Set breakpoints in the Ruby code that interests you.
 - 
    You have several options for initiating a debug session:
   
- Use the Debug Demo Visual Studio Code launch configuration.
 - 
        Type the 
demo/_bin/debugcommand, without the-roptions shown above.... lots of output as bundle update runs... Bundle updated!
INFO PluginMetaLogger: Loaded AllCollectionsHooks v0.2.0 :site, :pre_render, :normal hook plugin. INFO PluginMetaLogger: Loaded DraftFilter plugin. INFO PluginMetaLogger: Loaded all_collections v0.2.0 tag plugin. Configuration file: /mnt/_/work/jekyll/my_plugins/jekyll_all_collections/demo/_config.yml Cleaner: Removing /mnt/_/work/jekyll/my_plugins/jekyll_all_collections/demo/_site... Cleaner: Removing /mnt/_/work/jekyll/my_plugins/jekyll_all_collections/demo/.jekyll-metadata... Cleaner: Removing /mnt/_/work/jekyll/my_plugins/jekyll_all_collections/demo/.jekyll-cache... Cleaner: Nothing to do for .sass-cache. Fast Debugger (ruby-debug-ide 0.7.3, debase 0.2.5.beta2, file filtering is supported) listens on 0.0.0.0:1234 - 
        Run 
bin/attachand pass the directory name of a Jekyll website that has a suitable script called_bin/debug. Thedemo/subdirectory fits this description.Shell$ bin/attach demo Successfully uninstalled jekyll_all_collections-0.1.2 jekyll_all_collections 0.1.2 built to pkg/jekyll_all_collections-0.1.2.gem. jekyll_all_collections (0.1.2) installed. Fast Debugger (ruby-debug-ide 0.7.3, debase 0.2.4.1, file filtering is supported) listens on 0.0.0.0:1234 <
 - Attach to the debugger process if required. The git repo includes two Visual Studio Code launch configurations for this purpose labeled Attach rdbg and Attach with ruby_lsp.
 
 - 
    Point your web browser to 
http://localhost:4444 
  If a debugging session terminates abruptly and leaves ports tied up, run the demo/_bin/release_port script.
Pry Breakpoint On StandardError
  A pry breakpoint will be set in the StandardError handler if
  pry_on_standard_error: true
  is set in the Liquid variable definition section for your plugin within _config.yml.
  For example, if your plugin is called blah, enable the breakpoint with the following section:
blah: pry_on_standard_error: true
Demonstration Plugins and Website
  The jekyll_plugin_support GitHub project includes a
  demo website.
  It can be used to debug the plugin or to run it freely.
Examining the Demo Plugins
  The following example plugins use Ruby’s
  squiggly heredoc operator (<<~).
  The squiggly heredoc operator removes the outermost indentation.
  This provides easy-to-read multiline text literals.
require 'jekyll_plugin_support'
# Use the JekyllSupport module namespace so the self methods are automajically found
module JekyllSupport
  DemoInlineTagError = JekyllSupport.define_error
  class DemoTag < JekyllTag
    VERSION = '0.1.2'.freeze
    # JekyllSupport.redef_without_warning 'VERSION', '0.1.2'.freeze
    def render_impl
      @demo_tag_error = @helper.parameter_specified? 'raise_demo_tag_error'
      @keyword1       = @helper.parameter_specified? 'keyword1'
      @keyword2       = @helper.parameter_specified? 'keyword2'
      @name1          = @helper.parameter_specified? 'name1'
      @name2          = @helper.parameter_specified? 'name2'
      @standard_error = @helper.parameter_specified? 'raise_standard_error'
      if @tag_config
        @die_on_demo_tag_error = @tag_config['die_on_demo_tag_error'] == true
        @die_on_standard_error = @tag_config['die_on_standard_error'] == true
      end
      raise DemoInlineTagError, 'This DemoInlineTagError error is expected.' if @demo_tag_error
      raise StandardError, 'This StandardError error is expected.' if @standard_error
      # _infinity = 1 / 0 if @standard_error # Not required
      output
    rescue NoMethodError, DemoInlineTagError => e # jekyll_plugin_support handles StandardError
      @logger.error { e.logger_message }
      exit! 1 if (e.message != 'This DemoBlockTagError error is expected.') && @die_on_demo_block_error
      e.html_message
    end
    private
    def output
      <<~END_OUTPUT
        <pre># jekyll_plugin_support becomes able to perform variable substitution after this variable is defined.
        # The value could be updated at a later stage, but no need to add that complexity unless there is a use case.
        @argument_string="#{@argument_string}"
        @helper.argv=
          #{@helper.argv&.join("\n  ")}
        # Liquid variable name/value pairs
        @helper.params=
          #{@helper.params&.map { |k, v| "#{k}=#{v}" }&.join("\n  ")}
        # The keys_values property serves no purpose any more, consider it deprecated
        @helper.keys_values=
          #{(@helper.keys_values&.map { |k, v| "#{k}=#{v}" })&.join("\n  ")}
        @layout='#{@layout}'
        @page.keys='#{@page.keys}'
        remaining_markup='#{@helper.remaining_markup}'
        @keyword1='#{@keyword1}'
        @keyword2='#{@keyword2}'
        @name1='#{@name1}'
        @name2='#{@name2}'</pre>
      END_OUTPUT
    end
    JekyllPluginHelper.register(self, 'demo_inline_tag')
  end
end
require 'cgi'
require 'jekyll_plugin_support'
# Use the JekyllSupport module namespace so the self methods are automajically found
module JekyllSupport
  DemoBlockError = JekyllSupport.define_error
  class DemoBlock < JekyllBlock
    VERSION = '0.1.2'.freeze
    def render_impl(text)
      @demo_block_error = @helper.parameter_specified? 'raise_demo_block_error'
      @keyword1         = @helper.parameter_specified? 'keyword1'
      @keyword2         = @helper.parameter_specified? 'keyword2'
      @name1            = @helper.parameter_specified? 'name1'
      @name2            = @helper.parameter_specified? 'name2'
      @standard_error   = @helper.parameter_specified? 'raise_standard_error'
      if @tag_config
        @die_on_demo_block_error = @tag_config['die_on_demo_block_error'] == true
        @die_on_standard_error   = @tag_config['die_on_standard_error'] == true
      end
      raise DemoBlockTagError, 'This DemoBlockTagError error is expected.' if @demo_block_error
      raise StandardError, 'This StandardError error is expected.' if @standard_error
      # _infinity = 1 / 0 if @standard_error # Not required
      output text
    rescue DemoBlockTagError => e # jekyll_plugin_support handles StandardError
      @logger.error { e.logger_message }
      exit! 1 if (e.message != 'This DemoBlockTagError error is expected.') && @die_on_demo_block_error
      e.html_message
    end
    private
    def output(text)
      <<~END_OUTPUT
        <pre>@helper.tag_name=#{@helper.tag_name}
        @mode=#{@mode}
        # jekyll_plugin_support becomes able to perform variable substitution after this variable is defined.
        # The value could be updated at a later stage, but no need to add that complexity unless there is a use case.
        @argument_string="#{@argument_string}"
        @helper.argv=
          #{@helper.argv&.join("\n  ")}
        # Liquid variable name/value pairs
        @helper.params=
          #{@helper.params&.map { |k, v| "#{k}=#{v}" }&.join("\n  ")}
        # The keys_values property serves no purpose any more, consider it deprecated
        @helper.keys_values=
          #{(@helper.keys_values&.map { |k, v| "#{k}=#{v}" })&.join("\n  ")}
        @helper.remaining_markup='#{@helper.remaining_markup}'
        @envs=#{@envs.keys.sort.join(', ')}
        @config['url']='#{@config['url']}'
        @site.collection_names=#{@site.collection_names&.sort&.join(', ')}
        @page['description']=#{@page['description']}
        @page['path']=#{@page['path']}
        @keyword1=#{@keyword1}
        @keyword2=#{@keyword2}
        @name1=#{@name1}
        @name2=#{@name2}
        text=#{text}</pre>
      END_OUTPUT
    end
    JekyllPluginHelper.register(self, 'demo_block_tag')
  end
end
  The following is an example of no_arg_parsing optimization.
require 'jekyll_plugin_support'
# Use the JekyllSupport module namespace so the self methods are automajically found
module JekyllSupport
  class DemoTagNoArgs < JekyllTagNoArgParsing
    VERSION = '0.1.0'.freeze
    def render_impl
      <<~END_OUTPUT
        The raw arguments passed to this <code>DemoTagNoArgs</code> instance are:<br>
        <code>#{@argument_string}</code>
      END_OUTPUT
    end
    JekyllPluginHelper.register(self, 'demo_inline_tag_no_arg')
  end
end
Run Freely
- 
    Run from the command line:
Shell$ demo/_bin/debug -r - 
    View the generated website,
    which might be at 
http://localhost:4444, depending on how you configured it. 
Plugin Debugging
- Set breakpoints in Visual Studio Code.
 - 
    Initiate a debug session from the command line by running the 
demo/_bin/debugscript:
Shell$ demo/_bin/debug Fetching gem metadata from https://rubygems.org/.......... Resolving dependencies... Fetching public_suffix 5.0.4 Fetching nokogiri 1.15.5 (x86_64-linux) Installing public_suffix 5.0.4 Installing nokogiri 1.15.5 (x86_64-linux) Bundle complete! 17 Gemfile dependencies, 96 gems now installed. Use `bundle info [gemname]` to see where a bundled gem is installed. INFO PluginMetaLogger: Loaded DraftFilter plugin. INFO PluginMetaLogger: Loaded outline_js v1.2.1 plugin. INFO PluginMetaLogger: Loaded outline v1.2.1 plugin. Configuration file: /mnt/f/jekyll_plugin_support/demo/_config.yml Cleaner: Removing /mnt/f/jekyll_plugin_support/demo/_site... Cleaner: Removing /mnt/f/jekyll_plugin_support/demo/.jekyll-metadata... Cleaner: Removing /mnt/f/jekyll_plugin_support/demo/.jekyll-cache... Cleaner: Nothing to do for .sass-cache. DEBUGGER: Debugger can attach via TCP/IP (127.0.0.1:37177) DEBUGGER: wait for debugger connection... - 
    Once the 
DEBUGGER: wait for debugger connection...message appears, run the Visual Studio Code launch configuration calledAttach with rdbg. - 
    View the generated website,
    which might be at 
http://localhost:4444, depending on how you configured it. 
Contributing
- Fork the project
 - Create a descriptively named branch
 - Add your work
 - Submit a pull request
 
License
The gem is available as open source under the terms of the MIT License.