Skip to content

Instantly share code, notes, and snippets.

@Bahanix
Created September 27, 2017 16:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Bahanix/0b3e9205a1333dc7a4b7097729f5101b to your computer and use it in GitHub Desktop.
Save Bahanix/0b3e9205a1333dc7a4b7097729f5101b to your computer and use it in GitHub Desktop.
JSONAPI
# app/models/concerns/sortable.rb
module Sortable
extend ActiveSupport::Concern
included do
# Model.smart_sort("attribute_a,+attribute_b,-attribute_c")
# is equivalent to
# Model.order(attribute_a).order(attribute_b: :asc).order(attribute_c: :desc)
scope :smart_sort, -> (sort) {
scope = self.all
String(sort).split(',').each do |attribute|
# ASC or DESC?
first_char = attribute[0]
direction = :asc
if first_char == '-'
direction = :desc
attribute = attribute[1..-1]
elsif first_char == '+'
attribute = attribute[1..-1]
end
# Is it an authorized attribute?
if self.sortable_attributes.include? attribute
# Is it a "association_name.column_name" pattern?
association_name, column = attribute.split('.')
unless column
column = association_name
association_name = nil
end
# Lookup for association model (which is `self` when no association given)
if association_name
association = self.reflect_on_all_associations.select{ |association| association.name == association_name.to_sym }.first
if association
scope = scope.joins(association_name.to_sym)
model = association.class_name.constantize
else
raise ActionController::BadRequest, "#{self} misses #{association_name} association."
end
else
model = self
end
if column_reflection = model.columns_hash[column]
if column_reflection.type == :string
scope = scope.order("UNACCENT(LOWER(\"#{model.table_name}\".\"#{column}\")) #{direction.upcase}")
else
scope = scope.order("\"#{model.table_name}\".\"#{column}\" #{direction.upcase}")
end
else
# SQL computed / aliased attribute, do not prefix with table name
scope = scope.order("#{attribute} #{direction.upcase}")
end
else
raise ActionController::BadRequest, "#{self} sortable attributes do not include #{attribute}."
end
end
scope
}
end
module ClassMethods
attr_accessor :sortable_attributes
def sortable *attributes
self.sortable_attributes = attributes.map(&:to_s)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment