Created
April 29, 2024 09:07
-
-
Save romiras/2a50b2368b348da6e25ccb5788067245 to your computer and use it in GitHub Desktop.
Fixing memoization issues in Ruby 2.4.x
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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