Skip to content

Commit

Permalink
refactor: moved shared logic into Attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
cribbles committed Apr 6, 2023
1 parent d098480 commit 9d676d3
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 106 deletions.
45 changes: 45 additions & 0 deletions lib/identity_cache/cached/attribute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,46 @@ def load_one_from_db(key)
unique ? results.first : results
end

def fetch_multi(keys)
keys = keys.map { |key| cast_db_key(key) }

unless model.should_use_cache?
return load_multi_from_db(keys)
end

unordered_hash = CacheKeyLoader.load_multi(self, keys)

# Calling `values` on the result is expected to return the values in the same order as their
# corresponding keys. The fetch_multi_by_#{field_list} generated methods depend on this.
keys.each_with_object({}) do |key, ordered_hash|
ordered_hash[key] = unordered_hash.fetch(key)
end
end

def load_multi_from_db(keys)
rows = load_multi_rows(keys)
result = {}
default = unique ? nil : []
keys.each do |index_value|
result[index_value] = default.try!(:dup)
end
if unique
rows.each do |index_value, attribute_value|
result[index_value] = attribute_value
end
else
rows.each do |index_value, attribute_value|
result[index_value] << attribute_value
end
end
result
end

def cache_encode(db_value)
db_value
end
alias_method :cache_decode, :cache_encode

private

# @abstract
Expand All @@ -80,6 +120,11 @@ def load_from_db_where_conditions(_index_key_or_keys)
raise NotImplementedError
end

# @abstract
def load_multi_rows(_index_keys)
raise NotImplementedError
end

# @abstract
def cache_key_from_key_values(_key_values)
raise NotImplementedError
Expand Down
96 changes: 30 additions & 66 deletions lib/identity_cache/cached/attribute_by_multi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,93 +6,57 @@ class AttributeByMulti < Attribute
def build
cached_attribute = self

model.define_singleton_method(:"fetch_#{fetch_method_suffix}") do |*key_values|
model.define_singleton_method(:"fetch_#{fetch_method_suffix}") do |*keys|
raise_if_scoped
cached_attribute.fetch(key_values)
cached_attribute.fetch(keys)
end

model.define_singleton_method(:"fetch_multi_#{fetch_method_suffix}") do |key_values|
model.define_singleton_method(:"fetch_multi_#{fetch_method_suffix}") do |keys|
raise_if_scoped
cached_attribute.fetch_multi(*key_values)
cached_attribute.fetch_multi(keys)
end
end

def fetch_multi(*key_values)
key_values = key_values.map do |key_value|
cast_db_key(key_value)
end

unless model.should_use_cache?
return load_multi_from_db(key_values)
end

unordered_hash = CacheKeyLoader.load_multi(self, key_values)
private

# Calling `values` on the result is expected to return the values in the same order as their
# corresponding keys. The fetch_multi_by_#{field_list} generated methods depend on this.
ordered_hash = {}
key_values.each { |key_value| ordered_hash[key_value] = unordered_hash.fetch(key_value) }
ordered_hash
end
# Attribute method overrides

def load_multi_from_db(key_values)
rows = unsorted_model_with_where_conditions(key_values).pluck(attribute, *key_fields)
result = {}
default = unique ? nil : []
key_values.each do |index_value|
result[index_value] = default.try!(:dup)
end
if unique
rows.each do |attribute_value, *index_values|
result[index_values] = attribute_value
end
else
rows.each do |attribute_value, *index_values|
result[index_values] << attribute_value
end
def cast_db_key(keys)
field_types.each_with_index do |type, i|
keys[i] = type.cast(keys[i])
end
result
keys
end

def cache_encode(db_value)
db_value
def unhashed_values_cache_key_string(keys)
keys.map { |v| v.try!(:to_s).inspect }.join("/")
end
alias_method :cache_decode, :cache_encode

private

# Attribute method overrides
def load_from_db_where_conditions(keys)
Hash[key_fields.zip(keys)]
end

def unsorted_model_with_where_conditions(key_values)
unsorted_model = model.reorder(nil)
conditions = key_values.map do |keys|
key_fields.each_with_object({}).with_index do |(field, conds), i|
conds[field] = keys[i]
def load_multi_rows(keys)
query = begin
conditions = keys.map do |keys|
key_fields.each_with_object({}).with_index do |(field, conds), i|
conds[field] = keys[i]
end
end
end

conditions.reduce(nil) do |query, condition|
if query.nil?
unsorted_model.where(condition)
else
query.or(unsorted_model.where(condition))
unsorted_model = model.reorder(nil)
conditions.reduce(nil) do |query, condition|
if query.nil?
unsorted_model.where(condition)
else
query.or(unsorted_model.where(condition))
end
end
end
end

def cast_db_key(key_values)
field_types.each_with_index do |type, i|
key_values[i] = type.cast(key_values[i])
query.pluck(attribute, *key_fields).map do |attribute, *key_values|
[key_values, attribute]
end
key_values
end

def unhashed_values_cache_key_string(key_values)
key_values.map { |v| v.try!(:to_s).inspect }.join("/")
end

def load_from_db_where_conditions(key_values)
Hash[key_fields.zip(key_values)]
end

alias_method :cache_key_from_key_values, :cache_key
Expand Down
44 changes: 4 additions & 40 deletions lib/identity_cache/cached/attribute_by_one.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,46 +24,6 @@ def build
end
end

def fetch_multi(keys)
keys = keys.map { |key| cast_db_key(key) }

unless model.should_use_cache?
return load_multi_from_db(keys)
end

unordered_hash = CacheKeyLoader.load_multi(self, keys)

# Calling `values` on the result is expected to return the values in the same order as their
# corresponding keys. The fetch_multi_by_#{field_list} generated methods depend on this.
ordered_hash = {}
keys.each { |key| ordered_hash[key] = unordered_hash.fetch(key) }
ordered_hash
end

def load_multi_from_db(keys)
rows = model.reorder(nil).where(load_from_db_where_conditions(keys)).pluck(key_field, attribute)
result = {}
default = unique ? nil : []
keys.each do |index_value|
result[index_value] = default.try!(:dup)
end
if unique
rows.each do |index_value, attribute_value|
result[index_value] = attribute_value
end
else
rows.each do |index_value, attribute_value|
result[index_value] << attribute_value
end
end
result
end

def cache_encode(db_value)
db_value
end
alias_method :cache_decode, :cache_encode

private

# Attribute method overrides
Expand All @@ -80,6 +40,10 @@ def load_from_db_where_conditions(key_values)
{ key_field => key_values }
end

def load_multi_rows(keys)
model.reorder(nil).where(load_from_db_where_conditions(keys)).pluck(key_field, attribute)
end

def cache_key_from_key_values(key_values)
cache_key(key_values.first)
end
Expand Down

0 comments on commit 9d676d3

Please sign in to comment.