class Pagy
Core class
def self.root
def self.root @root ||= Pathname.new(__dir__).parent.freeze end
def initialize(vars)
def initialize(vars) normalize_vars(vars) setup_vars(count: 0, page: 1, outset: 0) setup_items_var setup_last_var raise OverflowError.new(self, :page, "in 1..#{@last}", @page) if @page > @last setup_offset_var @from = [@offset - @outset + 1, @count].min @to = [@offset - @outset + @items, @count].min @in = [@to - @from + 1, @count].min @prev = (@page - 1 unless @page == 1) @next = @page == @last ? (1 if @vars[:cycle]) : @page + 1 end
def label
def label @page.to_s end
def label_for(page)
def label_for(page) page.to_s end
def normalize_vars(vars)
def normalize_vars(vars) @vars = DEFAULT.merge(vars.delete_if { |k, v| DEFAULT.key?(k) && (v.nil? || v == '') }) end
def series(size: @vars[:size], **_)
def series(size: @vars[:size], **_) series = [] if size.is_a?(Array) && size.size == 4 && size.all? { |num| !num.negative? rescue false } # rubocop:disable Style/RescueModifier # This algorithm is up to ~5x faster and ~2.3x lighter than the previous one (pagy < 4.3) left_gap_start = 1 + size[0] left_gap_end = @page - size[1] - 1 right_gap_start = @page + size[2] + 1 right_gap_end = @last - size[3] left_gap_end = right_gap_end if left_gap_end > right_gap_end right_gap_start = left_gap_start if left_gap_start > right_gap_start start = 1 if (left_gap_end - left_gap_start).positive? series.push(*start...left_gap_start, :gap) start = left_gap_end + 1 end if (right_gap_end - right_gap_start).positive? series.push(*start...right_gap_start, :gap) start = right_gap_end + 1 end series.push(*start..@last) elsif size.is_a?(Integer) && size.positive? # only central series # The simplest and fastest algorithm size = @last if size > @last # reduce the max size to @last left = ((size - 1) / 2.0).floor # left half might be 1 page shorter for even size start = if @page <= left # beginning pages 1 elsif @page > @last - (size - left) # end pages @last - size + 1 else # intermediate pages @page - left end series = (start..start + size - 1).to_a else return [] if size.empty? raise VariableError.new(self, :size, 'to be a single positive Integer or an Array of 4', size) end series[series.index(@page)] = @page.to_s series end
def setup_items_var
def setup_items_var setup_vars(items: 1) end
def setup_last_var
def setup_last_var @last = [(@count.to_f / @items).ceil, 1].max @last = vars[:max_pages] if vars[:max_pages] && @last > vars[:max_pages] end
def setup_offset_var
def setup_offset_var @offset = (@items * (@page - 1)) + @outset # may be already set from gear_box end
def setup_vars(name_min)
def setup_vars(name_min) name_min.each do |name, min| raise VariableError.new(self, name, ">= #{min}", @vars[name]) \ unless @vars[name]&.respond_to?(:to_i) && instance_variable_set(:"@#{name}", @vars[name].to_i) >= min end end