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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d454d6b6a21129a7e91e0297105e6812b8d386eed0f36906fe3eeb84e970f00
4
- data.tar.gz: 558c823368a66efc849e0eb32bf3e03c70a167ec39752c7079403a35796798cb
3
+ metadata.gz: 74e7178bb7fe2835c6d327e53fc515daa42cee491b56fc0a896d9dc2abbacfdf
4
+ data.tar.gz: 22312d9e2b07d91bb38789b9363ffd542436e6849b364221844b36cebf0f3bb2
5
5
  SHA512:
6
- metadata.gz: cff6918cd34880a010ef55a24de8578ff585625b4b049bc4480bcf177f9994b06ce7e9199dfa31da05b3f25d4780d0e5e92e413d0cb853fe22b8e12c5a57b062
7
- data.tar.gz: efdde8a388ecc0d22887b278c76e16e5291ddf0ae5d74ce2ae4f843f905ba8a446e6499837cfe5b5aca81c4e20f4742e7d2e01e2be1b4d4af4275cf2dec39523
6
+ metadata.gz: bf6fd86289586aa970c2916caddaeb617fae9c1fc1cdee8c3977b40560f97990a431355a07bff6bc24ebc0b2073916e077203ef48c0a98ec9a0e9c7ba1740e0f
7
+ data.tar.gz: 917babeb3c44c49e3379acdb36cf613695c6282500b9ef0ff0edfd5c6c71e7db73cc4ecb5f5917de8c15f0ac24238f9b9dc04ad10660b4d8fe63bb9f827303b3
@@ -10,17 +10,16 @@
10
10
  require "bridgetown-core"
11
11
  require "bridgetown-core/version"
12
12
 
13
- unless ENV["BRIDGETOWN_DISABLE_PAGINATE_GEM"] == "true"
14
- module Bridgetown
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
- DEFAULT = {
8
- "enabled" => false,
9
- "collection" => "posts",
10
- "offset" => 0, # Supports skipping x number of posts from the
11
- # beginning of the post list
12
- "per_page" => 10,
13
- "permalink" => "/page/:num/", # Supports :num as customizable elements
14
- "title" => ":title (Page :num)", # Supports :num as customizable elements
15
- "page_num" => 1,
16
- "sort_reverse" => true,
17
- "sort_field" => "date",
18
- "limit" => 0, # Limit how many content objects to paginate (default: 0, means all)
19
- "trail" => {
20
- "before" => 0, # Limits how many links to show before the current page
21
- # in the pagination trail (0, means off, default: 0)
22
- "after" => 0, # Limits how many links to show after the current page
23
- # in the pagination trail (0 means off, default: 0)
24
- },
25
- "indexpage" => nil, # The default name of the index pages
26
- "extension" => "html", # The default extension for the output pages
27
- # (ignored if indexpage is nil)
28
- "debug" => false, # Turns on debug output for the gem
29
- }.freeze
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
- module Generator
6
- #
7
- # The main entry point into the generator, called by Bridgetown
8
- # this function extracts all the necessary information from the Bridgetown
9
- # end and passes it into the pagination logic. Additionally it also
10
- # contains all site specific actions that the pagination logic needs access
11
- # to (such as how to create new pages)
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
- class PaginationGenerator < Bridgetown::Generator
14
- # This generator should be passive with regard to its execution
15
- priority :lowest
16
-
17
- # Generate paginated pages if necessary (Default entry point)
18
- # site - The Site.
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
- Bridgetown.logger.debug "Pagination:", "Starting"
36
-
37
- ################ 0 ####################
38
- # Get all pages in the site (this will be used to find the pagination
39
- # templates)
40
- all_pages = site.pages
41
-
42
- # Get the default title of the site (used as backup when there is no
43
- # title available for pagination)
44
- site_title = site.data.dig("metadata", "title") || site.config["title"]
45
-
46
- ################ 1 ####################
47
- # Specify the callback function that returns the correct docs/posts
48
- # based on the collection name
49
- # "posts" are just another collection in Bridgetown but a specialized
50
- # version that require timestamps
51
- # This collection is the default and if the user doesn't specify a
52
- # collection in their front-matter then that is the one we load
53
- # If the collection is not found then empty array is returned
54
- collection_by_name_lambda = lambda do |collection_name|
55
- coll = []
56
- if collection_name == "all"
57
- # the 'all' collection_name is a special case and includes all
58
- # collections in the site (except posts!!)
59
- # this is useful when you want to list items across multiple collections
60
- site.collections.each do |coll_name, coll_data|
61
- next unless !coll_data.nil? && coll_name != "posts"
62
-
63
- # Exclude all pagination pages
64
- coll += coll_data.docs.reject do |doc|
65
- doc.data.key?("pagination")
66
- end
67
- end
68
- else
69
- # Just the one collection requested
70
- return [] unless site.collections.key?(collection_name)
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 = site.collections[collection_name].docs.reject do |doc|
75
+ coll += coll_data.docs.reject do |doc|
74
76
  doc.data.key?("pagination")
75
77
  end
76
78
  end
77
- return coll
78
- end
79
+ else
80
+ # Just the one collection requested
81
+ return [] unless site.collections.key?(collection_name)
79
82
 
80
- ################ 2 ####################
81
- # Create the proc that constructs the real-life site page
82
- # This is necessary to decouple the code from the Bridgetown site object
83
- page_add_lambda = lambda do |newpage|
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
- ################ 2.5 ####################
89
- # lambda that removes a page from the site pages list
90
- page_remove_lambda = lambda do |page_to_remove|
91
- site.pages.delete_if { |page| page == page_to_remove }
92
- end
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
- ################ 3 ####################
95
- # Create a proc that will delegate logging
96
- # Decoupling Bridgetown specific logging
97
- logging_lambda = lambda do |message, type = "info"|
98
- if type == "debug"
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
- ################ 4 ####################
110
- # Now create and call the model with the real-life page creation proc and site data
111
- model = PaginationModel.new(
112
- logging_lambda,
113
- page_add_lambda,
114
- page_remove_lambda,
115
- collection_by_name_lambda
116
- )
117
- count = model.run(default_config, all_pages, site_title)
118
- Bridgetown.logger.info "Pagination:", "Complete, processed #{count} pagination page(s)"
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
- module Generator
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
- # Performs indexing of the posts or collection documents as well as
8
- # filtering said collections when requested by the defined filters.
17
+ # Create a hash index for all documents based on a key in the
18
+ # document.data table
9
19
  #
10
- class PaginationIndexer
11
- #
12
- # Create a hash index for all documents based on a key in the
13
- # document.data table
14
- #
15
- def self.index_documents_by(all_documents, index_key)
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
- index
27
+ if (found_index = cached_index.dig(all_documents.object_id, index_key))
28
+ return found_index
48
29
  end
49
30
 
50
- #
51
- # Creates an intersection (only returns common elements)
52
- # between multiple arrays
53
- #
54
- def self.intersect_arrays(first, *rest)
55
- return nil if first.nil?
56
- return nil if rest.nil?
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
- intersect = first
59
- rest.each do |item|
60
- return [] if item.nil?
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
- intersect &= item
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
- intersect
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
- # Filters documents based on a keyed source_documents hash of indexed
69
- # documents and performs a intersection of the two sets. Returns only
70
- # documents that are common between all collections
71
- def self.read_config_value_and_filter_documents(
72
- config,
73
- config_key,
74
- documents,
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
- # The fully filtered final document list
108
- documents
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