Skip to content

Instantly share code, notes, and snippets.

@hopsoft
Created August 4, 2017 05:18
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 hopsoft/f25b804b6adf46910e99a5167adb1a94 to your computer and use it in GitHub Desktop.
Save hopsoft/f25b804b6adf46910e99a5167adb1a94 to your computer and use it in GitHub Desktop.
Monkey patch for ActiveRecord::CollectionCacheKey
# MONKEY_PATCH: Override ActiveRecord::CollectionCacheKey#collection_cache_key
# This allows us to preserve existing selects when paginating
# thus preventing errors when ordering by custom/computed select fields
#
# For the original implementation SEE: https://github.com/rails/rails/blob/5-0-4/activerecord/lib/active_record/collection_cache_key.rb
# IMPORTANT: Upgrade this file whenever upgrading rails versions
module ActiveRecord::CollectionCacheKey
def collection_cache_key(collection = all, timestamp_column = :updated_at) # :nodoc:
query_signature = Digest::MD5.hexdigest(collection.to_sql)
key = "#{collection.model_name.cache_key}/query-#{query_signature}"
if collection.loaded?
size = collection.size
if size > 0
timestamp = collection.max_by(&timestamp_column)._read_attribute(timestamp_column)
end
else
column_type = type_for_attribute(timestamp_column.to_s)
column = "#{connection.quote_table_name(collection.table_name)}.#{connection.quote_column_name(timestamp_column)}"
select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
if collection.limit_value || collection.offset_value
query = collection.spawn
subquery_alias = "subquery_for_cache_key"
subquery_column_alias = "subquery_for_cache_key_timestamp_column"
query.select_values = [query.select_values.dup, "#{column} AS #{subquery_column_alias}"].flatten
subquery = query.arel.as(subquery_alias)
arel = Arel::SelectManager.new(subquery).project(select_values % subquery_column_alias)
else
query = collection.unscope(:order)
query.select_values = [select_values % column]
arel = query.arel
end
result = connection.select_one(arel, nil, query.bound_attributes)
if result.blank?
size = 0
timestamp = nil
else
size = result["size"]
timestamp = column_type.deserialize(result["timestamp"])
end
end
if timestamp
"#{key}-#{size}-#{timestamp.utc.to_s(cache_timestamp_format)}"
else
"#{key}-#{size}"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment