module Paginative::ModelExtension

Public Class Methods

by_distance_from(latitude, longitude, distance=0, limit=25) click to toggle source
# File lib/paginative/models/model_extension.rb, line 11
def self.by_distance_from(latitude, longitude, distance=0, limit=25)
  return [] unless latitude.present? && longitude.present?
  distance_sql = send(:distance_sql, latitude.to_f, longitude.to_f, {:units => :km, select_bearing: false})

  self.where("#{distance_sql} >  ?", distance).offset(0).limit(limit)
end
with_field_from(field="", value="", limit=25, order="asc") click to toggle source
# File lib/paginative/models/model_extension.rb, line 27
def self.with_field_from(field="", value="", limit=25, order="asc")
  order ||= "asc"

  # Wrap and flatten whatever comes in, if it's a single value, we end up with an array.
  # If it's an array, stays an array.
  fields = Array.wrap(field).flatten
  values = Array.wrap(value).flatten
  zipped = fields.zip(values)
  fields, values = prune_fields(zipped).transpose

  q = self.all
  if fields.present? && fields.any?
    return raise "Wrong number of values. Expected 2, got #{values.try(:length)}. You must pass a value for each field that you are sorting by" unless values.length <= 2 && values.length == fields.length

    mapped_fields = map_fields(fields)
    q = q.order(sanitized_ordering(self.table_name, mapped_fields, order))

    mapped_fields.each_with_index do |field, idx|
      if idx == 0
        value = values[idx]
        operator = sort_operator(idx, mapped_fields.count, order)

        q = q.where("#{field} #{operator} ?", value)
      else
        previous_field = mapped_fields[idx - 1]
        previous_value = values[idx - 1]
        value = values[idx]
        operator = sort_operator(idx, mapped_fields.count, order)

        q = q.where("#{previous_field} != ? OR #{field} #{operator} ?", previous_value, value)
      end
    end
  end

  return q.limit(limit)
end
with_id_from(id=0, limit=25) click to toggle source
# File lib/paginative/models/model_extension.rb, line 23
def self.with_id_from(id=0, limit=25)
  self.order(id: :asc).where("id > ?", id).limit(limit)
end
with_name_from(name="", limit=25, order="asc") click to toggle source
# File lib/paginative/models/model_extension.rb, line 18
def self.with_name_from(name="", limit=25, order="asc")
  return self.order("name DESC").where("lower(name) < ?", name.downcase).offset(0).limit(limit) if order == "desc"
  self.order(name: :asc).where("lower(#{self.table_name}.name) > ?", name.downcase).offset(0).limit(limit)
end

Private Class Methods

map_fields(fields) click to toggle source

Takes the pruned fields as an array, and returns the mapped versions.

# File lib/paginative/models/model_extension.rb, line 78
def self.map_fields(fields)
  fields.map{ |f| self.paginative_fields[f.to_sym] }
end
prune_fields(zipped) click to toggle source

Steps through the provided paginated fields, zipped with their values, and removes those not in the ‘paginative_fields` hash as specified with `allow_paginative_on`.

# File lib/paginative/models/model_extension.rb, line 68
def self.prune_fields(zipped)
  zipped.select{ |f, v| self.paginative_fields.has_key? f.to_sym }.tap do |pruned|
    unless pruned.nil?
      items = zipped.map(&:first) - pruned.map(&:first)
      Rails.logger.warn "Paginative ignored unpermitted field: #{items}"
    end
  end
end
sort_operator(index, count, direction) click to toggle source

Returns the appropriate order given sort direction and current field in the collection. We don’t want to use inclusive paging if we are at the last field being paginated, so either lt or gt is used.

# File lib/paginative/models/model_extension.rb, line 84
def self.sort_operator(index, count, direction)
  if direction.try(:downcase) == "desc"
    index < (count - 1) ? '<=' : '<'
  else
    index < (count - 1) ? '>=' : '>'
  end
end