Files
danbooru/app/logical/pagination_extension.rb
evazion ceeed1e692 pagination: refactor page limits.
Refactor page limits to a) be explicitly listed in the User class (not
hidden away in the Danbooru config) and b) explicitly depend on the
CurrentUser (not implicitly by way of Danbooru.config.max_numbered_pages).
2021-01-11 21:09:06 -06:00

121 lines
3.4 KiB
Ruby

module PaginationExtension
class PaginationError < StandardError; end
attr_accessor :current_page, :records_per_page, :paginator_count, :paginator_mode
def paginate(page, limit: nil, max_limit: 1000, page_limit: CurrentUser.user.page_limit, count: nil, search_count: nil)
@records_per_page = limit || Danbooru.config.posts_per_page
@records_per_page = @records_per_page.to_i.clamp(1, max_limit)
if count.present?
@paginator_count = count
elsif !search_count.nil? && search_count.blank?
@paginator_count = 1_000_000
end
if page.to_s =~ /\Ab(\d+)\z/i
@paginator_mode = :sequential_before
paginate_sequential_before($1, records_per_page)
elsif page.to_s =~ /\Aa(\d+)\z/i
@paginator_mode = :sequential_after
paginate_sequential_after($1, records_per_page)
else
@paginator_mode = :numbered
@current_page = [page.to_i, 1].max
raise PaginationError if current_page > page_limit
paginate_numbered(current_page, records_per_page)
end
end
def paginate_sequential_before(before_id, limit)
where("#{table_name}.id < ?", before_id).reorder("#{table_name}.id desc").limit(limit + 1)
end
def paginate_sequential_after(after_id, limit)
where("#{table_name}.id > ?", after_id).reorder("#{table_name}.id asc").limit(limit + 1)
end
def paginate_numbered(page, limit)
offset((page - 1) * limit).limit(limit)
end
def is_first_page?
if paginator_mode == :numbered
current_page == 1
elsif paginator_mode == :sequential_before
false
elsif paginator_mode == :sequential_after
size <= records_per_page
end
end
def is_last_page?
if paginator_mode == :numbered
current_page >= total_pages
elsif paginator_mode == :sequential_before
size <= records_per_page
elsif paginator_mode == :sequential_after
false
end
end
def prev_page
if is_first_page?
nil
elsif paginator_mode == :numbered
current_page - 1
elsif paginator_mode == :sequential_before && records.present?
"a#{records.first.id}"
elsif paginator_mode == :sequential_after && records.present?
"b#{records.last.id}"
else
nil
end
rescue ActiveRecord::QueryCanceled
nil
end
def next_page
if is_last_page?
nil
elsif paginator_mode == :numbered
current_page + 1
elsif paginator_mode == :sequential_before && records.present?
"b#{records.last.id}"
elsif paginator_mode == :sequential_after && records.present?
"a#{records.first.id}"
else
nil
end
rescue ActiveRecord::QueryCanceled
nil
end
# XXX Hack: in sequential pagination we fetch one more record than we
# need so that we can tell when we're on the first or last page. Here
# we override a rails internal method to discard that extra record. See
# #2044, #3642.
def records
if paginator_mode == :sequential_before
super.first(records_per_page)
elsif paginator_mode == :sequential_after
super.first(records_per_page).reverse
elsif paginator_mode == :numbered
super
end
end
def total_pages
(total_count.to_f / records_per_page).ceil
end
# taken from kaminari (https://github.com/amatsuda/kaminari)
def total_count
@paginator_count ||= unscoped.from(except(:offset, :limit, :order).reorder(nil)).count
rescue ActiveRecord::StatementInvalid => e
raise unless e.to_s =~ /statement timeout/
@paginator_count ||= 1_000_000
end
end