class ParamsReady::Pagination::CursorBuilder::Cursor

Attributes

cte[R]
literals[R]
select_list[R]
selectors[R]

Public Class Methods

new(select_list, arel_table, context) click to toggle source
# File lib/params_ready/pagination/cursor.rb, line 77
def initialize(select_list, arel_table, context)
  @hash = select_list_to_hash(select_list)
  @selectors, @literals = select_list.partition { |attr| attr.is_a? Selector }
  @arel_table = arel_table
  @context = context
  names = column_names(@selectors)
  @cte_ref = Arel::Table.new(cte_reference(names))
  @cte_def = cte_definition(@cte_ref, names)

  freeze
end

Public Instance Methods

active_record_predicates(literals) click to toggle source
# File lib/params_ready/pagination/cursor.rb, line 121
def active_record_predicates(literals)
  literals.select do |literal|
    literal.pk
  end.map do |literal|
    [literal.key, literal.value]
  end.to_h
end
arel_predicates(literals, arel_table) click to toggle source
# File lib/params_ready/pagination/cursor.rb, line 129
def arel_predicates(literals, arel_table)
  literals.reduce(nil) do |query, literal|
    next query unless literal.pk

    predicate = arel_table[literal.key].eq(literal.quoted)
    next predicate if query.nil?

    query.and(predicate)
  end
end
column_expressions(selectors) click to toggle source
# File lib/params_ready/pagination/cursor.rb, line 144
def column_expressions(selectors)
  selectors.map do |selector|
    selector.expression(@arel_table, @context)
  end
end
column_names(selectors) click to toggle source
# File lib/params_ready/pagination/cursor.rb, line 140
def column_names(selectors)
  selectors.lazy.map(&:key).map(&:to_s).force
end
cte_definition(reference, names) click to toggle source
# File lib/params_ready/pagination/cursor.rb, line 155
def cte_definition(reference, names)
  node = Arel::Nodes::SqlLiteral.new(names.join(', '))
  grouping = Arel::Nodes::Grouping.new(node)
  # The name must be literal, otherwise
  # it will be quoted by the visitor
  expression = "#{reference.name} #{grouping.to_sql}"

  Arel::Nodes::CteName.new(expression)
end
cte_for_query(query, arel_table) click to toggle source
# File lib/params_ready/pagination/cursor.rb, line 110
def cte_for_query(query, arel_table)
  return nil if selectors.empty?

  query = query.deep_dup
  expressions = column_expressions(selectors)
  query = query.where(arel_predicates(literals, arel_table))
               .project(*expressions)
  grouping = Arel::Nodes::Grouping.new(query)
  Arel::Nodes::As.new(@cte_def, grouping)
end
cte_for_relation(relation) click to toggle source
# File lib/params_ready/pagination/cursor.rb, line 98
def cte_for_relation(relation)
  return nil if selectors.empty?

  expressions = column_expressions(selectors)
  relation = relation.where(**active_record_predicates(literals))
                     .select(*expressions)
  select = Arel::Nodes::SqlLiteral.new(relation.to_sql)
  grouping = Arel::Nodes::Grouping.new(select)
  as = Arel::Nodes::As.new(@cte_def, grouping)
  Arel::Nodes::With.new([as])
end
cte_reference(names) click to toggle source
# File lib/params_ready/pagination/cursor.rb, line 150
def cte_reference(names)
  unsafe_name = "#{names.join('_')}_cte"
  Helpers::ArelBuilder.safe_name(unsafe_name)
end
rvalue(key) click to toggle source
# File lib/params_ready/pagination/cursor.rb, line 165
def rvalue(key)
  @hash[key].rvalue(@cte_ref)
end
select_list_to_hash(select_list) click to toggle source
# File lib/params_ready/pagination/cursor.rb, line 89
def select_list_to_hash(select_list)
  res = select_list.each_with_object({}) do |item, hash|
    raise ParamsReadyError, "Repeated key in select list: '#{item.key}'" if hash.key? item.key

    hash[item.key] = item
  end
  res.freeze
end