I just encountered an interesting scenario that I felt deserved some discussion:
I needed to make a middleware function for getting an ActiveRecord association, and for whatever reason it occurred to me that alias_method_chain was going to be the right way to do it. While this instinct turned out to be correct, it didn’t occur to me at the time the fact that ActiveRecord sometimes does tricky things involving method_missing and not actually defining its method names. So when I then had to make a middleware function for getting/setting an ActiveRecord column, I turned to alias_method_chain once more. Well, when I tried doing that, I received “NameError: undefined method” for the name of the column that I was trying to alias. What gives? Why did one of these work and not the other one?
As I mentioned, ActiveRecord overrides the method_missing method to “define” some of its method names during runtime, but although column names are in this list, associations are not. Therefore, when you define “belongs_to” or “has_many” in your model, it is creating multiple methods at build time to assist you, named with the specific name you passed it. Column names, however, are not technically defined in the model or anywhere else in code, but by simply what the database returns when asked for these records. So when you call the name of a column as a method on an ActiveRecord object, it first recognizes that no such method exists, and it then redirects the request to see if any column can be fetched (or has been fetched) from the database with this name. (Note that this means you can finagle Rails into recognizing anything you select as a “column”, such as aggregations or other calculated values.) Alias_method and Alias_method_chain require that the method you are trying to alias exists in the first place, so this simply will not work for this case.
If you really think about it, however… Why try to alias something that doesn’t exist in the first place? Obviously, you can simply write the method yourself and bypass all of the method_missing magic, as long as you know what to reproduce. The below is how you would reproduce the accessors for the attribute “name” (with no actual changes functionally):
def name read_attribute(:name) end def name=(value) write_attribute(:name, value) end
From there, it should be easy to see how to make the changes you want.
-CT