kaminari--kaminari/kaminari-core/lib/kaminari/helpers/paginator.rb

192 lines
6.0 KiB
Ruby
Raw Normal View History

2016-10-11 20:44:41 +00:00
# frozen_string_literal: true
2018-11-10 22:54:07 +00:00
require 'active_support/inflector'
require 'kaminari/helpers/tags'
2011-02-10 18:45:43 +00:00
module Kaminari
module Helpers
# The main container tag
class Paginator < Tag
2017-06-06 08:27:22 +00:00
def initialize(template, window: nil, outer_window: Kaminari.config.outer_window, left: Kaminari.config.left, right: Kaminari.config.right, inner_window: Kaminari.config.window, **options) #:nodoc:
@window_options = {window: window || inner_window, left: left.zero? ? outer_window : left, right: right.zero? ? outer_window : right}
2016-11-23 00:21:23 +00:00
2016-12-08 06:58:56 +00:00
@template, @options, @theme, @views_prefix, @last = template, options, options[:theme], options[:views_prefix], nil
2013-11-23 16:04:11 +00:00
@window_options.merge! @options
2013-12-02 10:00:32 +00:00
@window_options[:current_page] = @options[:current_page] = PageProxy.new(@window_options, @options[:current_page], nil)
2013-11-23 16:04:11 +00:00
#XXX Using parent template's buffer class for rendering each partial here. This might cause problems if the handler mismatches
2016-12-08 02:07:15 +00:00
@output_buffer = if defined?(::ActionView::OutputBuffer)
::ActionView::OutputBuffer.new
elsif template.instance_variable_get(:@output_buffer)
2016-12-08 02:07:15 +00:00
template.instance_variable_get(:@output_buffer).class.new
else
2016-12-08 02:07:15 +00:00
ActiveSupport::SafeBuffer.new
end
end
# render given block as a view template
def render(&block)
2012-05-25 06:31:53 +00:00
instance_eval(&block) if @options[:total_pages] > 1
# This allows for showing fall-back HTML when there's only one page:
#
# <%= paginate(@search_results) || "Showing all search results" %>
@output_buffer.presence
end
# enumerate each page providing PageProxy object as the block parameter
# Because of performance reason, this doesn't actually enumerate all pages but pages that are seemingly relevant to the paginator.
# "Relevant" pages are:
# * pages inside the left outer window plus one for showing the gap tag
# * pages inside the inner window plus one on the left plus one on the right for showing the gap tags
# * pages inside the right outer window plus one for showing the gap tag
def each_relevant_page
return to_enum(:each_relevant_page) unless block_given?
relevant_pages(@window_options).each do |page|
yield PageProxy.new(@window_options, page, @last)
end
end
alias each_page each_relevant_page
def relevant_pages(options)
left_window_plus_one = [*1..options[:left] + 1]
right_window_plus_one = [*options[:total_pages] - options[:right]..options[:total_pages]]
inside_window_plus_each_sides = [*options[:current_page] - options[:window] - 1..options[:current_page] + options[:window] + 1]
2016-12-08 01:46:44 +00:00
(left_window_plus_one | inside_window_plus_each_sides | right_window_plus_one).sort.reject {|x| (x < 1) || (x > options[:total_pages])}
end
private :relevant_pages
def page_tag(page)
2016-11-22 13:16:57 +00:00
@last = Page.new @template, @options.merge(page: page)
end
%w[first_page prev_page next_page last_page gap].each do |tag|
eval <<-DEF, nil, __FILE__, __LINE__ + 1
def #{tag}_tag
@last = #{tag.classify}.new @template, @options
end
DEF
2011-02-16 14:24:31 +00:00
end
def to_s #:nodoc:
Thread.current[:kaminari_rendering] = true
2016-11-22 13:16:57 +00:00
super @window_options.merge paginator: self
ensure
Thread.current[:kaminari_rendering] = false
end
# delegates view helper methods to @template
def method_missing(name, *args, &block)
@template.respond_to?(name) ? @template.send(name, *args, &block) : super
end
private :method_missing
# Wraps a "page number" and provides some utility methods
class PageProxy
include Comparable
def initialize(options, page, last) #:nodoc:
@options, @page, @last = options, page, last
end
# the page number
def number
@page
end
# current page or not
def current?
@page == @options[:current_page]
end
# the first page or not
def first?
@page == 1
end
# the last page or not
def last?
2012-05-25 06:31:53 +00:00
@page == @options[:total_pages]
end
# the previous page or not
def prev?
@page == @options[:current_page] - 1
end
# the next page or not
def next?
@page == @options[:current_page] + 1
end
# relationship with the current page
def rel
if next?
'next'
elsif prev?
'prev'
end
end
# within the left outer window or not
def left_outer?
@page <= @options[:left]
end
# within the right outer window or not
def right_outer?
2012-05-25 06:31:53 +00:00
@options[:total_pages] - @page < @options[:right]
end
# inside the inner window or not
def inside_window?
2011-02-27 15:17:09 +00:00
(@options[:current_page] - @page).abs <= @options[:window]
end
2016-12-08 03:31:07 +00:00
# Current page is an isolated gap or not
def single_gap?
2016-12-08 03:26:59 +00:00
((@page == @options[:current_page] - @options[:window] - 1) && (@page == @options[:left] + 1)) ||
((@page == @options[:current_page] + @options[:window] + 1) && (@page == @options[:total_pages] - @options[:right]))
end
2016-12-08 03:31:07 +00:00
# The page number exceeds the range of pages or not
def out_of_range?
@page > @options[:total_pages]
end
# The last rendered tag was "truncated" or not
def was_truncated?
@last.is_a? Gap
end
#Should we display the link tag?
def display_tag?
left_outer? || right_outer? || inside_window? || single_gap?
end
2016-12-08 03:31:07 +00:00
def to_i #:nodoc:
number
end
2016-12-08 03:31:07 +00:00
def to_s #:nodoc:
number.to_s
end
2016-12-08 03:31:07 +00:00
def +(other) #:nodoc:
2011-02-27 15:17:09 +00:00
to_i + other.to_i
end
2016-12-08 03:31:07 +00:00
def -(other) #:nodoc:
2011-02-27 15:17:09 +00:00
to_i - other.to_i
end
2016-12-08 03:31:07 +00:00
def <=>(other) #:nodoc:
to_i <=> other.to_i
end
end
2011-02-05 14:09:07 +00:00
end
end
end