One-time script to update 1000s of values in a single column in a table: nil error?

Hi, I have a model called Stock with a value called "strength." I wrote a simple method in the Stock.rb model file to update the value of "strength" for each record in the stocks table. I created a controller called fix_controller.rb. I don't have access to the live system, so the idea is that an admin will go to http://url/fix and a script will run to check and potentially update the "strength" value for every record in the stocks table.

fix_controller.rb ^^^^^^^^^^^^^^^ class FixController < ApplicationController

  def index     stocks = Stock.find(:all)

    stocks.each do |s|         if s.strength.nil? || s.strength == ''           puts 'nothing to update'         else           s.update_units         end     end   end end

Stock.rb ^^^^^^^^^ def update_units     if self.strength == '' || self.strength.nil?    puts 'nil - cannot update' elsif self.strength != '' && self.strength != nil self.strength = self.strength.gsub!(' ml', 'ml') if self.strength.gsub!(' ml', 'ml') != nil self.strength = self.strength.gsub!(' mL', 'ml') if self.strength.gsub!(' mL', 'ml') != nil self.strength = self.strength.gsub!(' g', 'g') if self.strength.gsub! (' g', 'g') != nil self.strength = self.strength.gsub!(' oz', 'oz') if self.strength.gsub!(' oz', 'oz') != nil self.strength = self.strength.gsub!(' mg', 'mg') if self.strength.gsub!(' mg', 'mg') != nil end

When I try to run this, I always get the error:

NoMethodError: private method `gsub' called for nil:NilClass

I'm sure this is a simple error and/or I'm not going about this the correct way. I really only need to run this code once... Any idea why I'm getting that error? I'm checking for nil...

it looks like you are trying to strip a leading space from each "strength" value if its not nil.

something like

stocks = Stock.find(:all, :conditions => '(strength IS NOT NULL) AND (strength != "")')

would give you all the stocks values with non-blank values. So you can skip a lot of your testing above.

You could then do something like

stocks.each do |stock|    stock.strength = stock.strength.to_s.gsub(/^\s+/,'')    stock.save end

the above is using a regular expression to match the leading spaces. the to_s shouldn't be necessary, because you should only be dealing with non-nil strength values at this point, but it's defensive coding.

rails has a string extension .blank? which returns true for empty or nil or spaces values, e.g.

nil.blank? .blank? " ".blank? ''.blank?

all return true

Stock.rb ^^^^^^^^^ self.strength = self.strength.gsub!(' ml', 'ml') if

The "gsub bang" method returns nil if no changes are made: it's a method for changing string variables in place, not really for assigning the return value (unless you're using the return value to check whether any replacements were made.

Change to:

As you only need to run this once it might be better as a database migration

If you leave it as an action it should really be a POST. Otherwise google may come along and 'fix' it for you while scanning your site.

Colin

What exactly does the "...in place" part of that mean?

If you assign a value to a variable:

   my_variable = "100 mg" you can alter the variable using the bang method without using an = operator to assign so :    my_variable = my_variable.gsub(' mg', 'mg') is the same as:    my_variable.gsub!(' mg', 'mg') but beware of:    my_variable = my_variable.gsub!(' mg', 'mg') as if no substitution is made, the new value of my_variable will be "nil" (as you discovered :wink: