class Pagy

def self.new_from_elasticsearch_rails(response, vars={})

create a Pagy object from an Elasticsearch::Model::Response::Response object
def self.new_from_elasticsearch_rails(response, vars={})
  vars[:items] = response.search.options[:size] || 10
  vars[:page]  = (response.search.options[:from] || 0) / vars[:items] + 1
  vars[:count] = response.raw_response['hits']['total']
  new(vars)
end

def self.new_from_searchkick(results, vars={})

create a Pagy object from a Searchkick::Results object
def self.new_from_searchkick(results, vars={})
  vars[:items] = results.options[:per_page]
  vars[:page]  = results.options[:page]
  vars[:count] = results.total_count
  new(vars)
end

def self.root; @root ||= Pathname.new(__FILE__).dirname.freeze end

Root pathname to get the path of Pagy files like templates or dictionaries
def self.root; @root ||= Pathname.new(__FILE__).dirname.freeze end

def initialize(vars)

Merge and validate the options, do some simple arithmetic and set the instance variables
def initialize(vars)
  @vars = VARS.merge(vars.delete_if{|_,v| v.nil? || v == '' })               # default vars + cleaned vars
  { count:0, items:1, outset:0, page:1 }.each do |k,min|                     # validate instance variables
    (@vars[k] && instance_variable_set(:"@#{k}", @vars[k].to_i) >= min) \
       or raise(ArgumentError, "expected :#{k} >= #{min}; got #{@vars[k].inspect}")
  end
  @pages = @last = [(@count.to_f / @items).ceil, 1].max                      # cardinal and ordinal meanings
  @page <= @last or raise(OverflowError.new(self), "expected :page in 1..#{@last}; got #{@page.inspect}")
  @offset = @items * (@page - 1) + @outset                                   # pagination offset + outset (initial offset)
  @items  = @count - ((@pages-1) * @items) if @page == @last && @count > 0   # adjust items for last non-empty page
  @from   = @count == 0 ? 0 : @offset+1 - @outset                            # page begins from item
  @to     = @count == 0 ? 0 : @offset + @items - @outset                     # page ends to item
  @prev   = (@page-1 unless @page == 1)                                      # nil if no prev page
  @next   = @page == @last ? (1 if @vars[:cycle]) : @page+1                  # nil if no next page, 1 if :cycle
end

def initialize_with_overflow(vars)

def initialize_with_overflow(vars)
  @overflow ||= false                         # don't override if :last_page re-run the method after an overflow
  initialize_without_overflow(vars)
rescue OverflowError
  @overflow = true                            # add the overflow flag
  case @vars[:overflow]
  when :exception
    raise                                     # same as without the extra
  when :last_page
    initial_page = @vars[:page]               # save the very initial page (even after re-run)
    initialize(vars.merge!(page: @last))      # re-run with the last page
    @vars[:page] = initial_page               # restore the inital page
  when :empty_page
    @offset = @items = @from = @to = 0        # vars relative to the actual page
    @prev = @last                             # prev relative to the actual page
    extend(Series)                            # special series for :empty_page
  else
    raise ArgumentError, "expected :overflow variable in [:last_page, :empty_page, :exception]; got #{@vars[:overflow].inspect}"
  end
end

def overflow?; @overflow end

def overflow?; @overflow end

def sequels

Notice: if :steps is false it will use the single {0 => @vars[:size]} size
"550" => [1, 2, 3, :gap, 16, 17, 18, 19, "20", 21, 22, 23, 24, :gap, 48, 49, 50] }
"350" => [1, 2, :gap, 17, 18, 19, "20", 21, 22, 23, :gap, 49, 50],
#=> { "0" => [1, :gap, 18, 19, "20", 21, 22, :gap, 50],
>> pagy.sequels
>> pagy = Pagy.new(count:1000, page: 20, steps: {0 => [1,2,2,1], 350 => [2,3,3,2], 550 => [3,4,4,3]})
Example:
It returns the sequels of width/series generated from the :steps hash
`Pagy` instance method used by the `pagy*_nav_js` helpers.
def sequels
  steps = @vars[:steps] || {0 => @vars[:size]}
  steps.key?(0) or raise(ArgumentError, "expected :steps to define the 0 width; got #{steps.inspect}")
  sequels = {}; steps.each {|width, size| sequels[width.to_s] = series(size)}; sequels
end

def series(size=@vars[:size])

Return the array of page numbers and :gap items e.g. [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
def series(size=@vars[:size])
  (series = []) and size.empty? and return series
  4.times{|i| (size[i]>=0 rescue nil) or raise(ArgumentError, "expected 4 items >= 0 in :size; got #{size.inspect}")}
  [*0..size[0], *@page-size[1]..@page+size[2], *@last-size[3]+1..@last+1].sort!.each_cons(2) do |a, b|
    if    a<0 || a==b || a>@last                                        # skip out of range and duplicates
    elsif a+1 == b; series.push(a)                                      # no gap     -> no additions
    elsif a+2 == b; series.push(a, a+1)                                 # 1 page gap -> fill with missing page
    else            series.push(a, :gap)                                # n page gap -> add gap
    end                                                                 # skip the end boundary (last+1)
  end                                                                   # shift the start boundary (0) and
  series.shift; series[series.index(@page)] = @page.to_s; series        # convert the current page to String
end