class Pagy
def initialize(opts)
def initialize(opts) @opts = Opts.to_h.merge!(opts) # global opts + instance opts (bang faster) @opts[:page] = (@opts[:page]||1).to_i # set page to 1 if nil [:count, :limit, :offset, :initial, :before, :page, :after, :final].each do |k| @opts[k] >= 0 rescue nil || raise(ArgumentError, "expected #{k} >= 0; got #{@opts[k].inspect}") instance_variable_set(:"@#{k}", @opts.delete(k)) # set all the metrics variables end @pages = @last = [(@count.to_f / @limit).ceil, 1].max # cardinal and ordinal meanings (1..@last).cover?(@page) || raise(OutOfRangeError, "expected :page in 1..#{last}; got #{@page.inspect}") @offset += @limit * (@page - 1) # initial offset + offset for pagination @limit = @count % @limit if @page == @last # adjust limit for last page (for pre-limit(ed) collection) @from = @count.zero? ? 0 : @offset+1 # page begins from item @to = @offset + @limit # page ends to item @prev = (@page-1 unless @page == 1) # nil if no prev page @next = (@page+1 unless @page == @last) # nil if no next page @series = [] # e.g. [1, :gap, 7, 8, "9", 10, 11, :gap, 36] all = (0..@last+1) # page range with boundaries arund = ([@page-@before, 1].max .. [@page+@after, @last].min).to_a # before..after pages row = (all.first(@initial+1) | arund | all.last(@final+1)).sort # pages with boundaries row.each_cons(2) do |a, b| # loop in consecutive pairs if a+1 == b; @series.push(a) # no gap -> no additions elsif a+2 == b; @series.push(a, a+1) # 1 page gap -> fill with next page else @series.push(a, :gap) # larger gap -> add :gap end # skip the right boundary (last+1) end @series.shift # remove the left boundary (0) @series[@series.index(@page)] = @page.to_s # convert the current page to String end