Alex Rivera | Logout

Rails before_validation strip whitespace best practices

Asked 2010-07-16T19:05:31.367
78

I would like my User model to sanitize some input before before save. For now some simple whitespace stripping will do. So to avoid people registering with "Harry " and pretend to be "Harry", for example.

I assume it is a good idea to do this stripping before validation, so that the validates_uniqueness_of can avoid accidental duplicates.

class User < ActiveRecord::Base
  has_many :open_ids

  validates_presence_of :name
  validates_presence_of :email
  validates_uniqueness_of :name
  validates_uniqueness_of :email
  validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i

  before_validation :strip_whitespace, :only => [:name, :email, :nick]

  private
  def strip_whitespace(value)
    value.responds_to?('strip') ? value.strip : value
  end
end

However, this code comes with an error ArgumentError: wrong number of arguments (0 for 1). I assumed the callback would be passed the values.

Also: is this stripping actually a good idea? Or should I rather validate on space and tell the user that "Harry " contains invalid spacess (I want to allow "Harry Potter" but not "Harry\s\sPotter").

Edit: As pointed out in a comment, my code is wrong (which is why I was asking the question a.o.). Please make sure you read the accepted answer in addition to my question for the correct code and to avoid the same mistakes I made.

Edit
Report

2 Answers

30

Charlie's answer is good, but there's a little verbosity. Here's a tighter version:

def clean_data
  # trim whitespace from beginning and end of string attributes
  attribute_names.each do |name|
    if send(name).respond_to?(:strip)
      send("#{name}=", send(name).strip)
    end
  end
end

The reason we use

self.foo = "bar"

instead of

foo = "bar"

in the context of ActiveRecord objects is that Ruby interprets the latter as a local variable assignment. It will just set the foo variable in your method scope, instead of calling the "foo=" method of your object.

But if you are calling a method, there is no ambiguity. The interpreter knows you're not referring to a local variable called foo because there is none. So for example with:

self.foo = foo + 1

you need to use "self" for the assignment, but not to read the current value.

answered 2012-02-05T20:28:50.700
2

Since I can't comment yet, I'll have to ask here: which method is giving the ArgumentError? strip, or responds_to?

Also, .strip removes only leading and trailing whitespace. If you want "Harry Potter" with two spaces to not be accepted, you would either have to use a regex or, more simply, you could call .split, which removes spaces, and re-concatenate the string with a single space.

As far as if stripping is a good idea, I don't see a problem when it is just the leading/trailing whitespace. If there are multiple spaces in between words though, I would notify the user instead of automatically removing the extra spaces and giving the user a login that is not what they submitted.

answered 2010-07-16T19:20:03.407

Your Answer