class ActiveScaffold::DataStructures::Sorting

encapsulates the column sorting configuration for the List view

Constants

DEFAULT_SORTING_STAGES
SORTING_STAGES

Attributes

active_record_class[R]
constraint_columns[RW]
model[R]
sorting_by_primary_key[RW]

Public Class Methods

new(columns, model) click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 12
def initialize(columns, model)
  @columns = columns
  @clauses = []
  @constraint_columns = []
  @model = model
  @sorting_by_primary_key = false
end

Public Instance Methods

<<(arg) click to toggle source

an alias for add. must accept its arguments in a slightly different form, though.

# File lib/active_scaffold/data_structures/sorting.rb, line 49
def <<(arg)
  add(*arg)
end
add(column_name, direction = nil) click to toggle source

add a clause to the sorting, assuming the column is sortable

# File lib/active_scaffold/data_structures/sorting.rb, line 38
def add(column_name, direction = nil)
  direction ||= 'ASC'
  direction = direction.to_s.upcase
  column = get_column(column_name)
  raise ArgumentError, "Could not find column #{column_name}" if column.nil?
  raise ArgumentError, 'Sorting direction unknown' unless %i[ASC DESC].include? direction.to_sym
  @clauses << [column, direction] if column.sortable?
  raise ArgumentError, "Can't mix :method- and :sql-based sorting" if mixed_sorting?
end
clause(grouped_columns_calculations = nil) click to toggle source

builds an order-by clause

# File lib/active_scaffold/data_structures/sorting.rb, line 124
def clause(grouped_columns_calculations = nil)
  return nil if sorts_by_method? || default_sorting?

  # unless the sorting is by method, create the sql string
  order = []
  each do |sort_column, sort_direction|
    next if constraint_columns.include? sort_column.name
    sql = grouped_columns_calculations&.dig(sort_column.name) || sort_column.sort[:sql]
    next if sql.blank?
    sql = sql.to_sql if sql.respond_to?(:to_sql)

    parts = Array(sql).map do |column|
      mongoid? ? [column, sort_direction] : "#{column} #{sort_direction}"
    end
    order << parts
  end

  order << @primary_key_order_clause if @sorting_by_primary_key
  order.flatten!(1)
  order unless order.empty?
end
clear() click to toggle source

clears the sorting

# File lib/active_scaffold/data_structures/sorting.rb, line 73
def clear
  @default_sorting = false
  @clauses = []
end
direction_of(column) click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 83
def direction_of(column)
  clause = get_clause(column)
  return if clause.nil?
  clause[1]
end
each() { |clause| ... } click to toggle source

iterate over the clauses

# File lib/active_scaffold/data_structures/sorting.rb, line 106
def each
  @clauses.each { |clause| yield clause }
end
each_column() { |clause| ... } click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 110
def each_column
  @clauses.each { |clause| yield clause[0] }
end
first() click to toggle source

provides quick access to the first (and sometimes only) clause

# File lib/active_scaffold/data_structures/sorting.rb, line 115
def first
  @clauses.first
end
next_sorting_of(column, sorted_by_default) click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 91
def next_sorting_of(column, sorted_by_default)
  stages = sorted_by_default ? DEFAULT_SORTING_STAGES : SORTING_STAGES
  stages[direction_of(column)] || 'ASC'
end
set(*args) click to toggle source

clears the sorting before setting to the given column/direction set(column, direction) set({column => direction, column => direction}) set({column => direction}, {column => direction}) set([column, direction], [column, direction])

# File lib/active_scaffold/data_structures/sorting.rb, line 58
def set(*args)
  # TODO: add deprecation unless args.size == 1 && args[0].is_a? Hash
  # when deprecation is removed:
  # * change list#sorting= to sorting.set(val)
  clear
  if args.first.is_a?(Enumerable)
    args.each do |h|
      h.is_a?(Hash) ? h.each { |c, d| add(c, d) } : add(*h)
    end
  else
    add(*args)
  end
end
set_default_sorting() click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 20
def set_default_sorting
  return unless active_record?
  # fallback to setting primary key ordering
  setup_primary_key_order_clause
  model_scope = model.send(:build_default_scope)
  order_clause = model_scope.order_values.join(',') if model_scope
  return unless order_clause
  # If an ORDER BY clause is found set default sorting according to it
  set_sorting_from_order_clause(order_clause, model.table_name)
  @default_sorting = true
end
set_nested_sorting(table_name, order_clause) click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 32
def set_nested_sorting(table_name, order_clause)
  clear
  set_sorting_from_order_clause(order_clause, table_name)
end
size() click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 119
def size
  @clauses.size
end
sorts_by_method?() click to toggle source

checks whether any column is configured to sort by method (using a proc)

# File lib/active_scaffold/data_structures/sorting.rb, line 97
def sorts_by_method?
  @clauses.any? { |sorting| sorting[0].sort.is_a?(Hash) && sorting[0].sort.key?(:method) }
end
sorts_by_sql?() click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 101
def sorts_by_sql?
  @clauses.any? { |sorting| sorting[0].sort.is_a?(Hash) && sorting[0].sort.key?(:sql) }
end
sorts_on?(column) click to toggle source

checks whether the given column (a Column object or a column name) is in the sorting

# File lib/active_scaffold/data_structures/sorting.rb, line 79
def sorts_on?(column)
  !get_clause(column).nil?
end

Protected Instance Methods

default_sorting?() click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 167
def default_sorting?
  @default_sorting
end
different_table?(model_table_name, order_table_name) click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 190
def different_table?(model_table_name, order_table_name)
  !order_table_name.nil? && model_table_name != order_table_name
end
extract_direction(direction_part) click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 202
def extract_direction(direction_part)
  if direction_part.to_s.casecmp('DESC').zero?
    'DESC'
  else
    'ASC'
  end
end
extract_order_parts(criterion_parts) click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 181
def extract_order_parts(criterion_parts)
  column_name_part, direction_part = criterion_parts.strip.split(' ')
  column_name_parts = column_name_part.split('.')
  order = {:direction => extract_direction(direction_part),
           :column_name => remove_quotes(column_name_parts.last)}
  order[:table_name] = remove_quotes(column_name_parts[-2]) if column_name_parts.length >= 2
  order
end
get_clause(column) click to toggle source

retrieves the sorting clause for the given column

# File lib/active_scaffold/data_structures/sorting.rb, line 149
def get_clause(column)
  column = get_column(column)
  @clauses.find { |clause| clause[0] == column }
end
get_column(name_or_column) click to toggle source

possibly converts the given argument into a column object from @columns (if it’s not already)

# File lib/active_scaffold/data_structures/sorting.rb, line 155
def get_column(name_or_column)
  # it's a column
  return name_or_column if name_or_column.is_a? ActiveScaffold::DataStructures::Column
  # it's a name
  name_or_column = name_or_column.to_s.split('.').last if name_or_column.to_s.include? '.'
  @columns[name_or_column]
end
mixed_sorting?() click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 163
def mixed_sorting?
  sorts_by_method? && sorts_by_sql?
end
postgres?() click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 210
def postgres?
  model.connection&.adapter_name == 'PostgreSQL'
end
remove_quotes(sql_name) click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 194
def remove_quotes(sql_name)
  if sql_name.starts_with?('"') || sql_name.starts_with?('`')
    sql_name[1, (sql_name.length - 2)]
  else
    sql_name
  end
end
set_sorting_from_order_clause(order_clause, model_table_name = nil) click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 171
def set_sorting_from_order_clause(order_clause, model_table_name = nil)
  clear
  order_clause.to_s.split(',').each do |criterion|
    if criterion.present?
      order_parts = extract_order_parts(criterion)
      add(order_parts[:column_name], order_parts[:direction]) unless different_table?(model_table_name, order_parts[:table_name]) || get_column(order_parts[:column_name]).nil?
    end
  end
end
setup_primary_key_order_clause() click to toggle source
# File lib/active_scaffold/data_structures/sorting.rb, line 214
def setup_primary_key_order_clause
  return unless model.column_names.include?(model.primary_key)
  set([model.primary_key, 'ASC'])
  @primary_key_order_clause = clause
  @sorting_by_primary_key = postgres? # mandatory for postgres, so enabled by default
end