Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails / ActiveRecord: field normalization

I'm trying to remove the commas from a field in a model. I want the user to type a number, i.e. 10,000 and that number should be stored in the database as 10000. I was hoping that I could do some model-side normalization to remove the comma. I don't want to depend on the view or controller to properly format my data.

I tried:

before_validation :normalize

def normalize 
 self['thenumber'] = self['thenumber'].to_s.gsub(',','')
end

no worky.

like image 650
Bill Avatar asked Dec 10 '22 20:12

Bill


2 Answers

http://github.com/mdeering/attribute_normalizer looks like a promising solution to this common problem. Here are a few examples from the home page:

  # By default it will strip leading and trailing whitespace
  # and set to nil if blank.
  normalize_attributes :author, :publisher

  # Using one of our predefined normalizers.
  normalize_attribute  :price, :with => :currency

  # You can also define your normalization block inline.
  normalize_attribute :title do |value|
    value.is_a?(String) ? value.titleize.strip : value
  end

So in your case you might do something like this:

  normalize_attribute :title do |value|
    value.to_s.gsub(',', '')
  end
like image 180
Tyler Rick Avatar answered Dec 30 '22 08:12

Tyler Rick


I think you're doing it right. This test passes:

test "should remove commas from thenumber" do
  f = Foo.new(:thenumber => "10,000")
  f.save
  f = Foo.find(f.id)
  assert f.thenumber == "10000"    
end

And I used your code.

 class Foo < ActiveRecord::Base
  before_validation :normalize

  def normalize 
    self['thenumber'] = self['thenumber'].to_s.gsub(',','')
  end

 end

Now, my schema is set up for thenumber to be a string though, not an integer.

Started
.
Finished in 0.049666 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

If you wanted to store this in the db as an integer, then you definitely need to override the setter:

 def thenumber=(value)
   self['thenumber'] = value.to_s.gsub(',','').to_i
 end

If you do it your way, with an integer column, it gets truncated by AR....

>> f.thenumber = "10,000"
=> "10,000"
>> f.thenumber
=> 10

That's a little-known thing with Ruby and integers... it auto-casts by truncating anything that's no longer an integer.

irb(main):004:0> i = "155-brian-hogan".to_i
=> 155

Can be cool for things like

/users/155-brian-hogan

@user = User.find_by_id(params[:id])

But not so cool for what you're doing.

So either change the col to a string and use the filter, or change the setter :)

Good luck!

like image 39
Brian Hogan Avatar answered Dec 30 '22 08:12

Brian Hogan