class Middleman::Blog::Paginator

A sitemap plugin that splits indexes (including tag and calendar indexes) over multiple pages

Public Class Methods

new(app, blog_controller) click to toggle source
# File lib/middleman-blog/paginator.rb, line 10
def initialize(app, blog_controller)
  @app             = app
  @blog_controller = blog_controller
  @per_page        = blog_controller.options.per_page
  @page_link       = blog_controller.options.page_link
end

Public Instance Methods

manipulate_resource_list(resources) click to toggle source

Update the main sitemap resource list @return [void]

# File lib/middleman-blog/paginator.rb, line 19
def manipulate_resource_list(resources)
  new_resources = []

  resources.each do |res|
    next if res.ignored?

    # Avoid recomputing metadata over and over
    md = res.metadata

    next unless md[:page][:pageable]

    # Skip other blogs' resources
    next unless match_blog(res, md)

    # "articles" local variable is populated by Calendar and Tag page generators
    # If it's not set then use the complete list of articles
    # TODO: Some way to allow the frontmatter to specify the article filter?
    articles = md[:locals]['articles'] || @blog_controller.data.articles
    articles.select! { |article| article.lang == md[:options][:locale] } if md.fetch(:options, false) && md[:options].fetch(:locale, false)

    # Allow blog.per_page and blog.page_link to be overridden in the frontmatter
    per_page  = md[:page][:per_page] || @per_page
    page_link = uri_template(md[:page][:page_link] || @page_link)

    num_pages = (articles.length / per_page.to_f).ceil

    # Add the pagination metadata to the base page (page 1)
    res.add_metadata locals: page_locals(1, num_pages, per_page, nil, articles)

    prev_page_res = res

    # Create additional resources for the 2nd and subsequent pages.
    2.upto(num_pages) do |page_num|
      p = page_resource(res, page_num, page_link)

      # Copy the metadata from the base page
      p.add_metadata md
      p.add_metadata locals: page_locals(page_num, num_pages, per_page, prev_page_res, articles)

      # Add a reference in the previous page to this page
      prev_page_res.add_metadata locals: { 'next_page' => p }

      prev_page_res = p

      new_resources << p
    end
  end

  resources + new_resources
end

Private Instance Methods

match_blog(res, md) click to toggle source

Does this resource match the blog controller for this paginator? @return [Boolean]

# File lib/middleman-blog/paginator.rb, line 74
def match_blog(res, md)
  res_controller = md[:locals]['blog_controller'] || (res.respond_to?(:blog_controller) && res.blog_controller)
  return false if res_controller && res_controller != @blog_controller

  override_controller = md[:page][:blog]
  return false if override_controller && override_controller.to_s != @blog_controller.name.to_s

  true
end
page_locals(page_num, num_pages, per_page, prev_page_res, articles) click to toggle source

@param [Integer] page_num the page number to generate a resource for @param [Integer] num_pages Total number of pages @param [Integer] per_page How many articles per page @param [Sitemap::Resource] prev_page_res The resource of the previous page @param [Array<Sitemap::Resource>] articles The list of all articles

# File lib/middleman-blog/paginator.rb, line 103
def page_locals(page_num, num_pages, per_page, prev_page_res, articles)
  # Index into articles of the first article of this page
  page_start = (page_num - 1) * per_page

  # Index into articles of the last article of this page
  page_end = (page_num * per_page) - 1

  {
    # Set a flag to allow templates to be used with and without pagination
    'paginate' => true,

    # Include the numbers, useful for displaying "Page X of Y"
    'page_number' => page_num,
    'num_pages' => num_pages,
    'per_page' => per_page,

    # The range of article numbers on this page
    # (1-based, for showing "Items X to Y of Z")
    'page_start' => page_start + 1,
    'page_end' => [page_end + 1, articles.length].min,

    # These contain the next and previous page.
    # They are set to nil if there are no more pages.
    # The nils are overwritten when the later pages are generated, below.
    'next_page' => nil,
    'prev_page' => prev_page_res,

    # The list of articles for this page.
    'page_articles' => articles[page_start..page_end],

    # Include the articles so that non-proxied pages can use "articles" instead
    # of "blog.articles" for consistency with the calendar and tag templates.
    'articles' => articles,
    'blog_controller' => @blog_controller
  }
end
page_resource(res, page_num, page_link) click to toggle source

Generate a resource for a particular page @param [Sitemap::Resource] res the original resource @param [Integer] page_num the page number to generate a resource for @param [String] page_link The pagination link path component template

# File lib/middleman-blog/paginator.rb, line 88
def page_resource(res, page_num, page_link)
  path = page_sub(res, page_num, page_link)

  if res.is_a? Sitemap::ProxyResource
    Sitemap::ProxyResource.new(@app.sitemap, path, res.target)
  else
    Sitemap::Resource.new(@app.sitemap, path, res.source_file)
  end
end
page_sub(res, page_num, page_link) click to toggle source

Substitute the page number into the resource URL. @param [Middleman::Sitemap::Resource] res The resource to generate pages for @param [Integer] page_num The page page_number @param [String] page_link The pagination link path component template @return [String]

# File lib/middleman-blog/paginator.rb, line 145
def page_sub(res, page_num, page_link)
  if page_num == 1
    # First page has an unmodified URL.
    res.path
  else
    page_url = apply_uri_template page_link, num: page_num
    index_re = %r{(^|/)#{Regexp.escape(@app.config[:index_file])}$}
    if res.path =~ index_re
      res.path.sub(index_re, "\\1#{page_url}/#{@app.config[:index_file]}")
    else
      res.path.sub(%r{(^|/)([^/]*)\.([^/]*)$}, "\\1\\2/#{page_url}.\\3")
    end
  end
end