It seems that alias_method doesn't work the same way inside a class << self block as it does outside. Specifically, when I use alias_method for an instance method and subsequently override the method, the alias name can be used to access the original method. However, I am not able to do this with a class method.
This is my use case: I am overriding the destroy and destroy_all methods that are inherited from ActiveRecord base because the code as currently written makes it very easy to accidentally delete rows when you meant to delete join records. That design could change with a larger project, but for now this is my solution as for the most part, no one will ever want to be deleting rows from this table. But in order to still allow intentional deletion of rows in the table for the well-intentioned, I am using alias_method in order to preserve a handle to the original destroy and destroy_all methods. Using the alias is working to access the original :destroy (instance) method, but is not allowing me to access the original :desotry_all (class) method
class HoldReason < ActiveRecord::Base
class << self
alias_method :remove_hold_reasons_from_table, :destroy_all
def destroy_all
raise "Nope"
end
end
alias_method :remove_hold_reason, :destroy
def destroy
raise "Nope"
end
end
Here we see that while this strategy does work for the instance method to allow successful deletion of a single row:
> HoldReason.find_by(title: "Management Review").remove_hold_reason
HoldReason Load (0.7ms) SELECT `hold_reasons`.* FROM `hold_reasons` WHERE `hold_reasons`.`title` = 'Management Review' LIMIT 1
(0.5ms) BEGIN
SQL (4.6ms) DELETE FROM `hold_reasons` WHERE `hold_reasons`.`id` = 23
(0.6ms) COMMIT
=> #<HoldReason id: 23, title: "Management Review", category: "Operations">
I am not able to access the original :destroy_all method to delete multiple rows in one query; instead, I get the overridden method even when I use the alias:
> HoldReason.remove_hold_reasons_from_table
HoldReason Load (0.7ms) SELECT `hold_reasons`.* FROM `hold_reasons`
RuntimeError: Nope
What is going on that I can't seem to do this for the class methods, and how can I fix it (other than just use a raw SQL query to do the delete)?
The answer is that I was not getting the new :destroy_all method; I was correctly getting the old :destroy_all method, and this called :destroy under the hood. Of course, by calling :destroy, it got the new :destroy method, which raised the error. So it only seemed like I was getting the new class method because the new class method and the new instance method raise exactly the same error.
This is made clearer if I modify this slightly:
results in