I have create an initializer and defined the class ActiveStorage::VariantWithRecord… however this overwrites the whole class. Is there any way to overwrite only the single method?
The only solution would be to overwrite the entire class, but I’m wondering if there’s a way to overwrite only that specific method (which is private).
This is definitely a weird behavior of Ruby on Rails…
If I make some tests with a simple Ruby file everything works as expected.
Example:
class Human
def public_speak
speak
end
private
def speak
puts "Hello"
end
end
john = Human.new
john.public_speak
class Human
private
def speak
puts "Hi"
end
end
john.public_speak
The above code works as expected (prints “Hello”, then “Hi”).
However if I use the same strategy to overwrite a Rails core method everything breaks:
class ActiveStorage::VariantWithRecord
private
def record
puts "This is the new record!!!"
@record ||= if blob.variant_records.loaded?
blob.variant_records.find { |v| v.variation_digest == variation.digest }
else
blob.variant_records.find_by(variation_digest: variation.digest)
end
end
end
This code also changes initialize and other methods and I get weird errors like wrong number of arguments (given 2, expected 0) that have nothing to do with the overwritten method.
I think it might be related to preloading or something. What is the correct strategy to change a method in Rails?
When a reload happens, the class ActiveStorage::VariantWithRecord is reloaded too, because that is a model of the engine.
Your application autoloaded the class during initialization to change it. But on reload, the class is totaly new again, and since initializers do not run on reload, that new one does not have your decoration anymore.
As documented in the guide you linked to, when you want to do something on boot and on each reload, you need to wrap that in a to_prepare block. Your solution is correct.
In Rails 7, attempting to autoload a reloadable class or module during initialization is going to be an error condition, to force you to write it right, instead of wondering why it does not work. (However, starting with Rails 6, a warning is issued explaining the problem.)