bridgetown-paginate 0.11.0 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/bridgetown-paginate.rb +11 -12
- data/lib/bridgetown-paginate/defaults.rb +23 -25
- data/lib/bridgetown-paginate/hooks.rb +9 -0
- data/lib/bridgetown-paginate/pagination_generator.rb +112 -102
- data/lib/bridgetown-paginate/pagination_indexer.rb +104 -92
- data/lib/bridgetown-paginate/pagination_model.rb +376 -369
- data/lib/bridgetown-paginate/pagination_page.rb +43 -46
- data/lib/bridgetown-paginate/paginator.rb +114 -116
- data/lib/bridgetown-paginate/utils.rb +115 -117
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 74e7178bb7fe2835c6d327e53fc515daa42cee491b56fc0a896d9dc2abbacfdf
|
4
|
+
data.tar.gz: 22312d9e2b07d91bb38789b9363ffd542436e6849b364221844b36cebf0f3bb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf6fd86289586aa970c2916caddaeb617fae9c1fc1cdee8c3977b40560f97990a431355a07bff6bc24ebc0b2073916e077203ef48c0a98ec9a0e9c7ba1740e0f
|
7
|
+
data.tar.gz: 917babeb3c44c49e3379acdb36cf613695c6282500b9ef0ff0edfd5c6c71e7db73cc4ecb5f5917de8c15f0ac24238f9b9dc04ad10660b4d8fe63bb9f827303b3
|
data/lib/bridgetown-paginate.rb
CHANGED
@@ -10,17 +10,16 @@
|
|
10
10
|
require "bridgetown-core"
|
11
11
|
require "bridgetown-core/version"
|
12
12
|
|
13
|
-
|
14
|
-
module
|
15
|
-
module Paginate
|
16
|
-
end
|
13
|
+
module Bridgetown
|
14
|
+
module Paginate
|
17
15
|
end
|
18
|
-
|
19
|
-
require "bridgetown-paginate/defaults"
|
20
|
-
require "bridgetown-paginate/utils"
|
21
|
-
require "bridgetown-paginate/pagination_indexer"
|
22
|
-
require "bridgetown-paginate/paginator"
|
23
|
-
require "bridgetown-paginate/pagination_page"
|
24
|
-
require "bridgetown-paginate/pagination_model"
|
25
|
-
require "bridgetown-paginate/pagination_generator"
|
26
16
|
end
|
17
|
+
|
18
|
+
require "bridgetown-paginate/defaults"
|
19
|
+
require "bridgetown-paginate/utils"
|
20
|
+
require "bridgetown-paginate/hooks"
|
21
|
+
require "bridgetown-paginate/pagination_indexer"
|
22
|
+
require "bridgetown-paginate/paginator"
|
23
|
+
require "bridgetown-paginate/pagination_page"
|
24
|
+
require "bridgetown-paginate/pagination_model"
|
25
|
+
require "bridgetown-paginate/pagination_generator"
|
@@ -2,31 +2,29 @@
|
|
2
2
|
|
3
3
|
module Bridgetown
|
4
4
|
module Paginate
|
5
|
-
module Generator
|
6
5
|
# The default configuration for the Paginator
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
6
|
+
DEFAULT = {
|
7
|
+
"enabled" => false,
|
8
|
+
"collection" => "posts",
|
9
|
+
"offset" => 0, # Supports skipping x number of posts from the
|
10
|
+
# beginning of the post list
|
11
|
+
"per_page" => 10,
|
12
|
+
"permalink" => "/page/:num/", # Supports :num as customizable elements
|
13
|
+
"title" => ":title (Page :num)", # Supports :num as customizable elements
|
14
|
+
"page_num" => 1,
|
15
|
+
"sort_reverse" => true,
|
16
|
+
"sort_field" => "date",
|
17
|
+
"limit" => 0, # Limit how many content objects to paginate (default: 0, means all)
|
18
|
+
"trail" => {
|
19
|
+
"before" => 0, # Limits how many links to show before the current page
|
20
|
+
# in the pagination trail (0, means off, default: 0)
|
21
|
+
"after" => 0, # Limits how many links to show after the current page
|
22
|
+
# in the pagination trail (0 means off, default: 0)
|
23
|
+
},
|
24
|
+
"indexpage" => nil, # The default name of the index pages
|
25
|
+
"extension" => "html", # The default extension for the output pages
|
26
|
+
# (ignored if indexpage is nil)
|
27
|
+
"debug" => false, # Turns on debug output for the gem
|
28
|
+
}.freeze
|
31
29
|
end
|
32
30
|
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Bridgetown::Hooks.register :pages, :post_init do |page|
|
4
|
+
if page.class != Bridgetown::Paginate::PaginationPage &&
|
5
|
+
page.site.config.dig("pagination", "enabled") &&
|
6
|
+
page.data.dig("pagination", "enabled")
|
7
|
+
Bridgetown::Paginate::PaginationGenerator.add_matching_template(page)
|
8
|
+
end
|
9
|
+
end
|
@@ -2,121 +2,131 @@
|
|
2
2
|
|
3
3
|
module Bridgetown
|
4
4
|
module Paginate
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
#
|
6
|
+
# The main entry point into the generator, called by Bridgetown
|
7
|
+
# this function extracts all the necessary information from the Bridgetown
|
8
|
+
# end and passes it into the pagination logic. Additionally it also
|
9
|
+
# contains all site specific actions that the pagination logic needs access
|
10
|
+
# to (such as how to create new pages)
|
11
|
+
#
|
12
|
+
class PaginationGenerator < Bridgetown::Generator
|
13
|
+
# This generator should be passive with regard to its execution
|
14
|
+
priority :lowest
|
15
|
+
|
16
|
+
@matching_templates = []
|
17
|
+
|
18
|
+
def self.add_matching_template(template)
|
19
|
+
@matching_templates << template
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
attr_reader :matching_templates
|
24
|
+
end
|
25
|
+
|
26
|
+
# Generate paginated pages if necessary (Default entry point)
|
27
|
+
# site - The Site.
|
12
28
|
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
# Returns nothing.
|
21
|
-
def generate(site)
|
22
|
-
# Retrieve and merge the pagination configuration from the site yml file
|
23
|
-
default_config = Bridgetown::Utils.deep_merge_hashes(
|
24
|
-
DEFAULT,
|
25
|
-
site.config["pagination"] || {}
|
26
|
-
)
|
27
|
-
|
28
|
-
# If disabled then simply quit
|
29
|
-
unless default_config["enabled"]
|
30
|
-
Bridgetown.logger.info "Pagination:", "disabled. Enable in site config" \
|
31
|
-
" with pagination:\\n enabled: true"
|
32
|
-
return
|
33
|
-
end
|
29
|
+
# Returns nothing.
|
30
|
+
def generate(site)
|
31
|
+
# Retrieve and merge the pagination configuration from the site yml file
|
32
|
+
default_config = Bridgetown::Utils.deep_merge_hashes(
|
33
|
+
DEFAULT,
|
34
|
+
site.config["pagination"] || {}
|
35
|
+
)
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
37
|
+
# If disabled then simply quit
|
38
|
+
unless default_config["enabled"]
|
39
|
+
Bridgetown.logger.info "Pagination:", "disabled. Enable in site config" \
|
40
|
+
" with pagination:\\n enabled: true"
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
Bridgetown.logger.debug "Pagination:", "Starting"
|
45
|
+
|
46
|
+
################ 0 ####################
|
47
|
+
# Get all matching pages in the site found by the init hooks, and ensure they're
|
48
|
+
# still in the site.pages array
|
49
|
+
templates = self.class.matching_templates.select do |page|
|
50
|
+
site.pages.include? page
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get the default title of the site (used as backup when there is no
|
54
|
+
# title available for pagination)
|
55
|
+
site_title = site.data.dig("metadata", "title") || site.config["title"]
|
56
|
+
|
57
|
+
################ 1 ####################
|
58
|
+
# Specify the callback function that returns the correct docs/posts
|
59
|
+
# based on the collection name
|
60
|
+
# "posts" are just another collection in Bridgetown but a specialized
|
61
|
+
# version that require timestamps
|
62
|
+
# This collection is the default and if the user doesn't specify a
|
63
|
+
# collection in their front-matter then that is the one we load
|
64
|
+
# If the collection is not found then empty array is returned
|
65
|
+
collection_by_name_lambda = lambda do |collection_name|
|
66
|
+
coll = []
|
67
|
+
if collection_name == "all"
|
68
|
+
# the 'all' collection_name is a special case and includes all
|
69
|
+
# collections in the site (except posts!!)
|
70
|
+
# this is useful when you want to list items across multiple collections
|
71
|
+
site.collections.each do |coll_name, coll_data|
|
72
|
+
next unless !coll_data.nil? && coll_name != "posts"
|
71
73
|
|
72
74
|
# Exclude all pagination pages
|
73
|
-
coll
|
75
|
+
coll += coll_data.docs.reject do |doc|
|
74
76
|
doc.data.key?("pagination")
|
75
77
|
end
|
76
78
|
end
|
77
|
-
|
78
|
-
|
79
|
+
else
|
80
|
+
# Just the one collection requested
|
81
|
+
return [] unless site.collections.key?(collection_name)
|
79
82
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
site.pages << newpage # Add the page to the site so that it is generated correctly
|
85
|
-
return newpage # Return the site to the calling code
|
83
|
+
# Exclude all pagination pages
|
84
|
+
coll = site.collections[collection_name].docs.reject do |doc|
|
85
|
+
doc.data.key?("pagination")
|
86
|
+
end
|
86
87
|
end
|
88
|
+
return coll
|
89
|
+
end
|
87
90
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
91
|
+
################ 2 ####################
|
92
|
+
# Create the proc that constructs the real-life site page
|
93
|
+
# This is necessary to decouple the code from the Bridgetown site object
|
94
|
+
page_add_lambda = lambda do |newpage|
|
95
|
+
site.pages << newpage # Add the page to the site so that it is generated correctly
|
96
|
+
return newpage # Return the site to the calling code
|
97
|
+
end
|
93
98
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
Bridgetown.logger.debug "Pagination:", message.to_s
|
100
|
-
elsif type == "error"
|
101
|
-
Bridgetown.logger.error "Pagination:", message.to_s
|
102
|
-
elsif type == "warn"
|
103
|
-
Bridgetown.logger.warn "Pagination:", message.to_s
|
104
|
-
else
|
105
|
-
Bridgetown.logger.info "Pagination:", message.to_s
|
106
|
-
end
|
107
|
-
end
|
99
|
+
################ 2.5 ####################
|
100
|
+
# lambda that removes a page from the site pages list
|
101
|
+
page_remove_lambda = lambda do |page_to_remove|
|
102
|
+
site.pages.delete_if { |page| page == page_to_remove }
|
103
|
+
end
|
108
104
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
105
|
+
################ 3 ####################
|
106
|
+
# Create a proc that will delegate logging
|
107
|
+
# Decoupling Bridgetown specific logging
|
108
|
+
logging_lambda = lambda do |message, type = "info"|
|
109
|
+
if type == "debug"
|
110
|
+
Bridgetown.logger.debug "Pagination:", message.to_s
|
111
|
+
elsif type == "error"
|
112
|
+
Bridgetown.logger.error "Pagination:", message.to_s
|
113
|
+
elsif type == "warn"
|
114
|
+
Bridgetown.logger.warn "Pagination:", message.to_s
|
115
|
+
else
|
116
|
+
Bridgetown.logger.info "Pagination:", message.to_s
|
117
|
+
end
|
119
118
|
end
|
119
|
+
|
120
|
+
################ 4 ####################
|
121
|
+
# Now create and call the model with the real-life page creation proc and site data
|
122
|
+
model = PaginationModel.new(
|
123
|
+
logging_lambda,
|
124
|
+
page_add_lambda,
|
125
|
+
page_remove_lambda,
|
126
|
+
collection_by_name_lambda
|
127
|
+
)
|
128
|
+
count = model.run(default_config, templates, site_title)
|
129
|
+
Bridgetown.logger.info "Pagination:", "Complete, processed #{count} pagination page(s)"
|
120
130
|
end
|
121
131
|
end
|
122
132
|
end
|
@@ -2,111 +2,123 @@
|
|
2
2
|
|
3
3
|
module Bridgetown
|
4
4
|
module Paginate
|
5
|
-
|
5
|
+
#
|
6
|
+
# Performs indexing of the posts or collection documents as well as
|
7
|
+
# filtering said collections when requested by the defined filters.
|
8
|
+
#
|
9
|
+
class PaginationIndexer
|
10
|
+
@cached_index = {}
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_accessor :cached_index
|
14
|
+
end
|
15
|
+
|
6
16
|
#
|
7
|
-
#
|
8
|
-
#
|
17
|
+
# Create a hash index for all documents based on a key in the
|
18
|
+
# document.data table
|
9
19
|
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
|
16
|
-
return nil if all_documents.nil?
|
17
|
-
return all_documents if index_key.nil?
|
18
|
-
|
19
|
-
# Where queries are a key/value pair, so grab the first key element
|
20
|
-
index_key = index_key[0] if index_key.is_a?(Array)
|
21
|
-
|
22
|
-
index = {}
|
23
|
-
all_documents.each do |document|
|
24
|
-
next if document.data.nil?
|
25
|
-
next unless document.data.key?(index_key)
|
26
|
-
next if document.data[index_key].nil?
|
27
|
-
next if document.data[index_key].size <= 0
|
28
|
-
next if document.data[index_key].to_s.strip.empty?
|
29
|
-
|
30
|
-
# Only tags and categories come as premade arrays, locale does not,
|
31
|
-
# so convert any data elements that are strings into arrays
|
32
|
-
document_data = document.data[index_key]
|
33
|
-
document_data = document_data.split(%r!;|,!) if document_data.is_a?(String)
|
34
|
-
|
35
|
-
document_data.each do |key|
|
36
|
-
key = key.to_s.downcase.strip
|
37
|
-
# If the key is a delimetered list of values
|
38
|
-
# (meaning the user didn't use an array but a string with commas)
|
39
|
-
key.split(%r!;|,!).each do |k_split|
|
40
|
-
k_split = k_split.to_s.downcase.strip # Clean whitespace and junk
|
41
|
-
index[k_split.to_s] = [] unless index.key?(k_split)
|
42
|
-
index[k_split.to_s] << document
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
20
|
+
def self.index_documents_by(all_documents, index_key)
|
21
|
+
return nil if all_documents.nil?
|
22
|
+
return all_documents if index_key.nil?
|
23
|
+
|
24
|
+
# Where queries are a key/value pair, so grab the first key element
|
25
|
+
index_key = index_key[0] if index_key.is_a?(Array)
|
46
26
|
|
47
|
-
|
27
|
+
if (found_index = cached_index.dig(all_documents.object_id, index_key))
|
28
|
+
return found_index
|
48
29
|
end
|
49
30
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
31
|
+
index = {}
|
32
|
+
all_documents.each do |document|
|
33
|
+
next if document.data.nil?
|
34
|
+
next unless document.data.key?(index_key)
|
35
|
+
next if document.data[index_key].nil?
|
36
|
+
next if document.data[index_key].size <= 0
|
37
|
+
next if document.data[index_key].to_s.strip.empty?
|
57
38
|
|
58
|
-
|
59
|
-
|
60
|
-
|
39
|
+
# Only tags and categories come as premade arrays, locale does not,
|
40
|
+
# so convert any data elements that are strings into arrays
|
41
|
+
document_data = document.data[index_key]
|
42
|
+
document_data = document_data.split(%r!;|,!) if document_data.is_a?(String)
|
61
43
|
|
62
|
-
|
44
|
+
document_data.each do |key|
|
45
|
+
key = key.to_s.downcase.strip
|
46
|
+
# If the key is a delimetered list of values
|
47
|
+
# (meaning the user didn't use an array but a string with commas)
|
48
|
+
key.split(%r!;|,!).each do |k_split|
|
49
|
+
k_split = k_split.to_s.downcase.strip # Clean whitespace and junk
|
50
|
+
index[k_split.to_s] = [] unless index.key?(k_split)
|
51
|
+
index[k_split.to_s] << document
|
52
|
+
end
|
63
53
|
end
|
54
|
+
end
|
64
55
|
|
65
|
-
|
56
|
+
unless cached_index[all_documents.object_id].is_a?(Hash)
|
57
|
+
cached_index[all_documents.object_id] = {}
|
66
58
|
end
|
59
|
+
cached_index[all_documents.object_id][index_key] = index
|
60
|
+
index
|
61
|
+
end
|
67
62
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
source_documents
|
76
|
-
)
|
77
|
-
return nil if documents.nil?
|
78
|
-
|
79
|
-
# If the source is empty then simply don't do anything
|
80
|
-
return nil if source_documents.nil?
|
81
|
-
|
82
|
-
return documents if config.nil?
|
83
|
-
return documents unless config.key?(config_key)
|
84
|
-
return documents if config[config_key].nil?
|
85
|
-
|
86
|
-
# Get the filter values from the config (this is the cat/tag/locale
|
87
|
-
# values that should be filtered on)
|
88
|
-
config_value = config[config_key]
|
89
|
-
|
90
|
-
# Use the second key/value element if it's a "where query"
|
91
|
-
config_value = config_value[1] if config_key == "where_query"
|
92
|
-
|
93
|
-
# If we're dealing with a delimitered string instead of an array then
|
94
|
-
# let's be forgiving
|
95
|
-
config_value = config_value.split(%r!;|,!) if config_value.is_a?(String)
|
96
|
-
|
97
|
-
# Now for all filter values for the config key, let's remove all items
|
98
|
-
# from the documents that aren't common for all collections that the
|
99
|
-
# user wants to filter on
|
100
|
-
# TODO: right now this is an "AND" operation if multiple keys are present.
|
101
|
-
# Should offer the ability to do an OR operation instead.
|
102
|
-
config_value.each do |key|
|
103
|
-
key = key.to_s.downcase.strip
|
104
|
-
documents = PaginationIndexer.intersect_arrays(documents, source_documents[key])
|
105
|
-
end
|
63
|
+
#
|
64
|
+
# Creates an intersection (only returns common elements)
|
65
|
+
# between multiple arrays
|
66
|
+
#
|
67
|
+
def self.intersect_arrays(first, *rest)
|
68
|
+
return nil if first.nil?
|
69
|
+
return nil if rest.nil?
|
106
70
|
|
107
|
-
|
108
|
-
|
71
|
+
intersect = first
|
72
|
+
rest.each do |item|
|
73
|
+
return [] if item.nil?
|
74
|
+
|
75
|
+
intersect &= item
|
109
76
|
end
|
77
|
+
|
78
|
+
intersect
|
79
|
+
end
|
80
|
+
|
81
|
+
# Filters documents based on a keyed source_documents hash of indexed
|
82
|
+
# documents and performs a intersection of the two sets. Returns only
|
83
|
+
# documents that are common between all collections
|
84
|
+
def self.read_config_value_and_filter_documents(
|
85
|
+
config,
|
86
|
+
config_key,
|
87
|
+
documents,
|
88
|
+
source_documents
|
89
|
+
)
|
90
|
+
return nil if documents.nil?
|
91
|
+
|
92
|
+
# If the source is empty then simply don't do anything
|
93
|
+
return nil if source_documents.nil?
|
94
|
+
|
95
|
+
return documents if config.nil?
|
96
|
+
return documents unless config.key?(config_key)
|
97
|
+
return documents if config[config_key].nil?
|
98
|
+
|
99
|
+
# Get the filter values from the config (this is the cat/tag/locale
|
100
|
+
# values that should be filtered on)
|
101
|
+
config_value = config[config_key]
|
102
|
+
|
103
|
+
# Use the second key/value element if it's a "where query"
|
104
|
+
config_value = config_value[1] if config_key == "where_query"
|
105
|
+
|
106
|
+
# If we're dealing with a delimitered string instead of an array then
|
107
|
+
# let's be forgiving
|
108
|
+
config_value = config_value.split(%r!;|,!) if config_value.is_a?(String)
|
109
|
+
|
110
|
+
# Now for all filter values for the config key, let's remove all items
|
111
|
+
# from the documents that aren't common for all collections that the
|
112
|
+
# user wants to filter on
|
113
|
+
# TODO: right now this is an "AND" operation if multiple keys are present.
|
114
|
+
# Should offer the ability to do an OR operation instead.
|
115
|
+
config_value.each do |key|
|
116
|
+
key = key.to_s.downcase.strip
|
117
|
+
documents = PaginationIndexer.intersect_arrays(documents, source_documents[key])
|
118
|
+
end
|
119
|
+
|
120
|
+
# The fully filtered final document list
|
121
|
+
documents
|
110
122
|
end
|
111
123
|
end
|
112
124
|
end
|