Jekyll生成器(Generators)

当你需要 Jekyll 根据你自己的规则生成额外内容时,可以创建一个生成器(generator)。

生成器是 Jekyll::Generator 的子类,它定义一个 generate 方法,该方法接收一个 Jekyll::Site 实例作为参数。generate 方法的返回值会被忽略。

生成器会在 Jekyll 完成现有内容的整理之后、生成网站之前运行。带有 front matter 的页面会被存储为 Jekyll::Page 的实例,并可以通过 site.pages 访问。静态文件会变成 Jekyll::StaticFile 的实例,并可以通过 site.static_files 访问。详情请参阅 Variables 文档页面Jekyll::Site

在下面的示例中,生成器会在构建时为模板变量注入计算好的值。名为 reading.html 的模板有两个未定义变量 ongoingdone,它们会在生成器运行时被定义或赋值:

module Reading
  class Generator < Jekyll::Generator
    def generate(site)
      book_data = site.data['books']
      ongoing = book_data.select { |book| book['status'] == 'ongoing' }
      done = book_data.select { |book| book['status'] == 'finished' }

      # 获取模板
      reading = site.pages.find { |page| page.name == 'reading.html'}

      # 将数据注入模板
      reading.data['ongoing'] = ongoing
      reading.data['done'] = done
    end
  end
end

下面是一个更复杂的生成器示例,它用于生成新的页面。

在这个例子中,生成器的目标是为 site 中注册的每个分类(category)创建一个页面。这些页面是在运行时创建的,因此它们的内容、front matter 以及其他属性都需要由插件自行设计。

module SamplePlugin
  class CategoryPageGenerator < Jekyll::Generator
    safe true

    def generate(site)
      site.categories.each do |category, posts|
        site.pages << CategoryPage.new(site, category, posts)
      end
    end
  end

  # `Jekyll::Page` 的子类,包含自定义方法定义。
  class CategoryPage < Jekyll::Page
    def initialize(site, category, posts)
      @site = site             # 当前 site 实例。
      @base = site.source      # 源目录路径。
      @dir  = category         # 页面将所在的目录。

      # 所有页面使用相同的文件名,因此直接定义属性。
      @basename = 'index'      # 不包含扩展名的文件名。
      @ext      = '.html'      # 扩展名。
      @name     = 'index.html' # 基本上等于 @basename + @ext。

      # 初始化 data 哈希,其中包含指向当前分类下所有文章的键。
      # 这样可以在模板中通过 `page.linked_docs` 访问该列表。
      @data = {
        'linked_docs' => posts
      }

      # 如果 `data` 哈希中不存在指定 key,则查找作用域为 `categories` 的 front matter 默认值。
      data.default_proc = proc do |_, key|
        site.frontmatter_defaults.find(relative_path, :categories, key)
      end
    end

    # 用于构建页面 URL 的占位符。
    def url_placeholders
      {
        :path       => @dir,
        :category   => @dir,
        :basename   => basename,
        :output_ext => output_ext,
      }
    end
  end
end

现在生成的页面可以通过配置文件中的 front matter defaults 来设置使用特定布局或在目标目录的特定路径下输出。例如:

# _config.yml

defaults:
  - scope:
      type: categories  # 选择所有分类页面
    values:
      layout: category_page
      permalink: categories/:category/

技术方面

生成器只需要实现一个方法:

方法 描述

generate

以副作用的方式生成内容。

如果你的生成器只包含在一个文件中,可以随意命名,但必须使用 .rb 扩展名。如果你的生成器分布在多个文件中,则应将其打包为 Rubygem 并发布到 https://rubygems.org/。在这种情况下,gem 的名称取决于该网站上名称是否可用,因为不能有两个 gem 使用相同名称。

默认情况下,Jekyll 会在 _plugins 目录中查找生成器。不过,你可以在配置文件中将 plugins_dir 键设置为指定名称,从而更改默认目录。