Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

coffeebrew_jekyll_paginate

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

coffeebrew_jekyll_paginate

  • 0.1.0
  • Rubygems
  • Socket score

Version published
Maintainers
1
Created
Source

Jekyll Paginate Plugin

A Jekyll plugin to generate site pagination for collections.

Continuous Integration Gem Version

Installation

Add this line to your site's Gemfile:

gem 'coffeebrew_jekyll_paginate'

And then add this line to your site's _config.yml:

plugins:
  - coffeebrew_jekyll_paginate

By default, the plugin doesn't generate any pagination. You need to specify which collections to be paginated. For example,

---
coffeebrew_jekyll_paginate:
  collections:
    books:

Assuming layouts have been setup (see more in Layouts), then, the plugin will generate paginated collections using the default config below:

individual_page_pagination: false
frontmatter_defaults_key: "paginated_%{collection_type}"
first_page_as_root:
  enabled: false
permalink: /:collection_type/:page_num_one_index
index_page: "index"
per_page: 5
sort_field: "date"
sort_reverse: true
page_num_label: "%{page_num_one_index}"

An example of the generated directory structure is as such:

_site/
├── books
│   ├── 1
│   │   ├── index.html
│   └── 2
│       └── index.html
└── index.html

Configuration

You can configure for as many collections as needed, and configure a site-wide pagination config, or configure for each collection to use its own configuration. If there is no configuration for the site default, or the collection, the plugin will use the default values as previously mentioned.

Site-wide defaults

For example, you can configure site-wide defaults that override the plugin's defaults, which will be used for all the collections enabled.

coffeebrew_jekyll_paginate:
  defaults:
    individual_page_pagination: true
    first_page_as_root:
      enabled: true
      permalink: /
      index_page: /:collection_type
    per_page: 3
    page_num_label: "Page %{page_num_one_index}"
  collections:
    books:
    posts:

If you do override the configuration, the plugin will perform a simple validation on your overrides according to these rules:

Frontmatter defaults key config

This tells the plugin what is the key used for setting defaults. For example, the plugin will use paginated_posts for posts type by default, and this will allow Jekyll to lookup defaults for type: "paginated_posts" in _config.yml.

defaults:
  - scope:
      path: ""
      type: "paginated_posts"
    values:
      layout: "posts_index"

Individual page pagination config

If set to true, this tells the plugin to generate previous/next pagination for individual collection pages. This is useful if you want to have individual page-level pagination to navigate to the previous or next page. For example:

---
layout: default
---
<h1>Posts</h1>
{% raw %}
{{ content }}

<div class="pagination">
  <ul class="pager">
    <li class="previous">
      <a href="{{ page.paginator.previous_page_path }}">< {{ page.paginator.previous_page.title }}</a>
    </li>
    <li class="next">
      <a href="{{ page.paginator.next_page_path }}">{{ page.paginator.next_page.title }} ></a>
    </li>
  </ul>
</div>
{% endraw %}

First page as root config

This tells the plugin to generate the first page outside of the collection directory in the paginated set. For example, if there are 4 pages in the paginated books collection, then the first page will be rendered in /books.html, and the other pages will be rendered as /books/2/index.html, /books/3/index.html and /books/4/index.html.

This will be useful if you want to have the first page as part of your root pages and include it in the navigation.

KeyAllowed Value(s)DefaultRemark
enabledBooleanfalseIf set to true, then the plugin will render the first page in a separate path as defined by the permalink and index_page below.
permalinkStringnilThe directory in which to generate the first page. Normally this will be / if you want it to be a root-level page.
index_pageStringnilThe filename of the first page. Normally this will be the name corresponding to the collection.

If enabled is false, both permalink and index_page will be ignored, and the plugin will generate the first page the same way as the remaining pages.

Pagination config

This tells the plugin how to generate the pagination pages.

KeyAllowed Value(s)DefaultRemark
permalinkString/:collection_type/:page_num_one_indexThe directory in which to generate the page.
index_pageStringindexThe filename of the page.
per_pageInteger5The number of collection items in each page.
sort_fieldStringdateThe field to be used to sort the collection for deterministic pagination.
sort_reverseBooleantrueThe collection will be sorted in reverse if set to true.
page_num_labelString%{page_num_one_index}The format string for the page number label.

A few placeholders are available to be used in the permalink, index_page and page_num_label:

FieldDescription
collection_typeThis is the collection type current page, eg. posts.
page_num_zero_indexThis will be the 0-index page number of the current page.
page_num_one_indexThis will be the 1-index page number of the current page.

Validation

If the config overrides have invalid structure, keys or values, the plugin will raise a Jekyll::Errors::InvalidConfigurationError during build.

Layouts

The plugin does not provide a default layout. You will need to create your own layout in _layouts and configure the defaults in _config.yml, for example, if you want to paginate the books collection, then you need to configure the layout for the collection:

---
defaults:
  - scope:
      type: "paginated_books"
    values:
      layout: "books_pagination"
  - scope:
      type: "books"
    values:
      layout: "book"
      permalink: /books/:name:output_ext

As mentioned earlier, you need to set the scope.type here to match the frontmatters_defaults_key config of the plugin. Note that there are 2 layouts configured here, books_pagination is used for the paginated collection page, and book is used for the individual book page.

In addition to Jekyll's default page data, you can also use the additional page data and page's paginator data generated by the plugin in the layout:

Paginated collection page

Use page to access the fields below.

FieldDescription
titleCurrent page's title.
collectionCurrent page's collection.
collection_typeCurrent page's collection type.
page_num_zero_indexCurrent page's 0-index page number.
page_num_one_indexCurrent page's 1-index page number.
page_num_labelCurrent page's page number label.
full_urlCurrent page full url. Can be used to match the current window url for highlighting purpose.

Paginated collection paginator

Use page.paginator to acceess the fields below.

FieldDescription
total_pagesTotal number of pages of the paginated collection.
pagesAll the pages in the collection.
page_num_zero_indexCurrent page's 0-index page number.
page_num_one_indexCurrent page's 1-index page number.
page_num_labelCurrent page's page number label.
collectionCurrent page's collection.
collection_typeCurrent page's collection type.
current_pageCurrent page.
current_page_pathCurrent page full url. Can be used to match the current window url for highlighting purpose.
previous_pagePrevious page.
previous_page_pathPrevious page full url. Can be used to generate navigation link.
next_pageNext page.
next_page_pathNext page full url. Can be used to generate navigation link.

An example of the paginated collection page layout books_pagination:

---
layout: default
---
<h1>Books</h1>
<div class="collection">
{% raw %}
{% for book in page.paginator.collection %}
  <div class="item">
    <div class="title">
      <span>{{ book.title }}</span>
    </div>
    <p>{{ book.excerpt }}</p>
  </div>
{% endfor %}
</div>

{% if page.paginator.total_pages > 1 %}
<div class="pagination">
  <ul class="pager">
    {% if page.paginator.previous_page %}
    <li class="previous">
      <a href="{{ page.paginator.previous_page_path }}"><i class="fa-solid fa-arrow-left"></i> Newer {{ page.paginator.collection_type | capitalize }}</a>
    </li>
    {% endif %}
    {% if page.paginator.next_page %}
    <li class="next">
      <a href="{{ page.paginator.next_page_path }}">Older {{ page.paginator.collection_type | capitalize }} <i class="fa-solid fa-arrow-right"></i></a>
    </li>
    {% endif %}
  </ul>
</div>
{% endif %}
{% endraw %}

The resulting index pages using the example configuration and layout are as such:

Root page

Generated at: _site/books.html.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Books</title>
  </head>
  <body>
    <div class="container">
      <h1>Books</h1>
      <div class="collection">
        <div class="item">
          <div class="title">
            <span>Harry Potter 7</span>
          </div>
          <p>This is the final book in the Harry Potter series.</p>
        </div>

        <div class="item">
          <div class="title">
            <span>Harry Potter 6</span>
          </div>
          <p>This is the sixth book in the Harry Potter series.</p>
        </div>

        <div class="item">
          <div class="title">
            <span>Harry Potter 5</span>
          </div>
          <p>This is the fifth book in the Harry Potter series.</p>
        </div>
      </div>

      <div class="pagination">
        <ul class="pager">
          <li class="next">
            <a href="/books/2/index.html">Older Books <i class="fa-solid fa-arrow-right"></i></a>
          </li>
        </ul>
      </div>
    </div>
  </body>
</html>

Next page

Generated at: _site/books/2/index.html.

Note: Header elements omitted for clarity.

<div class="container">
  <h1>Books</h1>
  <div class="collection">
    <div class="item">
      <div class="title">
        <span>Harry Potter 4</span>
      </div>
      <p>This is the fourth book in the Harry Potter series.</p>
    </div>

    <div class="item">
      <div class="title">
        <span>Harry Potter 3</span>
      </div>
      <p>This is the third book in the Harry Potter series.</p>
    </div>

    <div class="item">
      <div class="title">
        <span>Harry Potter 2</span>
      </div>
      <p>This is the second book in the Harry Potter series.</p>
    </div>
  </div>

  <div class="pagination">
    <ul class="pager">
      <li class="previous">
        <a href="/books.html"><i class="fa-solid fa-arrow-left"></i> Newer Books</a>
      </li>
      <li class="next">
        <a href="/books/3/index.html">Older Books <i class="fa-solid fa-arrow-right"></i></a>
      </li>
    </ul>
  </div>
</div>

Last page

Generated at: _site/books/3/index.html.

Note: Header elements omitted for clarity.

<div class="container">
  <h1>Books</h1>
  <div class="collection">
    <div class="item">
      <div class="title">
        <span>Harry Potter 1</span>
      </div>
      <p>This is the first book in the Harry Potter series.</p>
    </div>
  </div>

  <div class="pagination">
    <ul class="pager">
      <li class="previous">
        <a href="/books/2/index.html"><i class="fa-solid fa-arrow-left"></i> Newer Books</a>
      </li>
    </ul>
  </div>
</div>

Individual page paginator

Use page.paginator to access the fields below.

FieldDescription
collection_typeCurrent page's collection type.
current_pageCurrent page.
current_page_pathCurrent page full url. Can be used to match the current window url for highlighting purpose.
previous_pagePrevious page.
previous_page_pathPrevious page full url. Can be used to generate navigation link.
next_pageNext page.
next_page_pathNext page full url. Can be used to generate navigation link.

An example of the individual page layout book:

---
layout: default
---
<h1>{{ page.title }}</h1>
{% raw %}
<div class="title">
  <span>{{ page.title }}</span>
</div>
<p>{{ content }}</p>

{% if page.paginator %}
<div class="pagination">
  <ul class="pager">
    {% if page.paginator.previous_page %}
    <li class="previous">
      <a href="{{ page.paginator.previous_page_path }}"><i class="fa-solid fa-arrow-left"></i> Previous Book</a>
    </li>
    {% endif %}
    {% if page.paginator.next_page %}
    <li class="next">
      <a href="{{ page.paginator.next_page_path }}">Next Book <i class="fa-solid fa-arrow-right"></i></a>
    </li>
    {% endif %}
  </ul>
</div>
{% endif %}
{% endraw %}

The resulting page using the example configuration and layout are as such:

Individual page

Generated at: _site/books/book-1.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Books</title>
  </head>
  <body>
    <div class="container">
      <h1>Book 1</h1>
      <div class="title">
        <span>Book 1</span>
      </div>
      <p>This is book 1.</p>

      <div class="pagination">
        <ul class="pager">
          <li class="next">
            <a href="/books/book-2.html">Next Book <i class="fa-solid fa-arrow-right"></i></a>
          </li>
        </ul>
      </div>
    </div>
  </body>
</html>

Contributing

Contribution to the gem is very much welcome!

  1. Fork it (https://github.com/coffeebrewapps/coffeebrew_jekyll_paginate/fork).
  2. Create your feature branch (git checkout -b my-new-feature).
  3. Make sure you have setup the repo correctly so that you can run RSpec and Rubocop on your changes. Read more under the Development section.
  4. Commit your changes (git commit -am 'Add some feature').
  5. If you have added something that is worth mentioning in the README, please also update the README.md accordingly and commit the changes.
  6. Push to the branch (git push origin my-new-feature).
  7. Create a new Pull Request.

The repo owner will try to respond to a new PR as soon as possible.

Development

We want to provide a robust gem as much as possible for the users, so writing test cases will be required for any new feature.

If you are contributing to the gem, please make sure you have setup your development environment correctly so that RSpec and Rubocop can run properly.

  1. After forking the repo, go into the repo directory (cd coffeebrew_jekyll_paginate/).
  2. Make sure you have the correct Ruby version installed. This gem requires Ruby >= 2.7.0.
  3. Once you have the correct Ruby version, install the development dependencies (bundle install).
  4. To test that you have everything installed correctly, run the test cases (bundle exec rspec).
  5. You should see all test cases pass successfully.

Source directory structure

All the gem logic lives in the /lib directory:

lib
├── coffeebrew_jekyll_paginate
│   ├── config.yml
│   ├── generator.rb
│   ├── individual_paginator.rb
│   ├── page.rb
│   ├── page_drop.rb
│   ├── paginator.rb
│   ├── validator.rb
│   └── version.rb
└── coffeebrew_jekyll_paginate.rb

The files that are currently in the repo:

FileDescription
lib/coffeebrew_jekyll_paginate/config.ymlThis contains the default configuration for the plugin to generate pagination.
lib/coffeebrew_jekyll_paginate/generator.rbThis is the generator that reads the configuration and generate pagination.
lib/coffeebrew_jekyll_paginate/individual_paginator.rbThis is the abstract model containing the pagination methods for individual pages.
lib/coffeebrew_jekyll_paginate/page.rbThis is the abstract model of the paginated collection pages.
lib/coffeebrew_jekyll_paginate/page_drop.rbThis is the page drop used by the paginator class so its methods can be used in Liquid template.
lib/coffeebrew_jekyll_paginate/paginator.rbThis is the abstract model containing the pagination methods for paginated collection pages.
lib/coffeebrew_jekyll_paginate/validator.rbThis validates the configuration.
lib/coffeebrew_jekyll_paginate/version.rbThis contains the version number of the gem.
lib/coffeebrew_jekyll_paginate.rbThis is the entry point of the gem, and it loads the dependencies.

Test cases directory structure

All the test cases and fixtures live in the /spec directory:

Note: Some files have been omitted for clarity.

spec
├── coffeebrew_jekyll_paginate_spec.rb
├── dest
├── fixtures
│   ├── _books
│   │   ├── 1997-06-26-harry-potter-1.md
│   │   ├── 1998-07-02-harry-potter-2.md
│   │   ├── 1999-07-08-harry-potter-3.md
│   │   ├── 2000-07-08-harry-potter-4.md
│   │   ├── 2003-06-21-harry-potter-5.md
│   │   ├── 2005-07-16-harry-potter-6.md
│   │   └── 2007-07-21-harry-potter-7.md
│   ├── _layouts
│   │   ├── book.html
│   │   ├── default.html
│   │   ├── paginated_books.html
│   │   ├── paginated_posts.html
│   │   └── post.html
│   ├── _posts
│   │   ├── 2021-03-12-test-post-1.md
│   │   ├── 2021-03-28-test-post-2.md
│   │   ├── 2021-05-03-test-post-3.md
│   │   ├── 2021-05-03-test-post-4.md
│   │   ├── 2022-01-27-test-post-5.md
│   │   ├── 2022-03-12-test-post-6.md
│   │   ├── 2022-11-23-test-post-7.md
│   │   └── 2023-02-21-test-post-8.md
│   └── _config.yml
├── scenarios
│   ├── default
│   │   ├── _site
│   │   │   ├── books
│   │   │   │   ├── 1
│   │   │   │   │   └── index.html
│   │   │   │   ├── 2
│   │   │   │   │   └── index.html
│   │   │   │   ├── 1997-06-26-harry-potter-1.html
│   │   │   │   ├── 1998-07-02-harry-potter-2.html
│   │   │   │   ├── 1999-07-08-harry-potter-3.html
│   │   │   │   ├── 2000-07-08-harry-potter-4.html
│   │   │   │   ├── 2003-06-21-harry-potter-5.html
│   │   │   │   ├── 2005-07-16-harry-potter-6.html
│   │   │   │   └── 2007-07-21-harry-potter-7.html
│   │   │   └── posts
│   │   │       ├── 1
│   │   │       │   └── index.html
│   │   │       ├── 2
│   │   │       │   └── index.html
│   │   │       ├── 2021-03-12-test-post-1.html
│   │   │       ├── 2021-03-28-test-post-2.html
│   │   │       ├── 2021-05-03-test-post-3.html
│   │   │       ├── 2021-05-03-test-post-4.html
│   │   │       ├── 2022-01-27-test-post-5.html
│   │   │       ├── 2022-03-12-test-post-6.html
│   │   │       ├── 2022-11-23-test-post-7.html
│   │   │       └── 2023-02-21-test-post-8.html
│   │   └── context.rb
│   └── invalid_config_keys
│       └── context.rb
└── spec_helper.rb

The files that are currently in the repo:

FileDescription
spec/coffeebrew_jekyll_paginate_spec.rbThis is the main RSpec file to be executed. It contains all the contexts of various scenarios.
spec/dest/This directory is where generated files are located. It will be deleted immediately after each context is executed.
spec/fixtures/This directory follows the Jekyll site source structure and contains the minimal files required to generate the paginated pages.
spec/fixtures/_booksThis directory contains the test books, you can add more to it to test your new feature.
spec/fixtures/_postsThis directory contains the test posts, you can add more to it to test your new feature.
spec/scenarios/This directory contains the expected files of various test scenarios.
spec/scenarios/<scenario>/This is the scenario name.
spec/scenarios/<scenario>/_site/This directory contains the expected paginated pages.
spec/scenarios/<scenario>/context.rbThis is the file that sets up the context for the test case.
spec/spec_helper.rbThis contains RSpec configuration and certain convenience methods for the main RSpec file.

Writing test cases

There is a certain convention to follow when writing new test scenarios. The recommendation is to use the rake tasks provided in the gem to generate the scenario files.

For success scenarios, run:

bundle exec rake coffeebrew:jekyll:paginate:test:create_success[test_scenario]

This will generate a placeholder file and directory:

spec
├── coffeebrew_jekyll_paginate_spec.rb
├── scenarios
│   └── test_scenario
│       ├── _site
│       └── context.rb
└── spec_helper.rb

Where the context.rb file will be pre-populated:

# frozen_string_literal: true

CONTEXT_TEST_SCENARIO = "when using test_scenario config"

RSpec.shared_context CONTEXT_TEST_SCENARIO do
  let(:scenario) { "test_scenario" }
  let(:overrides) {} # TODO: remove if unused
  let(:generated_files) {} # TODO: remove if unused
  let(:expected_files) do
    [
    ]
  end
end

For failure scenarios, run:

bundle exec rake coffeebrew:jekyll:paginate:test:create_failure[test_scenario]

This will generate a placeholder file and directory:

spec
├── coffeebrew_jekyll_paginate_spec.rb
├── scenarios
│   └── test_scenario
│       └── context.rb
└── spec_helper.rb

Where the context.rb file will be pre-populated:

# frozen_string_literal: true

CONTEXT_TEST_SCENARIO = "when using test_scenario config"

RSpec.shared_context CONTEXT_TEST_SCENARIO do
  let(:scenario) { "test_scenario" }
  let(:generated_files) { [] }
  let(:expected_files) { [] }
  let(:overrides) do
    {
    }
  end

  let(:expected_errors) do
    [
    ]
  end
end

If you do write other test cases that are not asserting the generated files like above, you can initiate your convention. The repo owner will evaluate the PR and accept the convention if it fits the repo existing convention, or will recommend an alternative if it doesn't.

License

See the LICENSE file.

FAQs

Package last updated on 09 Apr 2023

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc