Skip to content

Instantly share code, notes, and snippets.

@romiras
Created April 29, 2024 09:07
Show Gist options
  • Save romiras/2a50b2368b348da6e25ccb5788067245 to your computer and use it in GitHub Desktop.
Save romiras/2a50b2368b348da6e25ccb5788067245 to your computer and use it in GitHub Desktop.
Fixing memoization issues in Ruby 2.4.x
# FILE: config/initializers/fix_yaml_column.rb
# serializes the columns that break because of memoized cache
#
# alternatively, we could have just removed the string for the memoized cache, but that sounded too dangerous
# hard-require to load the class
require 'active_record/coders/yaml_column'
class ActiveRecord::Coders::YAMLColumn
private
def yaml_load(payload)
["ThreadSafe::Cache", "Monitor", "Thread::Mutex"].each do |thing_to_remove|
payload.gsub!("!ruby/object:#{thing_to_remove}", "")
end
# make the main object a hash
["Memoizable::Memory"].each do |thing|
payload.gsub!(thing, "Hash")
end
# remove the memoized_method_cache so that the memoized gem doesn't try to use it as its cache
#
# use a trailing : so that we don't have repeating if we gsub again
payload.gsub!("_memoized_method_cache:", "_memoized_method_cache_hack:")
if !ActiveRecord::Base.use_yaml_unsafe_load
YAML.safe_load(payload, permitted_classes: ActiveRecord::Base.yaml_column_permitted_classes, aliases: true)
else
if YAML.respond_to?(:unsafe_load)
YAML.unsafe_load(payload)
else
YAML.load(payload)
end
end
end
end
# FILE: lib/memoized_cache_sanitizer.rb
# a helper module to sanitize object before serialization
module MemoizedCacheSanitizer
REMOVE_INSTANCE_VARIABLE = :@_memoized_method_cache
def self.sanitize(obj)
return obj if obj.nil? || obj.instance_of?(Hash) || obj.instance_of?(Array)
attributes = obj.instance_variables.each_with_object({}) do |iv, h|
attr_name = iv.to_s[1..-1].to_sym
h[attr_name] = obj.send(attr_name) unless REMOVE_INSTANCE_VARIABLE == iv
end
attrs = attributes.delete(:attrs).with_indifferent_access
# build the new object with the sanitized attributes
obj.class.new(attrs).tap do |o|
attributes.each do |k, v|
o.instance_variable_set(:"@#{k}", v) # use attribue setter to avoid memoization
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment