Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: ensure capitalization in model?

In a Rails model, if I have a field that is for proper names, what is the best practice way to ensure they are uniformly capitalized despite potentially lazy input from users?

IE let's say the model is Contact and the field is Name. No matter what the user inputs I want the words to be capitalized, ie the model would tranform the following input to the following output:

john doe -> John Doe
j doe -> J Doe
John doe -> John Doe
john doe jr -> John Doe Jr

So, do you create a before_save callback and tranform the field with a regex, or do you create some kind of validation, or something different? I'd very much appreciate answers with an emphasis on the rationale, of why you would do it one way or another, because that's what I'm most stuck on.

like image 389
Andrew Avatar asked Apr 18 '12 13:04

Andrew


4 Answers

The method you want is titleize. The capitalize method only does the first character in the entire string so "john doe".capitalize #=> "John doe", "john doe".titleize #=> "John Doe".

As for how to implement it, you could go with a before_save, but I like to do these type of simple preparations of single attributes with pseudo-attributes. Basically, you define a writer method for an attribute that will hide the default one created by ActiveRecord. You'll still be able to access the attribute using the write_attribute method (docs). Doing it this way will ensure that the attribute is set properly as soon as you assign it, meaning that even before you save it, you can be sure your object is in a valid state:

class Contact < ActiveRecord::Base

  def name=(s)
    write_attribute(:name, s.to_s.titleize) # The to_s is in case you get nil/non-string
  end

end
like image 186
tsherif Avatar answered Nov 06 '22 15:11

tsherif


I would suggest to use before_save .

like in you model :

before_save :capitalize_name

def capitalize_name
  self.name = self.name.split.collect(&:capitalize).join(' ') if self.name && !self.name.blank?
end
like image 21
Vik Avatar answered Nov 06 '22 17:11

Vik


A cleaner rendition of @tserif's is the following:

def name=(s)
    super s.titleize
end

This removes the dependency on write_attribute and the need to specify the attribute name name in favor of a more primitive method. I don't see the need for to_s, unless you want the ability to assign a symbol.

like image 8
YWCA Hello Avatar answered Nov 06 '22 16:11

YWCA Hello


The most important question is how will you be displaying the names? If it will be on the web, as I would guess, I wouldn't handle this in the model at all. The simplest solution is to just use css text-tansform: capitalize; to handle the capitalization, but if you'll be displaying it often you would want to define a decorator or helper method to normalize it for you.

def full_name
  [first_name, last_name].map { |name| name[0].upcase + name[1..-1] }.join(' ')
end

Note that instead of mapping capitalize you would just want to uppercase the first letter. Somebody named RJ or CJ would probably want the J to remain capitalized.

The data is canonical regardless, seeing the parts capitalized is purely display.

like image 3
Parker Selbert Avatar answered Nov 06 '22 17:11

Parker Selbert