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:

http://ruby-doc.org/core/classes/String.html#M000817