# `jekyll_plugin_support` [![Gem Version](https://badge.fury.io/rb/jekyll_plugin_support.svg)](https://badge.fury.io/rb/jekyll_plugin_support) `Jekyll_plugin_support` is a Ruby gem that facilitates writing and testing Jekyll plugins. At present, only Jekyll tags and blocks are supported. ## Installation `Jekyll_plugin_support` can be used to create simple Jekyll plugins in the `_plugins/` directory, or gem-based Jekyll plugins. ### Simple Plugins For jekyll plugins defined in the `_plugins/` directory, add this line to your Jekyll plugin's `Gemfile`: ```ruby group :jekyll_plugins do gem 'jekyll_plugin_support' end ``` And then execute: ```shell $ bundle ``` ### Gem-Based Plugins Add this line to your Jekyll plugin's `.gemspec`: ```ruby spec.add_dependency 'jekyll_plugin_support' ``` And then execute: ```shell $ bundle ``` ## General Usage `JekyllSupport::JekyllBlock` and `JekyllSupport::JekyllTag` provide support for Jekyll tag blocks and Jekyll tags, respectively. They are very similar in construction and usage. Instead of subclassing your Jekyll block tag class from `Liquid::Block`, subclass from `JekyllSupport::JekyllBlock` instead. Similarly, instead of subclassing your Jekyll tag class from `Liquid::Tag`, subclass from `JekyllSupport::JekyllTag` instead. Both `JekyllSupport` classes instantiate new instances of [`PluginMetaLogger`](https://github.com/mslinn/jekyll_plugin_logger) (called `@logger`) and [`JekyllPluginHelper`](lib/jekyll_plugin_support_helper.rb) (called `@helper`). `JekyllPluginHelper` defines a generic `initialize` method, and your tag or block tag class should not override it. Also, your tag or block tag class should not define a method called `render`, because `JekyllBlock.initialize` defines one, which creates variables called [`@page`](https://jekyllrb.com/docs/variables/#page-variables) and [`@site`](https://jekyllrb.com/docs/variables/#site-variables). Instead, define a method called `render_impl`. For tags, `render_impl` does not accept any parameters. For block tags, a single parameter is required, which contains any text enclosed within your block. Your implementation of `render_impl` can access `@page` and `@site`, and can parse parameters passed to the tag / block tag, [as described here](https://mslinn.com/jekyll/10100-jekyll-plugin-background.html#params): ### For a tag: ```ruby require 'jekyll_plugin_support' module Jekyll class MyTag < JekyllSupport::JekyllTag VERSION = '0.1.0'.freeze def render_impl my_output = "
blah blah
" <<~END_OUTPUT #{my_output} END_OUTPUT end JekyllPluginHelper.register(self, 'demo_tag') end end ``` ### For a tag block: ```ruby require 'jekyll_plugin_support' module Jekyll class MyBlock < JekyllSupport::JekyllBlock VERSION = '0.1.0'.freeze def render_impl(content) @helper.gem_file __FILE__ # Enables attribution my_output = "blah blah
" <<~END_OUTPUT #{my_output} #{@helper.attribute if @helper.attribution} END_OUTPUT end JekyllPluginHelper.register(self, 'demo_block') end end ``` Note that each tag or tag block must define a constant called `VERSION`. If your plugin is packaged as a gem, then you might need to include `version.rb` into the plugin class. For example, if `lib/my_plugin/version.rb` looks like this: ```ruby module MyPluginVersion VERSION = '0.5.1'.freeze end ``` Then your plugin can incorporate the `VERSION` constant into your plugin like this: ```ruby require 'jekyll_plugin_support' require_relative 'my_block/version' module Jekyll class MyBlock < JekyllSupport::JekyllBlock include MyPluginVersion def render_impl(text) @helper.gem_file __FILE__ # Enables attribution my_output = "blah blah
" <<~END_OUTPUT #{my_output} #{@helper.attribute if @helper.attribution} END_OUTPUT end JekyllPluginHelper.register(self, 'demo_tag') end end ``` ### 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`](https://ruby-doc.org/stdlib-2.5.1/libdoc/shellwords/rdoc/Shellwords.html) and [`key_value_parser`](https://www.rubydoc.info/gems/key-value-parser). All your code has to do is to 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_tag.rb`](demo/_plugins/demo_tag.rb) and [`demo/_plugins/demo_block.rb`](demo/_plugins/demo_block.rb) contain the following: ```ruby @keyword1 = @helper.parameter_specified? 'keyword1' @keyword2 = @helper.parameter_specified? 'keyword2' @name1 = @helper.parameter_specified? 'name1' @name2 = @helper.parameter_specified? 'name2' ``` In [`demo/index.html`](demo/index.html), the following invoked the tag: ```html {% demo_tag keyword1 name1='value1' unreferenced_key unreferenced_name="unreferenced_value" %} ``` The `demo/_plugins/demo_tag.rb` plugin uses `@helper.parameter_specified?` provided by `jekyll_support_plugin` to parse the string passed to the tag, which is `keyword1 name1='value1' unreferenced_key unreferenced_name="unreferenced_value"`. * Because `keyword1` was referenced by `@helper.parameter_specified?` above, that keyword option is removed from the argument string. * Because the `name1` key/value parameter was referenced by `@helper.parameter_specified?` above, that name/value pair is removed from the argument string. * The remainder of the argument string is now `unreferenced_key unreferenced_name="unreferenced_value"`. Name/value parameters can be quoted; if the value consists of only one token then it does not need to be quoted. The following name/value parameters all have the same result: * `pay_tuesday="true"` * `pay_tuesday='true'` * `pay_tuesday=true` * `pay_tuesday` The following also have the same result, however note that because the value has more than one token, quotes must be used: * `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. ### `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`. ## Subclass Attribution `JekyllTag` and `JekyllBlock` subclasses of `jekyll_plugin_support` can utilize the `attribution` option IFF they are published as a gem. `JekyllTagNoArgParsing` and `JekyllBlockNoArgParsing` subclasses cannot. * 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 cause subclasses to replace their usual output with HTML that looks like: ```html ``` The `id` attribute is in the sample HTML above is randomized so more than one attribution can appear on a page. ### Usage Typical usage for the `attribution` tag is: ```html {% my_tag attribution %} ``` 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: ```ruby "Generated by the #{name} #{version} Jekyll plugin, written by #{author} #{date}." ``` Because `jekyll_plugin_suppprt` subclasses are `gem`s, their `gemfile`s 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 properties can be output using any of the above properties: ```html {% my_tag attribution="Generated by the #{name} #{version} Jekyll plugin, written by #{author} #{date}" %} ``` ### 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](https://github.com/mslinn/jekyll_outline/blob/v1.1.1/lib/outline_tag.rb#L32-L46) generates output: ```html <<~HEREDOC