Class: Jekyll::Plugins::PaginateV3::Query::Sorter

Inherits:
Object
  • Object
show all
Defined in:
lib/jekyll-paginate-v3/query/sorter.rb

Overview

Multi-level sorter for v3 sort definitions.

Example definitions:

  • sort: date desc
  • sort: owner.name, date desc empty:first (delimiter is configurable)
  • sort: ["featured desc", "date desc"]

Used by Pagination::Model to apply deterministic item ordering.

Class Method Summary collapse

Class Method Details

.apply(items, raw_sort, nested_separator:, equivalents:, split_delimiter: ',') ⇒ Object

Applies parsed sort instructions while preserving input order as a final deterministic tiebreak.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/jekyll-paginate-v3/query/sorter.rb', line 20

def self.apply(items, raw_sort, nested_separator:, equivalents:, split_delimiter: ',')
	instructions = parse(raw_sort, split_delimiter: split_delimiter)
	return items if instructions.empty?

	frontmatter_path = Jekyll::Plugins::PaginateV3::Support::FrontmatterPath.new(
		separator: nested_separator,
		arrays: :expand,
		equivalents: equivalents
	)

	# Keep original index as final tiebreak so ordering remains predictable.
	indexed_items = items.each_with_index.to_a
	indexed_items.sort! do |(left_item, left_index), (right_item, right_index)|
		comparison = compare_items(left_item, right_item, instructions, frontmatter_path)
		comparison.zero? ? left_index <=> right_index : comparison
	end

	indexed_items.map(&:first)
end

.parse(raw_sort, split_delimiter: ',') ⇒ Object

Parses sort config entries into normalised field instructions.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/jekyll-paginate-v3/query/sorter.rb', line 41

def self.parse(raw_sort, split_delimiter: ',')
	entries = Utils.arrayify(raw_sort, split_delimiter: split_delimiter).map { |entry| entry.to_s.strip }.reject(&:empty?)

	entries.map do |entry|
		fragments = entry.split(/\s+/)
		field = fragments.shift.to_s.strip
		next nil if field.empty?

		direction = 'asc'
		empty = 'last'

		fragments.each do |fragment|
			token = fragment.to_s.strip.downcase
			case token
			when 'asc', 'ascending'
				direction = 'asc'
			when 'desc', 'descending'
				direction = 'desc'
			else
				if token.start_with?('empty:')
					empty_value = token.split(':', 2).last
					empty = %w[first last].include?(empty_value) ? empty_value : empty
				end
			end
		end

		{
			'field' => field,
			'direction' => direction,
			'empty' => empty
		}
	end.compact
end