• S
    ActiveRecord#respond_to? No longer allocates strings · f45267bc
    schneems 提交于
    This is an alternative to https://github.com/rails/rails/pull/34195
    
    The active record `respond_to?` method needs to do two things if `super` does not say that the method exists. It has to see if the "name" being passed in represents a column in the table. If it does then it needs to pass it to `has_attribute?` to see if the key exists in the current object. The reason why this is slow is that `has_attribute?` needs a string and most (almost all) objects passed in are symbols.
    
    The only time we need to allocate a string in this method is if the column does exist in the database, and since these are a limited number of strings (since column names are a finite set) then we can pre-generate all of them and use the same string. 
    
    We generate a list hash of column names and convert them to symbols, and store the value as the string name. This allows us to both check if the "name" exists as a column, but also provides us with a string object we can use for the `has_attribute?` call. 
    
    I then ran the test suite and found there was only one case where we're intentionally passing in a string and changed it to a symbol. (However there are tests where we are using a string key, but they don't ship with rails).
    
    As re-written this method should never allocate unless the user passes in a string key, which is fairly uncommon with `respond_to?`.
    
    This also eliminates the need to special case every common item that might come through the method via the `case` that was originally added in https://github.com/rails/rails/commit/f80aa5994603e684e3fecd3f53bfbf242c73a107 (by me) and then with an attempt to extend in https://github.com/rails/rails/pull/34195.
    
    As a bonus this reduces 6,300 comparisons (in the CodeTriage app homepage) to 450 as we also no longer need to loop through the column array to check for an `include?`.
    f45267bc
attribute_methods.rb 16.4 KB