Sha256: 8ce6180bc4c75f800ac55adfd8e92b76bf72d6de9b25e21b9738e9db5bf32ef3

Contents?: true

Size: 1.88 KB

Versions: 2

Compression:

Stored size: 1.88 KB

Contents

require "order_as_specified/version"
require "order_as_specified/error"

# This module adds the ability to query an ActiveRecord class for results from
# the database in an arbitrary order, without having to store anything extra
# in the database. Simply `extend` it into your class and then you can use the
# `order_as_specified` class method.

module OrderAsSpecified
  # @param hash [Hash] the ActiveRecord arguments hash
  # @return [ActiveRecord::Relation] the objects, ordered as specified
  def order_as_specified(hash)
    distinct_on = hash.delete(:distinct_on)
    params = extract_params(hash)

    table = params[:table]
    attribute = params[:attribute]

    # We have to explicitly quote for now because SQL sanitization for ORDER BY
    # queries isn't in less current versions of Rails.
    # See: https://github.com/rails/rails/pull/13008
    conditions = params[:values].map do |value|
      raise OrderAsSpecified::Error, "Cannot order by `nil`" if value.nil?

      "#{table}.#{attribute}='#{value}'"
    end

    scope = order(conditions.map { |cond| "#{cond} DESC" }.join(", "))

    if distinct_on
      scope = scope.select("DISTINCT ON (#{conditions.join(', ')}) #{table}.*")
    end

    scope
  end

  private

  # Recursively search through the hash to find the last elements, which
  # indicate the name of the table we want to condition on, the attribute name,
  # and the attribute values for ordering by.
  # @param table [String/Symbol] the name of the table, default: the class table
  # @param hash [Hash] the ActiveRecord-style arguments, such as:
  #   { other_objects: { id: [1, 5, 3] } }
  def extract_params(table = table_name, hash)
    raise "Could not parse params" unless hash.size == 1

    key, val = hash.first

    if val.is_a? Hash
      extract_params(key, hash[key])
    else
      {
        table: table,
        attribute: key,
        values: val
      }
    end
  end
end

Version data entries

2 entries across 2 versions & 1 rubygems

Version Path
order_as_specified-1.1 lib/order_as_specified.rb
order_as_specified-1.0 lib/order_as_specified.rb