{"id":13612542,"url":"https://github.com/mslinn/jekyll_plugin_support","last_synced_at":"2025-03-21T05:30:34.853Z","repository":{"id":65209016,"uuid":"587804832","full_name":"mslinn/jekyll_plugin_support","owner":"mslinn","description":"Ruby gem that provides a framework for writing and testing Jekyll plugins.","archived":false,"fork":false,"pushed_at":"2025-03-06T16:29:51.000Z","size":888,"stargazers_count":1,"open_issues_count":5,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-17T21:43:34.374Z","etag":null,"topics":["jekyll","jekyll-plugin"],"latest_commit_sha":null,"homepage":"https://www.mslinn.com/jekyll_plugins/jekyll_plugin_support.html","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mslinn.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-01-11T16:18:49.000Z","updated_at":"2025-03-04T23:04:15.000Z","dependencies_parsed_at":"2023-11-15T03:25:29.258Z","dependency_job_id":"fcf81c6f-8002-4208-b7f7-e00838259c9b","html_url":"https://github.com/mslinn/jekyll_plugin_support","commit_stats":{"total_commits":35,"total_committers":2,"mean_commits":17.5,"dds":"0.22857142857142854","last_synced_commit":"ea5209e2690f7fc4f241e583229a57b2572d4965"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mslinn%2Fjekyll_plugin_support","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mslinn%2Fjekyll_plugin_support/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mslinn%2Fjekyll_plugin_support/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mslinn%2Fjekyll_plugin_support/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mslinn","download_url":"https://codeload.github.com/mslinn/jekyll_plugin_support/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244745632,"owners_count":20503042,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jekyll","jekyll-plugin"],"created_at":"2024-08-01T20:00:31.366Z","updated_at":"2025-03-21T05:30:34.841Z","avatar_url":"https://github.com/mslinn.png","language":"Ruby","funding_links":[],"categories":["Coding Support for Plugins \u0026amp; Liquid Programming"],"sub_categories":[],"readme":"# `jekyll_plugin_support` [![Gem Version](https://badge.fury.io/rb/jekyll_plugin_support.svg)](https://badge.fury.io/rb/jekyll_plugin_support)\n\nAfter writing over two dozen Jekyll plugins, I distilled the common code into `Jekyll_plugin_support`.\nThis F/OSS Ruby gem facilitates writing and testing Jekyll plugins and handles the standard housekeeping that every Jekyll\ninline and block tag plugin requires.\nLogging, parsing arguments, obtaining references to site and page objects, etc. are all handled.\nThe result is faster Jekyll plugin writing with fewer bugs.\n\n`Jekyll_plugin_support` can be used to create simple Jekyll plugins in\nthe `_plugins/` directory of your Jekyll project, or gem-based Jekyll plugins.\n\nAt present, only Jekyll tags and blocks are supported.\n\nPublic plugins that use `jekyll_plugin_support` include:\n\n\u003cul style=\"columns: 2\"\u003e\n  \u003cli\u003e\u003ca href='https://www.mslinn.com/jekyll_plugins/jekyll_badge'\u003e\u003ccode\u003ejekyll_badge\u003c/code\u003e\u003c/a\u003e\u003c/li\u003e\n  \u003cli\u003e\u003ca href='https://www.mslinn.com/jekyll_plugins/jekyll_draft'\u003e\u003ccode\u003ejekyll_draft\u003c/code\u003e\u003c/a\u003e\u003c/li\u003e\n  \u003cli\u003e\u003ca href='https://www.mslinn.com/jekyll_plugins/jekyll_emoji'\u003e\u003ccode\u003ejekyll_emoji\u003c/code\u003e\u003c/a\u003e\u003c/li\u003e\n  \u003cli\u003e\u003ca href='https://www.mslinn.com/jekyll_plugins/jekyll_flexible_include.html'\u003e\u003ccode\u003ejekyll_flexible_include\u003c/code\u003e\u003c/a\u003e\u003c/li\u003e\n  \u003cli\u003e\u003ca href='https://www.mslinn.com/jekyll_plugins/jekyll_google_translate'\u003e\u003ccode\u003ejekyll_google_translate\u003c/code\u003e\u003c/a\u003e\u003c/li\u003e\n  \u003cli\u003e\u003ca href='https://www.mslinn.com/jekyll_plugins/jekyll_href.html'\u003e\u003ccode\u003ejekyll_href\u003c/code\u003e\u003c/a\u003e\u003c/li\u003e\n  \u003cli\u003e\u003ca href='https://www.mslinn.com/jekyll_plugins/jekyll_img.html'\u003e\u003ccode\u003ejekyll_img\u003c/code\u003e\u003c/a\u003e\u003c/li\u003e\n  \u003cli\u003e\u003ca href='https://www.mslinn.com/jekyll_plugins/jekyll_outline.html'\u003e\u003ccode\u003ejekyll_outline\u003c/code\u003e\u003c/a\u003e\u003c/li\u003e\n  \u003cli\u003e\u003ca href='https://www.mslinn.com/jekyll_plugins/jekyll_pre.html'\u003e\u003ccode\u003ejekyll_pre\u003c/code\u003e\u003c/a\u003e\u003c/li\u003e\n  \u003cli\u003e\u003ca href='https://www.mslinn.com/jekyll_plugins/jekyll_quote.html'\u003e\u003ccode\u003ejekyll_quote\u003c/code\u003e\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n... and also the demonstration plugins in\n[`jekyll_plugin_support`](https://github.com/mslinn/jekyll_plugin_support/tree/master/demo/_plugins)\n\n\n## Features\n\nJekyll plugin tags created from `jekyll_plugin_support` framework automatically have the following features:\n\n1. Boilerplate is removed, so you can focus on the required logic and output.\n2. Arguments are parsed for keywords and name/value parameters.\n3. Single or double quotes can be used for arguments and parameters.\n4. Important variables are predefined.\n5. Error handling is standardized, and includes an automatically defined error type\n   and corresponding CSS tag for each Jekyll tag.\n6. Jekyll and Liquid variables, including `layout`, `page` and `include` variables,\n   can be passed as parameters to tags, and used in the body of block tags.\n7. Plugin registration is integrated, and important configuration details are reported during registration.\n8. A custom logger is created for each tag, independent of the default Jekyll logger.\n9. Variables can be defined in `_config.yml`, and optionally have different values for debug mode,\n   production mode and test mode.\n10. An attribution message is available.\n11. Draft pages are automatically detected.\n12. A demonstration website is provided for easy testing of every plugin.\n13. Visual Studio Code debugging is set up for the plugin code and the demo website.\n14. Plugins can be subclassed.\n15. [`Nugem`](https://mslinn.com/ruby/6800-nugem.html) can create working scaffolding for new plugins built\n    using `jekyll_plugin_support`.\n16. Four new attributes are added to\n  [`site`](https://jekyllrb.com/docs/variables/#site-variables):\n\n    a. `all_collections` includes all documents in all collections.\n\n    b. `all_documents` includes `all_collections` plus all standalone pages.\n\n    c. `everything` includes `all_documents` plus all static files.\n\n    d. `sorted_lru_files` is used by a new binary search lookup for matching page suffixes.\n      The `jekyll_href` and `jekyll_draft` plugins use this feature.\n\n## Installation\n\n### For A Jekyll Website\n\n`Jekyll_plugin_support` is packaged as a Ruby gem.\nIf you want to write a custom Jekyll plugin that will reside in a Jekyll project’s `_plugins` directory,\nadd the following line to your Jekyll plugin’s `Gemfile`.\n\n```ruby\ngroup :jekyll_plugins do\n  # ...\n  gem 'jekyll_plugin_support', '\u003e= 1.1.0'\n  # ...\nend\n```\n\nRun the standard `jekyll_plugin_support` setup procedure:\n\n```shell\n$ bin/setup\n```\n\n\n### As a Gem Dependency\n\nIf your custom plugin will be packaged into a gem, add the following to your plugin’s `.gemspec`:\n\n```ruby\nGem::Specification.new do |spec|\n  # ...\n  spec.add_dependency 'jekyll_plugin_support', '\u003e= 1.1.0'\n  # ...\nend\n```\n\nInstall the `jekyll_plugin_support` gem into your plugin project in the usual manner:\n\n```shell\n$ bundle\n```\n\nCopy the CSS classes from\n[`demo/assets/css/jekyll_plugin_support.css`](demo/assets/css/jekyll_plugin_support.css)\nto your Jekyll project\u0026rsquo;s CSS file.\n\n\n\n\n## About `jekyll_plugin_support`\n\nThis Jekyll plugin includes a generator,\ntriggered by a high-priority hook, and a block tag called `all_collections`.\n\n`JekyllSupport::JekyllBlock` and `JekyllSupport::JekyllTag`\nprovide support for [Jekyll tag block plugins](https://jekyllrb.com/docs/plugins/tags/#tag-blocks)\nand [Jekyll inline tag plugins](https://jekyllrb.com/docs/plugins/tags/), respectively.\nThey are very similar in construction and usage.\n\nInstead of subclassing your custom Jekyll block tag class from `Liquid::Block`,\nsubclass from `JekyllSupport::JekyllBlock`.\nSimilarly, instead of subclassing your custom Jekyll tag class from `Liquid::Tag`,\nsubclass from `JekyllSupport::JekyllTag`.\n\nBoth JekyllSupport classes instantiate new instances of\n[`PluginMetaLogger`](https://github.com/mslinn/jekyll_plugin_logger) (called `@logger`) and\n[`JekyllPluginHelper`](https://github.com/mslinn/jekyll_plugin_support/blob/master/lib/jekyll_plugin_support_helper.rb)\n(called `@helper`).\n\n`JekyllPluginHelper` defines a generic initialize method, and your tag or block tag class should not need to override it.\nAlso, your tag or block tag class should not define a method called render, because `jekyll_plugin_support` defines one.\n\nInstead, define a method called `render_impl`.\nFor inline tags, `render_impl` does not accept any parameters.\nFor block tags, a single parameter is required, which contains text passed from your block in the page.\n\nYour implementation of render_impl can parse parameters passed to your tag, as described in\n[Tag Parameter Parsing](http://mslinn.com/jekyll/10100-jekyll-plugin-background.html#params).\n\nIn addition, within \u003ccode\u003erender_impl\u003c/code\u003e,\nthe arguments passed to the tag will have been tokenized and parsed,\nwith Jekyll and Liquid variables substituted for their values,\nand all the public Jekyll variables will be available as instance variables.\nError handling will also have been set up,\nand access to your tag's entry within \u003ccode\u003e_config.yml\u003c/code\u003e will have been set up.\n\n\n\n## General Usage\n\nPlease see the [`demo/`](demo/) project for a well-documented set of demonstration Jekyll plugins built from `jekyll_plugin_support`.\nAdditional information is available [here](https://mslinn.com/jekyll/10200-jekyll-plugin-background.html) and the\n[`jekyll_plugin_support`](https://www.mslinn.com/jekyll_plugins/jekyll_plugin_support.html) documentation.\n\n[`JekyllSupport::JekyllBlock`](https://github.com/mslinn/jekyll_plugin_support/blob/master/lib/jekyll_plugin_support_block.rb) and\n[`JekyllSupport::JekyllTag`](https://github.com/mslinn/jekyll_plugin_support/blob/master/lib/jekyll_plugin_support_tag.rb) provide\nsupport for Jekyll block tags and Jekyll inline tags, respectively.\nThey are similar in construction and usage.\n\nInstead of subclassing your Jekyll block tag class from `Liquid::Block`,\nsubclass from `JekyllSupport::JekyllBlock` instead.\n\nLikewise, instead of subclassing your Jekyll inline tag class from `Liquid::Tag`,\nsubclass from `JekyllSupport::JekyllTag` instead.\n\nBoth `JekyllSupport` classes instantiate new instances of\n[`PluginMetaLogger`](https://github.com/mslinn/jekyll_plugin_logger) (called `@logger`) and\n[`JekyllPluginHelper`](lib/jekyll_plugin_helper.rb) (called `@helper`).\n\n\n### Inline and Block Tag Plugin Implementation\n\nBoth `JekyllSupport` classes define a generic `initialize` method,\nand your inline tag or block tag class should not override it.\n\nAlso, your inline tag or block tag class should not define a method called `render`,\nbecause both `JekyllSupport` classes define this method.\n\n\nInstead, define a method called `render_impl`.\nFor inline tags, `render_impl` does not accept any parameters.\nFor block tags, a single parameter is required, which receives any text enclosed within your block by the website author.\n\n\n### New `Site` Attributes\n\nNo explicit initialization or setup is required.\nJekyll plugins can access the value of\n`site.all_collections`, `site.all_documents` and `site.everything`;\nhowever, Liquid code in Jekyll pages and documents cannot.\n\n\n### Excluding Files\n\nThere are two ways to exclude files from the new `site` attributes.\n\n1) The [`exclude` entry in `_config.yml`](https://jekyllrb.com/docs/configuration/options#global-configuration)\n   can be used as it normally would.\n\n2) Adding the following entry to a page\u0026rsquo;s front matter causes that page to be excluded\n  from the collection created by this plugin:\n\n   ```html\n   ---\n   exclude_from_all: true\n   ---\n   ```\n\n### `all_collections` Plugin Usage\n\nJekyll generators and tags receive an enhanced version of the `site` Jekyll variable.\n\n\n#### From a Custom Plugin\n\nIn the following example of how to use the `all_collections` plugin in a custom plugin,\nthe `do_something_with` function processes all `Jekyll::Page`s, `Jekyll:Document`s, and static files.\n\n```ruby\n@site.everything.each do |apage|\n  do_something_with apage\nend\n```\n\n\n#### Using the Block Tag\n\nThe general form of the Jekyll tag, including all options, is:\n\n```html\n{% all_collections\n  date_column='date|last_modified'\n  heading='All Posts'\n  id='asdf'\n  sort_by='SORT_KEYS'\n%}\n```\n\n\n##### `date_column` Attribute\n\nOne of two date columns can be displayed in the generated HTML:\neither `date` (when the article was originally written),\nor `last_modified`.\nThe default value for the `date_column` attribute is `date`.\n\n\n##### `heading` Attribute\n\nIf no `heading` attribute is specified, a heading will automatically be generated, which contains the `sort_by` values,\nfor example:\n\n```html\n{% all_collections id='abcdef' sort_by=\"last_modified\" %}\n```\n\nThe above generates a heading like:\n\n```html\n\u003ch2 id=\"abcdef\"\u003eAll Posts Sorted By last_modified\u003c/h2\u003e\n```\n\nTo suppress both a `h2` heading (and the enclosed `id`) from being generated,\nspecify an empty string for the value of `heading`:\n\n```html\n{% all_collections heading='' %}\n```\n\n\n##### `id` Attribute\n\nIf your Jekyll layout employs [`jekyll-toc`](https://github.com/allejo/jekyll-toc), then `id` attributes are important.\nThe `jekyll-toc` include checks for `id` attributes in `h2` ... `h6` tags, and if found,\nand if the attribute value is enclosed in double quotes\n(`id=\"my_id\"`, not single quotes like `id='my_id'`),\nthen the heading is included in the table of contents.\n\nTo suppress an `id` from being generated,\nand thereby preventing the heading from appearing in the automatically generated table of contents from `jekyll-toc`,\nspecify an empty string for the value of `id`, like this:\n\n```html\n{% all_collections id='' %}\n```\n\n\n##### `SORT_KEYS` Values\n\n`SORT_KEYS` specifies how to sort the collection.\nValues can include one or more of the following attributes:\n`date`, `destination`, `draft`, `label`, `last_modified`, `last_modified_at`, `path`, `relative_path`,\n`title`, `type`, and `url`.\nAscending sorts are the default; however, a descending sort can be achieved by prepending `-` before an attribute.\n\nTo specify more than one sort key, provide a comma-delimited string of values.\nIncluded spaces are ignored.\nFor example, specify the primary sort key as `draft`,\nthe secondary sort key as `last_modified`,\nand the tertiary key as `label`:\n\n```html\n{% all_collections\n  date_column='last_modified'\n  heading='All Posts'\n  id='asdf'\n  sort_by='draft, last_modified, label'\n%}\n```\n\n\n#### Liquid Usage Examples\n\nHere is a short Jekyll page, including front matter,\ndemonstrating this plugin being invoked with all default attribute values:\n\n```html\n---\ndescription: \"\n  Dump of all collections, sorted by date originally written, newest to oldest.\n  The heading text will be \u003ccode\u003eAll Posts Sorted By -date\u003c/code\u003e\n\"\nlayout: default\ntitle: Testing, 1, 2, 3\n---\n{% all_collections %}\n```\n\nFollowing are examples of how to specify the sort parameters.\n\n**Explicitly express the default sort**\u003cbr\u003e\n(sort by the date originally written, newest to oldest):\n\n```html\n{% all_collections sort_by=\"-date\" %}\n```\n\nSort by date, from oldest to newest:\n\n```html\n{% all_collections sort_by=\"date\" %}\n```\n\n**Sort by the date last modified, oldest to newest:**\n\n```html\n{% all_collections sort_by=\"last_modified\" %}\n```\n\n**Sort by the date last modified, newest to oldest:**\n\n```html\n{% all_collections sort_by=\"-last_modified\" %}\n```\n\n**Several attributes can be specified as sort criteria**\u003cbr\u003e\nby passing them as a comma-delimited string.\nIncluded spaces are ignored:\n\n```html\n{% all_collections sort_by=\"-last_modified, -date\" %}\n{% all_collections sort_by=\"-last_modified, title\" %}\n{% all_collections sort_by=\"-last_modified, -date, title\" %}\n```\n\n**The following two examples produce the same output:**\n\n```html\n{% all_collections sort_by=\"-last_modified,-date\" %}\n{% all_collections sort_by=\"-last_modified, -date\" %}\n```\n\n\n## Predefined Plugin Variables\n\n`Jekyll_plugin_support` defines the following Ruby variables that you can use in your plugin\u0026rsquo;s `render_impl` method:\n\n* `@argument_string` Unparsed markup passed as a parameter to your block tag and inline tag.\n\n* `@argv` returns any remaining tokens after `parameter_specified?` has been invoked.\n\n* [`@attribution`](#subclass-attribution) Attribution markup\n\n* [`@config`](https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/about-github-pages-and-jekyll#configuring-jekyll-in-your-github-pages-site)\n  The [YAML](https://yaml.org/) Jekyll site configuration file\n\n* [`@helper`](https://github.com/mslinn/jekyll_plugin_support/blob/master/lib/jekyll_plugin_helper.rb)\n  `JekyllPluginHelper` instance for your plugin.\n\n* [`@layout`](https://jekyllrb.com/docs/variables/#global-variables) Layout information\n\n* `@logger` [`jekyll_plugin_logger`](https://github.com/mslinn/jekyll_plugin_logger) instance for your Jekyll plugin.\n\n* [`@mode`](https://jekyllrb.com/docs/configuration/environments/)\n  Indicates `production`, `test` or `development` mode.\n\n* [`@page`](https://jekyllrb.com/docs/variables/#page-variables) `Jekyll::Page` variables\n\n* [`@paginator`](https://jekyllrb.com/docs/variables/#page-variables) Pagination variables\n\n* [`@scopes`](https://jekyllrb.com/docs/variables/)\n  See the [`jekyll_plugin_support` demo project](demo/variables.html)\n\n* [`@site`](https://jekyllrb.com/docs/variables/#site-variables) Site variables\n\n* [`@tag_config`](lib/jekyll_plugin_support_tag.rb)\n  Contents of the section of `_config.yml` named after your plugin.\n\n* `@tag_name` Name of your Jekyll block tag or inline tag plugin.\n\n* [`@theme`](https://jekyllrb.com/docs/variables/#global-variables) Theme variables\n\n* `text` Content provided to your block tag.\n\n\n## Argument Parsing\n\nTag arguments can be obtained within `render_impl`.\nBoth keyword options and name/value parameters are supported.\n\nBoth `JekyllTag` and `JekyllBlock` use the standard Ruby mechanism for parsing command-line options:\n[`shellwords`](https://ruby-doc.org/stdlib-2.5.1/libdoc/shellwords/rdoc/Shellwords.html) and\n[`key_value_parser`](https://www.rubydoc.info/gems/key-value-parser).\n\nAll your code has to do is to specify the keywords to search for in the string\npassed from the HTML page that your tag is embedded in.\nThe included `demo` website has examples;\nboth [`demo/_plugins/demo_inline_tag.rb`](demo/_plugins/demo_inline_tag.rb) and\n[`demo/_plugins/demo_block_tag.rb`](demo/_plugins/demo_block_tag.rb) contain the following:\n\n```ruby\n@keyword1  = @helper.parameter_specified? 'keyword1'\n@keyword2  = @helper.parameter_specified? 'keyword2'\n@name1     = @helper.parameter_specified? 'name1'\n@name2     = @helper.parameter_specified? 'name2'\n```\n\nIf an argument has a variable reference in it, the value of the variable is substituted for the reference.\nFor example, given:\n\n* `_layouts/default.html` defines a variable called `var_layout` in its front matter.\n* `index.html` defines a variable called `var_page` in its front matter.\n* `index.html` assigns a variable called `x` via the liquid `assign` statement.\n\n... then the following references in a page will be substituted for their values in arguments and in block tag bodies:\n\n```html\n{% my_block_tag\n  param1=\"x={{x}}\"\n  param2=\"var_page={{page.var_page}}\"\n  param3=\"var_layout={{layout.var_layout}}\"\n%}\n\nAssigned variables do not need a namespace: x={{x}}\n\nPage variables must be qualified with the 'page' namespace:\n  var_page={{page.var_page}}\n\nLayout variables must be qualified with the 'layout' namespace:\n  var_layout={{layout.var_layout}}\n{% endmy_block_tag %}\n```\n\nYou can see similar code in [`demo/demo_inline_tag.html`](demo/demo_inline_tag.html).\n\nThe `page['excerpt']` and `page['output']` key/value pairs are removed from processing because of recursion issues.\nYou cannot look up those values from a `jekyll_plugin_support` plugin.\n\n\n### Keyword Options\n\nFor all keyword options, values specified in the document _may_ be provided.\nIf a value is not provided, the value `true` is assumed.\nOtherwise, if a value is provided, it _must_ be wrapped in single or double quotes.\n\n\n### Examples\n\nThe following examples use the `die_if_error` keyword option for the\n[`pre`](https://www.mslinn.com/jekyll_plugins/jekyll_pre.html#tag) and\n[`exec`](https://www.mslinn.com/jekyll_plugins/jekyll_pre.html#tag_exec)\ntags from the [`jekyll_pre`](https://www.mslinn.com/jekyll_plugins/jekyll_pre.html) plugin %}.\n\n\n#### Specifying Tag Option Values\n\nThe following sets `die_if_error` `true`:\n\n```html\n{% pre die_if_error %} ... {% endpre %}\n```\n\nThe above is the same as writing:\n\n```html\n{% pre die_if_error='true' %} ... {% endpre %}\n```\n\nOr writing:\n\n```html\n{% pre die_if_error=\"true\" %} ... {% endpre %}\n```\n\nNeglecting to provide surrounding quotes around the provided value causes the parser to not recognize the option.\nInstead, what you had intended to be the keyword/value pair will be parsed as part of the command.\nFor the `pre` tag, this means the erroneous string becomes part of the `label` value,\nunless `label` is explicitly specified.\nFor the `exec` tag, this means the erroneous string becomes part of the command to execute.\nThe following demonstrates the error.\n\n```html\n{% pre die_if_error=false %} ... {% endpre %}\n```\n\nThe above causes the label to be `die_if_error=false`.\n\n```html\n{% exec die_if_error=false ls %} ... {% endpre %}\n```\n\nThe above causes the command to be executed to be `die_if_error=false ls` instead of `ls`.\n\n\n### Quoting\n\nParameter values can be quoted.\n\nIf the value consists of only one token then quoting is optional.\nThe following name/value parameters all have the same result:\n\n* `pay_tuesday=\"true\"`\n* `pay_tuesday='true'`\n* `pay_tuesday=true`\n* `pay_tuesday`\n\nIf the values consist of more than one token, quotes must be used.\nThe following examples both yield the same result:\n\n* `pay_tuesday=\"maybe not\"`\n* `pay_tuesday='maybe not'`\n\n\n### Remaining Markup\n\nAfter your plugin has parsed all the keyword options and name/value parameters,\ncall `@helper.remaining_markup` to obtain the remaining markup that was passed to your plugin.\n\n\n## Configuration Variables\n\n`jekyll_plugin_support` provides support for\n[Liquid variables](https://shopify.github.io/liquid/tags/variable/)\nto be defined in `_config.yml`, in a section called `liquid_vars`.\nThese variables behave exactly like Liquid variables defined by `assign` and `capture` expressions,\nexcept they are global in scope; these variables are available in every Jekyll web page.\n\nThe following `_config.yml` fragment defines 3 variables called `var1`, `var2` and `var3`:\n\n```yaml\nliquid-vars:\n  var1: value1\n  var2: 'value 2'\n  var3: value3\n```\n\nLiquid variables defined in this manner are intended to be embedded in a webpage.\nThey are can be used like any other Liquid variable.\n\n\n## Variable Expansion\n\nJekyll expands Liquid variable references during the page rendering process.\nJekyll does not expand Liquid variable references passes as parameters to tag and block plugins, however.\nHowever, plugins made from `jekyll_plugin_support` automatically\nexpand all types of variable references passed as parameters and in block tag bodies.\n\n`Jekyll_plugin_support` tag and block plugins expand the following types of variables:\n\n* Jekyll_plugin_support configuration variables, discussed above.\n* Jekyll [page](https://jekyllrb.com/docs/variables/#page-variables) and\n  [layout](https://jekyllrb.com/docs/layouts/#variables) variables.\n* Inline Liquid variables (defined in [assign](https://shopify.dev/docs/api/liquid/tags/assign) and [capture](https://shopify.dev/docs/api/liquid/tags/capture) statements).\n\nIn the following example web page, Jekyll expands the `var1` reference within the `\u003cp\u003e\u003c/p\u003e` tag,\nbut not the `var1` or `var2` references passed to `my_plugin`.\n\n```html\n\u003cp\u003eThis is the value of var1: {{var1}}.\u003c/p\u003e\n\n{% my_plugin param1=\"{{var1}}\" param2=\"{{var2}}\" %}\n```\n\nAssuming that `my_plugin` was written as a `jekyll_plugin_support` plugin,\nall variable references in its parameters are expanded.\nThus, the above is interpreted as follows when `my_plugin` is evaluated during the Jekyll rendering process:\n\u003c/p\u003e\n\n```html\n\u003cp\u003eThis is the value of var1: value1.\u003c/p\u003e\n\n{% my_plugin param1=\"value1\" param2=\"value 2\" %}\n```\n\n\n`Jekyll_plugin_support` expands most of the [plugin variables described above](#predefined-variables),\nreplacing Liquid variable references with their values.\nThe exception is `@argument_string`, which is not expanded.\n\n\n### Liquid Variable Values Specific To Production And Development Modes\n\n`jekyll_plugin_support` allows Liquid variables defined in `_config.yml` to have different values\nwhen Jekyll is running in `development`, `production` and `test` modes.\nWhen injecting variables into your Jekyll website, `Jekyll_plugin_support`\nrefers to definitions specific to the current environment,\nand then refers to other definitions that are not overridden.\n\nHere is an example:\n\n```yaml\nliquid-vars:\n  development:\n    var1: 'http://localhost:4444/demo_block_tag.html'\n    var2: 'http://localhost:4444/demo_inline_tag.html'\n  production:\n    var1: 'https://github.com/django/django/blob/3.1.7'\n    var2: 'https://github.com/django-oscar/django-oscar/blob/3.0.2'\n  var3: 'https://github.com/mslinn'\n```\n\nFor the above, the following variable values are set in `development` mode:\n\n* `var1`: `http://localhost:4444/demo_block_tag.html`\n* `var2`: `http://localhost:4444/demo_inline_tag.html`\n* `var3`: `https://github.com/mslinn`\n\n... and the following variable values are set in `production` and `test` modes:\n\n* `var1`: `https://github.com/django/django/blob/3.1.7`\n* `var2`: `https://github.com/django-oscar/django-oscar/blob/3.0.2`\n* `var3`: `https://github.com/mslinn`\n\n\n### Liquid Variables in `jekyll_plugin_support` Subclasses\n\nYou can define additional Liquid variables in plugins built using `jekyll_plugin_support`.\nTo do this, make entries in `_config.yml` under a key named after the value of `@tag_name`.\n\nFor example, let\u0026rsquo;s imagine you create a plugin using `jekyll_plugin_support`,\nand hou register it with the name `phonetic_alphabet`.\nYou could define Liquid variables that would be made available to content pages in web applications that\nincorporate the `phonetic_alphabet` plugin.\nThe following section in `_config.yml` defines variables called `x`, `y` and `z`,\nwith values `xray`, `yankee` and `zulu`, respectively:\n\n```yaml\nphonetic_alphabet:\n  x: xray\n  y: yankee\n  z: zulu\n```\n\nThe above definitions allow you to write content pages that use those variables, like the following page containing markup:\n\n```html\n---\nlayout: default\ntitle: Variable demo\n---\nThe letter `x` is pronounced {{x}}.\nSimilarly, the letters `y` and `z` are pronounced {{y}} and {{z}}.\n```\n\n\n### Evaluating Include Variables\n\nThis information is only useful if a plugin might be executed from within an included file.\n\nWhile Liquid handles regular variables, Jekyll has special handling for variables defined by include parameters.\nFor example, the following defines a variable in the `include` scope called `var1`\nwhen processing the body of an included file:\n\n```html\n{% include myfile.html var1='value1' %}\n```\n\nYou can obtain the value of this variable from the `render_impl` method of a\n`JekyllSupport::JekyllTag` or `JekyllSupport::JekyllBlock` subclass as follows:\n\n```ruby\n  @var1 = @scopes.first['include']\u0026.[]('var1')\n```\n\n\n## Automatically Created Error Classes\n\n`JekyllSupport::JekyllBlock` and `JekyllSupport::JekyllTag` subclasses\nautomatically create error classes, named after the subclass.\n\nFor example, if you create a `JekyllSupport::JekyllBlock` subclass called `DemoBlockTag`,\nthe automatically generated error class will be called `DemoBlockTagError`.\n\nAlthough you could use it as you would any other error class, `JekyllPluginSupport`\nprovides additional helper methods.\nThese methods fill in the page path and line number that caused the error, shorten the stack trace,\nlog an error message, and can be used to return an HTML-friendly version of the message to the web page.\n\nThe following example is a shortened version of `demo/_plugins/demo_block_tag.rb`.\nYou might want to write similar code in your `rescue` blocks.\n\n```ruby\nclass DemoBlock \u003c JekyllSupport::JekyllBlock\n  VERSION = '0.1.2'.freeze\n\n  def render_impl(text)\n    raise DemoBlockTagError, 'Fall down, go boom.'\n  rescue DemoBlockTagError =\u003e e\n    @logger.error e.logger_message\n    exit! 1 if @die_on_demo_block_error\n\n    e.html_message\n  end\nend\n```\n\nError class methods have been provided for standardized and convenient error handling:\n\n* `shorten_backtrace` - most of the lines that spew from a Jekyll backtrace are uninteresting.\n* `logger_message` - The message is constructed from the string provided when the error was raised,\n   with the page path and line number added.\n* `html_message` - The same as `logger_message`, but constructed with HTML.\n\n\n## Self-Reporting Upon Registration\n\nWhen each tag is registered, it self-reports, for example:\n\n```text\nINFO PluginMetaLogger: Loaded plugin demo_inline_tag v0.1.2. It has:\n  Error class: DemoTagError\n  CSS class for error messages: demo_tag_error\n\n  _config.yml contains the following configuration for this plugin:\n    {\"die_on_demo_tag_error\"=\u003efalse, \"die_on_standard_error\"=\u003efalse}\n\n\nINFO PluginMetaLogger: Loaded plugin demo_inline_tag_no_arg v0.1.0. It has:\n  Error class: DemoTagNoArgsError\n  CSS class for error messages: demo_tag_no_args_error\n\n  _config.yml does not contain configuration information for this plugin.\n  You could add a section containing default values by specifying a section for the tag name,\n  and an entry whose name starts with `die_on_`, followed by a snake_case version of the error name.\n\n    demo_inline_tag_no_arg:\n      die_on_demo_tag_no_args_error: false\n```\n\n\n## `no_arg_parsing` Optimization\n\nIf your tag or block plugin only needs access to the raw arguments passed from the web page,\nwithout tokenization, and you expect that the plugin might be invoked with large amounts of text,\nderive your plugin from `JekyllBlockNoArgParsing` or `JekyllTagNoArgParsing`.\n\n## Writing Plugins\n\nThe following minimal examples define `VERSION`,\nwhich is important because `JekyllPluginHelper.register` logs that value when registering the plugin.\n\nThis is how you would define plugins in the `_plugins` directory\n\n**Boilerplate for an inline tag plugin**\n\n```ruby\nrequire 'jekyll_plugin_support'\n\nmodule Jekyll\n  class DemoTag \u003c JekyllSupport::JekyllTag\n    VERSION = '0.1.0'.freeze\n\n    def render_impl\n      @helper.gem_file __FILE__ # Enables attribution; only works when plugin is a gem\n      # Your Jekyll plugin logic goes here\n    end\n\n    JekyllPluginHelper.register(self, 'demo_tag')\n  end\nend\n```\n\n**Boilerplate for a tag block plugin**\n\n```ruby\nrequire 'jekyll_plugin_support'\n\nmodule Jekyll\n  class DemoBlock \u003c JekyllSupport::JekyllBlock\n    VERSION = '0.1.0'.freeze\n\n    def render_impl(text)\n      @helper.gem_file __FILE__ # Enables attribution; only works when plugin is a gem\n      # Your Jekyll plugin logic goes here\n    end\n\n    JekyllPluginHelper.register(self, 'demo_block')\n  end\nend\n```\n\nIf your plugin is packaged as a gem, then you might need to include `version.rb` into the plugin class.\nFor example, if your version module looks like this:\n\n**lib/my_plugin/version.rb**:\n\n```ruby\nmodule MyPluginVersion\n  VERSION = '0.5.0'.freeze\nend\n```\n\nThen your plugin can incorporate the VERSION constant into your plugin like this:\n\n**lib/my_plugin.rb**:\n\n```ruby\nrequire 'jekyll_plugin_support'\nrequire_relative 'my_plugin/version'\n\nmodule Jekyll\n  class MyBlock \u003c JekyllSupport::JekyllBlock\n    include MyPluginVersion\n\n    def render_impl(text)\n      @helper.gem_file __FILE__ # Enables attribution; only works when plugin is a gem\n      # Your code here\n    end\n\n    JekyllPluginHelper.register(self, 'demo_block')\n  end\nend\n```\n\n## Attribution\n\n`JekyllTag` and `JekyllBlock` subclasses of `jekyll_plugin_support` can utilize the `attribution`\noption if they are published as a gem.\n`JekyllTagNoArgParsing` and `JekyllBlockNoArgParsing` subclasses cannot.\nThis feature is usually only desired for `JekyllBlock` subclasses.\n\n* When used as a keyword option, a default value is used for the attribution string.\n* When used as a name/value option, the attribution string can be specified.\n\nUsing the `attribution` option cause subclasses to replace their usual output with HTML that looks like:\n\n```html\n\u003cdiv id=\"jps_attribute_12345\" class=\"jps_attribute\"\u003e\n  \u003ca href=\"https://github.com/mslinn/jekyll_outline\"\u003e\n    \u003cb\u003eGenerated by \u003ccode\u003ejekyll_outline\u003c/code\u003e.\n  \u003c/a\u003e\n\u003c/div\u003e\n```\n\nThe `id` attribute is in the sample HTML above is randomized so more than one attribution can appear on a page.\n\n\n### Attribution Generation\n\nYou can decide where you want the attribution string for your Jekyll tag to appear by invoking `@helper.attribute`.\nFor example, this is how the\n[`jekyll_outline` tag](https://github.com/mslinn/jekyll_outline/blob/v1.1.1/lib/outline_tag.rb#L32-L46) generates output:\n\n```html\n\u003c\u003c~HEREDOC\n  \u003cdiv class=\"outer_posts\"\u003e\n  #{make_entries(collection)\u0026.join(\"\\n\")}\n  \u003c/div\u003e\n  #{@helper.attribute if @helper.attribution}\nHEREDOC\n```\n\n\n### Usage\n\nTypical usage for the `attribution` tag is:\n\n```html\n{% my_block_tag attribution %}\n  Content of my_block_tag.\n{% endmy_block_tag %}\n```\n\nThe normal processing of `my_block_tag` is augmented by interpolating the attribution format string,\nwhich is a Ruby-compatible interpolated string.\n\nThe default attribution format string is:\n\n```ruby\n\"Generated by the #{name} #{version} Jekyll plugin, written by #{author} #{date}.\"\n```\n\nBecause `jekyll_plugin_suppprt` subclasses are `gem`s, their `gemfile`s define values for\n`name`, `version`, `homepage`, and `authors`, as well as many other properties.\nThe `date` property is obtained from the plugin/gem publishing date.\n\nAn alternative attribution string can be specified properties can be output using any of the above properties:\n\n```html\n{% my_tag attribution=\"Generated by the #{name} #{version} Jekyll plugin, written by #{author} #{date}\" %}\n```\n\n## Subclassing Plugins\n\nJekyll plugins created using `jekyll_plugin_support` are implemented as Ruby classes.\nIf you would like to create a version of an existing Jekyll plugin, you will need to subclass the plugin.\nIn order to do that, you will need to override the plugin name and version, which are defined as constants.\n\n`Jekyll_plugin_support` provides a method that allows\na constant to be redefined, called `redef_without_warning`.\nUse it in a subclass like this:\n\n```ruby\nredef_without_warning :PLUGIN_NAME, 'my_plugin'.freeze\nredef_without_warning :VERSION, '0.1.0'.freeze\n```\n\n\n## Generator\n\nThis plugin incorporates a generator, which adds four new attributes to\n[`site`](https://jekyllrb.com/docs/variables/#site-variables):\n`all_collections`, `all_documents`, `everything`, and `sorted_lru_files`.\n\nThese three attributes can be referenced as `site.everything`, `site.all_collections`\nand `site.all_documents`.\n\n* `all_collections` includes all documents in all collections.\n\n* `all_documents` includes `all_collections` plus all standalone pages.\n\n* `everything` includes `all_documents` plus all static files.\n\n* `sorted_lru_files` is used by a new binary search lookup for matching page suffixes.\n  Currently only `jekyll_href` and `jekyll_draft` use this feature.\n\n\n### Collection Management\n\nJekyll provides inconsistent attributes for\n[`site.pages`](https://jekyllrb.com/docs/pages/),\n[`site.posts`](https://jekyllrb.com/docs/posts/) and\n[`site.static_files`](https://jekyllrb.com/docs/static-files/).\n\n\n* While the `url` attributes of items in `site.posts` and `site.pages` start with a slash (/),\n  `site.static_files` items do not have a `url` attribute.\n* Static files have a `relative_path` attribute, which starts with a slash (/),\n  but although that attribute is also provided in `site.posts` and `site.pages`,\n  those values do not start with a slash.\n* Paths ending with a slash (`/`) imply that a file called `index.html` should be fetched.\n* HTML redirect files created by the\n  [`jekyll-redirect-from`](https://github.com/jekyll/jekyll-redirect-from) Jekyll plugin,\n  which are included in `site.static_files`, should be ignored.\n\nThese inconsistencies mean that combining the standard three collections of files\nprovided as `site` attributes will create a new collection that is difficult\nto process consistently:\n\n```ruby\n# This pseudocode creates `oops`, which is problematic to process consistently\noops = site.all_collections + site.pages + site.static_files\n```\n\n`oops`, above, is difficult to process because of inconsistencies in the provided attributes\nand how the attributes are constructed.\n\n\n### Solving The Problem\n\nThe generator normalizes these inconsistencies by utilizing the `APage` class\nand filtering out HTML redirect files.\n\nThe `all_collections` collection contains `APage` representations of `site.collections`.\n\nThe `all_documents` collection contains `APage` representations of `site.pages`.\n\nThe `everything` collection contains `APage` representations of:\n\n```text\n# Pseudocode\nsite.collections + site.pages + site.static_files - HTML_redirect_files\n```\n\n\n## The `APage` Class\n\nThe `site.all_collections`, `site.all_documents` and `site.everything` attributes\nconsist of arrays of [`APage`](lib/hooks/a_page.rb) instances.\n\nThe `APage` class has the following attributes:\n`content` (HTML or Markdown), `data` (array), `date` (Ruby Date), `description`, `destination`,\n`draft` (Boolean), `ext`, `href`, `label`, `last_modified` or `last_modified_at` (Ruby Date),\n`layout`, `origin`, `path`, `relative_path`, `tags`, `title`, `type`, and `url`.\n\n* `href` always starts with a slash.\n  This value is consistent with `a href` values in website HTML.\n  Paths ending with a slash (`/`) have `index.html` appended so the path specifies an actual file.\n\n* `origin` indicates the original source of the item.\n  Possible values are `collection`, `individual_page` and `static_file`.\n  Knowing the origin of each item allows code to process each type of item appropriately.\n\n\n## `all_collections` Block Tag\n\nThe `all_collections` block tag creates a formatted listing of Jekyll files.\nThe ordering is configurable; by default, the listing is sorted by `date`, newest to oldest.\nThe `all_collections` tag has a `data_source` parameter that specifies which new property to report on\n(`all_collections`, `all_documents`, or `everything`).\n\n\n## Requirements\n\nAll the pages in the Jekyll website must have an implicit date\n(for example, all posts are assigned this property by Jekyll),\nor an explicit `date` set in front matter, like this:\n\n```html\n---\ndate: 2022-01-01\n---\n```\n\nIf a front matter variable called `last_modified` or `last_modified_at` exists,\nits value will be converted to a Ruby `Date`:\n\n```html\n---\nlast_modified: 2023-01-01\n---\n```\n\nOr:\n\n```html\n---\nlast_modified_at: 2023-01-01\n---\n```\n\nOtherwise, if `last_modified` or `last_modified_at` is not present in the front matter for a page,\nthe `date` value will be used last modified date value.\n\n\n## Development\n\nAfter checking out the `jekyll_plugin_suppprt` repository, run `bin/setup` to install dependencies.\n\n`bin/console` provides an interactive prompt that allows you to experiment.\n\n\nTo build and install this gem onto your local machine, run:\n\n```shell\n$ bundle exec rake install\njekyll_plugin_support 0.1.0 built to pkg/jekyll_plugin_support-0.1.0.gem.\njekyll_plugin_support (0.1.0) installed.\n```\n\nExamine the newly built gem:\n\n```shell\n$ gem info jekyll_plugin_support\n\n*** LOCAL GEMS ***\n\njekyll_plugin_support (0.1.0)\n    Author: Mike Slinn\n    Homepage:\n    https://github.com/mslinn/jekyll_plugin_support\n    License: MIT\n    Installed at: /home/mslinn/.gems\n\n    Provides support for writing Jekyll plugins.\n```\n\n\n### Build and Install Locally\n\nTo build and install this gem onto your local machine, run:\n\n```shell\n$ bundle exec rake install\njekyll_all_collections 0.3.8 built to pkg/jekyll_all_collections-0.3.8.gem.\njekyll_all_collections (0.3.8) installed.\n```\n\nExamine the newly built gem:\n\n```shell\n$ gem info jekyll_all_collections\n\n*** LOCAL GEMS ***\n\njekyll_all_collections (0.3.8)\n    Author: Mike Slinn\n    Homepage:\n    https://www.mslinn.com/jekyll_plugins/jekyll_all_collections.html\n    License: MIT\n    Installed at (0.3.8): /home/mslinn/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0\n\n    Provides normalized collections and extra functionality for Jekyll websites.\n```\n\n\n### Build and Push to RubyGems\n\nTo release a new version:\n\n1. Update the version number in `version.rb`.\n2. Add an entry in `CHANGELOG.md` describing the changes since the last release.\n3. Commit all changes to git; if you don't the next step might fail with a confusing error message.\n4. Run the following:\n\n    ```shell\n    $ bundle exec rake release\n    ```\n\n    The above creates a git tag for the version, commits the created tag,\n    and pushes the new `.gem` file to [RubyGems.org](https://rubygems.org).\n\n\n### Pry Breakpoint On StandardError\n\nA `pry` breakpoint will be set in the `StandardError` handler if `pry_on_standard_error: true`\nis set in variable configuration section of `_config.yml`.\n\nFor example, if your plugin is called `blah`, enable the breakpoint with the following section:\n\n```yml\nblah:\n  pry_on_standard_error: true\n```\n\n\n## Debugging\n\nYou can control the verbosity of log output by adding the following to `_config.yml` in your Jekyll project:\n\n```yaml\nplugin_loggers:\n  AllCollectionsTag::AllCollectionsTag: warn\n```\n\n1. First set breakpoints in the Ruby code that interests you.\n\n2. You have several options for initiating a debug session:\n\n   1. Use the **Debug Demo** Visual Studio Code launch configuration.\n\n   2. Type the `demo/_bin/debug` command, without the `-r` options shown above.\n\n      ```console\n      ... lots of output as bundle update runs...\n      Bundle updated!\n\n      INFO PluginMetaLogger: Loaded AllCollectionsHooks v0.2.0 :site, :pre_render, :normal hook plugin.\n      INFO PluginMetaLogger: Loaded DraftFilter plugin.\n      INFO PluginMetaLogger: Loaded all_collections v0.2.0 tag plugin.\n      Configuration file: /mnt/_/work/jekyll/my_plugins/jekyll_all_collections/demo/_config.yml\n                Cleaner: Removing /mnt/_/work/jekyll/my_plugins/jekyll_all_collections/demo/_site...\n                Cleaner: Removing /mnt/_/work/jekyll/my_plugins/jekyll_all_collections/demo/.jekyll-metadata...\n                Cleaner: Removing /mnt/_/work/jekyll/my_plugins/jekyll_all_collections/demo/.jekyll-cache...\n                Cleaner: Nothing to do for .sass-cache.\n      Fast Debugger (ruby-debug-ide 0.7.3, debase 0.2.5.beta2, file filtering is supported) listens on 0.0.0.0:1234\n      ```\n\n   3. Run `bin/attach` and pass the directory name of a Jekyll website that has a suitable script called `_bin/debug`.\n      The `demo` subdirectory fits this description.\n\n      ```console\n      $ bin/attach demo\n      Successfully uninstalled jekyll_all_collections-0.1.2\n      jekyll_all_collections 0.1.2 built to pkg/jekyll_all_collections-0.1.2.gem.\n      jekyll_all_collections (0.1.2) installed.\n      Fast Debugger (ruby-debug-ide 0.7.3, debase 0.2.4.1, file filtering is supported) listens on 0.0.0.0:1234\n      ```\n\n\n3. Attach to the debugger process if required.\n  This git repo includes two [Visual Studio Code launch configurations](./.vscode/launch.json) for this purpose labeled\n  **Attach rdbg** and **Attach with ruby_lsp**.\n\n4. Point your web browser to http://localhost:4444\n\nIf a debugging session terminates abruptly and leaves ports tied up,\nrun the `demo/_bin/release_port` script.\n\n\n## Demonstration Plugins and Website\n\nA demo / test website is provided in the [`demo`](demo) directory.\nIt can be used to debug the plugin or to run freely.\n\n### Examining the Demo Plugins\n\nThe following example plugins use\n[Ruby’s squiggly heredoc operator](https://ruby-doc.org/core-2.5.0/doc/syntax/literals_rdoc.html#label-Here+Documents) (`\u003c\u003c~`).\nThe squiggly heredoc operator removes the outermost indentation.\nThis provides easy-to-read multiline text literals.\n\n**demo/_plugins/demo_tag.rb**:\n\n```ruby\nrequire 'jekyll_plugin_support'\n\n# Use the JekyllSupport module namespace so the self methods are automajically found\nmodule JekyllSupport\n  DemoInlineTagError = JekyllSupport.define_error\n\n  class DemoTag \u003c JekyllTag\n    VERSION = '0.1.2'.freeze\n    # JekyllSupport.redef_without_warning 'VERSION', '0.1.2'.freeze\n\n    def render_impl\n      @demo_tag_error = @helper.parameter_specified? 'raise_demo_tag_error'\n      @keyword1       = @helper.parameter_specified? 'keyword1'\n      @keyword2       = @helper.parameter_specified? 'keyword2'\n      @name1          = @helper.parameter_specified? 'name1'\n      @name2          = @helper.parameter_specified? 'name2'\n      @standard_error = @helper.parameter_specified? 'raise_standard_error'\n\n      if @tag_config\n        @die_on_demo_tag_error = @tag_config['die_on_demo_tag_error'] == true\n        @die_on_standard_error = @tag_config['die_on_standard_error'] == true\n      end\n\n      raise DemoInlineTagError, 'This DemoInlineTagError error is expected.' if @demo_tag_error\n      raise StandardError, 'This StandardError error is expected.' if @standard_error\n\n      # _infinity = 1 / 0 if @standard_error # Not required\n\n      output\n    rescue DemoInlineTagError =\u003e e # jekyll_plugin_support handles StandardError\n      @logger.error { e.logger_message }\n      exit! 1 if @die_on_demo_tag_error\n\n      e.html_message\n    end\n\n    private\n\n    def output\n      \u003c\u003c~END_OUTPUT\n        \u003cpre\u003e# jekyll_plugin_support becomes able to perform variable substitution after this variable is defined.\n        # The value could be updated at a later stage, but no need to add that complexity unless there is a use case.\n        @argument_string=\"#{@argument_string}\"\n\n        @helper.argv=\n          #{@helper.argv\u0026.join(\"\\n  \")}\n\n        # Liquid variable name/value pairs\n        @helper.params=\n          #{@helper.params\u0026.map { |k, v| \"#{k}=#{v}\" }\u0026.join(\"\\n  \")}\n\n        # The keys_values property serves no purpose any more, consider it deprecated\n        @helper.keys_values=\n          #{(@helper.keys_values\u0026.map { |k, v| \"#{k}=#{v}\" })\u0026.join(\"\\n  \")}\n\n        @layout='#{@layout}'\n        @page.keys='#{@page.keys}'\n\n        remaining_markup='#{@helper.remaining_markup}'\n\n        @keyword1='#{@keyword1}'\n        @keyword2='#{@keyword2}'\n        @name1='#{@name1}'\n        @name2='#{@name2}'\u003c/pre\u003e\n      END_OUTPUT\n    end\n\n    JekyllPluginHelper.register(self, 'demo_inline_tag')\n  end\nend\n```\n\n**demo/_plugins/demo_block.rb**:\n\n```ruby\nrequire 'cgi'\nrequire 'jekyll_plugin_support'\n\n# Use the JekyllSupport module namespace so the self methods are automajically found\nmodule JekyllSupport\n  DemoBlockError = JekyllSupport.define_error\n\n  class DemoBlock \u003c JekyllBlock\n    VERSION = '0.1.2'.freeze\n\n    def render_impl(text)\n      @demo_block_error = @helper.parameter_specified? 'raise_demo_block_error'\n      @keyword1         = @helper.parameter_specified? 'keyword1'\n      @keyword2         = @helper.parameter_specified? 'keyword2'\n      @name1            = @helper.parameter_specified? 'name1'\n      @name2            = @helper.parameter_specified? 'name2'\n      @standard_error   = @helper.parameter_specified? 'raise_standard_error'\n\n      if @tag_config\n        @die_on_demo_block_error = @tag_config['die_on_demo_block_error'] == true\n        @die_on_standard_error   = @tag_config['die_on_standard_error'] == true\n      end\n\n      raise DemoBlockTagError, 'This DemoBlockTagError error is expected.' if @demo_block_error\n      raise StandardError, 'This StandardError error is expected.' if @standard_error\n\n      # _infinity = 1 / 0 if @standard_error # Not required\n\n      output text\n    rescue DemoBlockTagError =\u003e e # jekyll_plugin_support handles StandardError\n      @logger.error { e.logger_message }\n      exit! 1 if @die_on_demo_block_error\n\n      e.html_message\n    end\n\n    private\n\n    def output(text)\n      \u003c\u003c~END_OUTPUT\n        \u003cpre\u003e@helper.tag_name=#{@helper.tag_name}\n\n        @mode=#{@mode}\n\n        # jekyll_plugin_support becomes able to perform variable substitution after this variable is defined.\n        # The value could be updated at a later stage, but no need to add that complexity unless there is a use case.\n        @argument_string=\"#{@argument_string}\"\n\n        @helper.argv=\n          #{@helper.argv\u0026.join(\"\\n  \")}\n\n        # Liquid variable name/value pairs\n        @helper.params=\n          #{@helper.params\u0026.map { |k, v| \"#{k}=#{v}\" }\u0026.join(\"\\n  \")}\n\n        # The keys_values property serves no purpose any more, consider it deprecated\n        @helper.keys_values=\n          #{(@helper.keys_values\u0026.map { |k, v| \"#{k}=#{v}\" })\u0026.join(\"\\n  \")}\n\n        @helper.remaining_markup='#{@helper.remaining_markup}'\n\n        @envs=#{@envs.keys.sort.join(', ')}\n\n        @config['url']='#{@config['url']}'\n\n        @site.collection_names=#{@site.collection_names\u0026.sort\u0026.join(', ')}\n\n        @page['description']=#{@page['description']}\n\n        @page['path']=#{@page['path']}\n\n        @keyword1=#{@keyword1}\n\n        @keyword2=#{@keyword2}\n\n        @name1=#{@name1}\n\n        @name2=#{@name2}\n\n        text=#{text}\u003c/pre\u003e\n      END_OUTPUT\n    end\n\n    JekyllPluginHelper.register(self, 'demo_block_tag')\n  end\nend\n```\n\nThe following is an example of no_arg_parsing optimization.\n\n```ruby\nrequire 'jekyll_plugin_support'\n\n# Use the JekyllSupport module namespace so the self methods are automajically found\nmodule JekyllSupport\n  class DemoTagNoArgs \u003c JekyllTagNoArgParsing\n    VERSION = '0.1.0'.freeze\n\n    def render_impl\n      \u003c\u003c~END_OUTPUT\n        The raw arguments passed to this \u003ccode\u003eDemoTagNoArgs\u003c/code\u003e instance are:\u003cbr\u003e\n        \u003ccode\u003e#{@argument_string}\u003c/code\u003e\n      END_OUTPUT\n    end\n\n    JekyllPluginHelper.register(self, 'demo_inline_tag_no_arg')\n  end\nend\n```\n\n### Run Freely\n\n 1. Run from the command line:\n\n    ```shell\n    $ demo/_bin/debug -r\n    ```\n\n 2. View the generated website,\n    which might be at [`http://localhost:4444`](http://localhost:4444),\n    depending on how you configured it.\n\n\n### Plugin Debugging\n\n 1. Set breakpoints in Visual Studio Code.\n\n 2. Run the **Debug Demo development** or **Debug Demo production** launch configuration.\n\nAlternatively, you can:\n\n 2. Initiate a debug session from the command line by running the `demo/_bin/debug` script:\n\n    ```shell\n    $ demo/_bin/debug\n    Fetching gem metadata from https://rubygems.org/..........\n    Resolving dependencies...\n    Fetching public_suffix 5.0.4\n    Fetching nokogiri 1.15.5 (x86_64-linux)\n    Installing public_suffix 5.0.4\n    Installing nokogiri 1.15.5 (x86_64-linux)\n    Bundle complete! 17 Gemfile dependencies, 96 gems now installed.\n    Use `bundle info [gemname]` to see where a bundled gem is installed.\n\n    INFO PluginMetaLogger: Loaded DraftFilter plugin.\n    INFO PluginMetaLogger: Loaded outline_js v1.2.1 plugin.\n    INFO PluginMetaLogger: Loaded outline v1.2.1 plugin.\n    Configuration file: /mnt/f/jekyll_plugin_support/demo/_config.yml\n              Cleaner: Removing /mnt/f/jekyll_plugin_support/demo/_site...\n              Cleaner: Removing /mnt/f/jekyll_plugin_support/demo/.jekyll-metadata...\n              Cleaner: Removing /mnt/f/jekyll_plugin_support/demo/.jekyll-cache...\n              Cleaner: Nothing to do for .sass-cache.\n    DEBUGGER: Debugger can attach via TCP/IP (127.0.0.1:37177)\n    DEBUGGER: wait for debugger connection...\n    ```\n\n 3. Once the `DEBUGGER: wait for debugger connection...` message appears,\n    run the Visual Studio Code launch configuration called **Attach with rdbg**.\n\n 4. View the generated website,\n    which might be at [`http://localhost:4444`](http://localhost:4444),\n    depending on how you configured it.\n\n\n### Build and Push to RubyGems\n\nTo release a new version:\n\n  1. Update the version number in `version.rb`.\n  2. Add an entry to `CHANGELOG.md` describing the changes since the previous version.\n  3. Commit all changes to git;\n     if you don't the next step might fail with an unexplainable error message.\n  4. Run the following:\n\n     ```shell\n     $ bundle exec rake release\n     ```\n\n     The above creates a git tag for the version, commits the created tag,\n     and pushes the new `.gem` file to [RubyGems.org](https://rubygems.org).\n\n\n## Contributing\n\n1. Fork the project\n2. Create a descriptively named feature branch\n3. Add your feature\n4. Submit a pull request\n\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmslinn%2Fjekyll_plugin_support","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmslinn%2Fjekyll_plugin_support","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmslinn%2Fjekyll_plugin_support/lists"}