Improve documentation on developing generators (#8527)

Merge pull request 8527
This commit is contained in:
Ashwin Maroli 2021-01-01 21:59:26 +05:30 committed by GitHub
parent fc76919c69
commit 319cc95254
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 82 additions and 42 deletions

View File

@ -3,36 +3,34 @@ title: Generators
permalink: /docs/plugins/generators/
---
You can create a generator when you need Jekyll to create additional content
based on your own rules.
You can create a generator when you need Jekyll to create additional content based on your own rules.
A generator is a subclass of `Jekyll::Generator` that defines a `generate`
method, which receives an instance of
[`Jekyll::Site`]({{ site.repository }}/blob/master/lib/jekyll/site.rb). The
return value of `generate` is ignored.
A generator is a subclass of `Jekyll::Generator` that defines a `generate` method, which receives an instance of
[`Jekyll::Site`]({{ site.repository }}/blob/master/lib/jekyll/site.rb). The return value of `generate` is ignored.
Generators run after Jekyll has made an inventory of the existing content, and
before the site is generated. Pages with front matter are stored as
instances of
[`Jekyll::Page`]({{ site.repository }}/blob/master/lib/jekyll/page.rb)
and are available via `site.pages`. Static files become instances of
Generators run after Jekyll has made an inventory of the existing content, and before the site is generated. Pages with
front matter are stored as instances of [`Jekyll::Page`]({{ site.repository }}/blob/master/lib/jekyll/page.rb) and are
available via `site.pages`. Static files become instances of
[`Jekyll::StaticFile`]({{ site.repository }}/blob/master/lib/jekyll/static_file.rb)
and are available via `site.static_files`. See
[the Variables documentation page](/docs/variables/) and
[`Jekyll::Site`]({{ site.repository }}/blob/master/lib/jekyll/site.rb)
for details.
and are available via `site.static_files`. See [the Variables documentation page](/docs/variables/) and
[`Jekyll::Site`]({{ site.repository }}/blob/master/lib/jekyll/site.rb) for details.
For instance, a generator can inject values computed at build time for template
variables. In the following example, the template `reading.html` has two
variables `ongoing` and `done` that are filled in the generator:
In the following example, the generator will inject values computed at build time for template variables. The template
named `reading.html` has two undefined variables `ongoing` and `done` that will be defined or assigned a value when
the generator runs:
```ruby
module Reading
class Generator < Jekyll::Generator
def generate(site)
ongoing, done = Book.all.partition(&:ongoing?)
book_data = site.data['books']
ongoing = book_data.select { |book| book['status'] == 'ongoing' }
done = book_data.select { |book| book['status'] == 'finished' }
reading = site.pages.detect {|page| page.name == 'reading.html'}
# get template
reading = site.pages.find { |page| page.name == 'reading.html'}
# inject data into template
reading.data['ongoing'] = ongoing
reading.data['done'] = done
end
@ -40,42 +38,80 @@ module Reading
end
```
The following example is a more complex generator that generates new pages. In this example, the generator will create a series of files under the `categories` directory for each category, listing the posts in each category using the `category_index.html` layout.
The following example is a more complex generator that generates new pages.
In this example, the aim of the generator is to create a page for each category registered in the `site`. The pages are
created at runtime, so their contents, front matter and other attributes need to be designed by the plugin itself.
* The pages are intended to render a list of all documents under a given category. So the basename of the rendered file
would be better as `index.html`.
* Having the ability to configure the pages via [front matter defaults](/docs/configuration/front-matter-defaults/)
would be awesome! So assigning a particular `type` to these pages would be beneficial.
```ruby
module Jekyll
class CategoryPageGenerator < Generator
module SamplePlugin
class CategoryPageGenerator < Jekyll::Generator
safe true
def generate(site)
if site.layouts.key? 'category_index'
dir = site.config['category_dir'] || 'categories'
site.categories.each_key do |category|
site.pages << CategoryPage.new(site, site.source, File.join(dir, category), category)
end
site.categories.each do |category, posts|
site.pages << CategoryPage.new(site, category, posts)
end
end
end
# A Page subclass used in the `CategoryPageGenerator`
class CategoryPage < Page
def initialize(site, base, dir, category)
@site = site
@base = base
@dir = dir
@name = 'index.html'
# Subclass of `Jekyll::Page` with custom method definitions.
class CategoryPage < Jekyll::Page
def initialize(site, category, posts)
@site = site # the current site instance.
@base = site.source # path to the source directory.
@dir = category # the directory the page will reside in.
self.process(@name)
self.read_yaml(File.join(base, '_layouts'), 'category_index.html')
self.data['category'] = category
# All pages have the same filename, so define attributes straight away.
@basename = 'index' # filename without the extension.
@ext = '.html' # the extension.
@name = 'index.html' # basically @basename + @ext.
category_title_prefix = site.config['category_title_prefix'] || 'Category: '
self.data['title'] = "#{category_title_prefix}#{category}"
# Initialize data hash with a key pointing to all posts under current category.
# This allows accessing the list in a template via `page.linked_docs`.
@data = {
'linked_docs' => posts
}
# Look up front matter defaults scoped to type `categories`, if given key
# doesn't exist in the `data` hash.
data.default_proc = proc do |_, key|
site.frontmatter_defaults.find(relative_path, :categories, key)
end
end
# Placeholders that are used in constructing page URL.
def url_placeholders
{
:category => @dir,
:basename => basename,
:output_ext => output_ext,
}
end
end
end
```
The generated pages can now be set up to use a particular layout or output at a particular path in the destination
directory all via the config file using front matter defaults. For example:
```yaml
# _config.yml
defaults:
- scope:
type: categories # select all category pages
values:
layout: category_page
permalink: categories/:category/
```
## Technical Aspects
Generators need to implement only one method:
<div class="mobile-side-scroller">
@ -99,6 +135,10 @@ Generators need to implement only one method:
</table>
</div>
If your generator is contained within a single file, it can be named whatever you want but it should have an `.rb` extension. If your generator is split across multiple files, it should be packaged as a Rubygem to be published at https://rubygems.org/. In this case, the name of the gem depends on the availability of the name at that site because no two gems can have the same name.
If your generator is contained within a single file, it can be named whatever you want but it should have an `.rb`
extension. If your generator is split across multiple files, it should be packaged as a Rubygem to be published at
https://rubygems.org/. In this case, the name of the gem depends on the availability of the name at that site because
no two gems can have the same name.
By default, Jekyll looks for generators in the `_plugins` directory. However, you can change the default directory by assigning the desired name to the key `plugins_dir` in the config file.
By default, Jekyll looks for generators in the `_plugins` directory. However, you can change the default directory by
assigning the desired name to the key `plugins_dir` in the config file.