Stange random crashes with Rails 3.1 in production Stange random crashes with Rails 3.1 in production ruby-on-rails ruby-on-rails

Stange random crashes with Rails 3.1 in production


solution: if your validation line says this ...

validates :tag, :uniqueness => {:scope => :post}

... then change it to reference column name instead of association name like this ...

validates :tag_id, :uniqueness => {:scope => :post_id}

I recreated your failure in rails 3.1.0.rc5 and produced the same error message. The short explanation is that the code in the build_relation method is expecting a literal column name, not an association name.

For a more thorough explanation including code snippets see my blog at http://thetenelements.blogspot.com/2011/08/undefined-method-text-for-nilnilclass.html


Luke Griffiths has given you most of the answer. The essence is this, you have a uniqueness validation somewhere. According to your comment the only one you have is this one:

validates_uniqueness_of :body, :scope => [:commentable_id, :user_id], :message => "Duplicate record.."

If that is the case, then we must assume it is the culprit (it would help to know which model that validation was, also would help to see the relevant parts of your User model code as well as the relevant parts of the model where that validation is found). But no matter we can still have a guess as to what your problem is.

The issue you're getting occurs in the build_relation method in the rails source rails/activerecord/lib/active_record/validations/uniqueness.rb on line 57. This method is trying to look up a column based on the name of an attribute and then tries to see if it is text. The errors occurs because the attribute it is getting does not seem to be in the column hash to the value of column is nil.

It has been suggested in another answer that you output some debugging info in the build_relation method. This is not a bad idea (all you really need to know is what are the keys in the columns_hash and what the the value of the attribute that is passed in to the method), here is how you do that. Create an initializer in config/initializers, lets call it debugging_stuff.rb. Inside there you can put the following:

ActiveRecord::Validations::UniquenessValidator.class_eval do  def build_relation(klass, table, attribute, value) #:nodoc:    puts "************** #{attribute}"    puts "************** #{klass.columns_hash.keys.inspect}"    column = klass.columns_hash[attribute.to_s]    value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if column.text?    if !options[:case_sensitive] && value && column.text?      # will use SQL LOWER function before comparison      relation = table[attribute].lower.eq(table.lower(value))    else      value    = klass.connection.case_sensitive_modifier(value)      relation = table[attribute].eq(value)    end    relation  endend

What we're looking for is to find out when none of the keys in the columns_hash match the attribute value that is passed.

If that uniqueness validator above is really the only one you have, it should be one of the three attributes in there (:body, :commentable_id, :user_id). My money is on :body but I could be wrong. Does :body refer to another model. If it does perhaps it should be :body_id as Luke Griffiths indicated. If you still can't find the issue, you would need to provide more debugging info like, full stack trace and code for the relevant models (User etc.) - we've gone as far as we can with the information we have.


If you're still unable to resolve this add some debugging to the build_relation method yourself. Just let it write some data to the console to see where it actually goes wrong.

Change

def build_relation(klass, table, attribute, value) #:nodoc:  column = klass.columns_hash[attribute.to_s]  value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if column.text?

to

def build_relation(klass, table, attribute, value) #:nodoc:  puts "build_relation klass: #{klass.inspect}"  puts "build_relation table: #{table.inspect}"  puts "build_relation attribute: #{attribute.inspect}"  puts "build_relation value: #{value.inspect}"  puts "build_relation columns_hash: #{klass.columns_hash.inspect}"  column = klass.columns_hash[attribute.to_s]  value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if column.text?

Then you'll at least know what attribute is causing this. Now hope it's the same everytime it goes wrong...