5

I have a User model with Profit field. Profit field is a DECIMAL (11,0) type. I have a masked input on the form which allows user to input something like $1,000. I want to format that value and remove everything except numbers from it so i will have 1000 saved. Here is what i have so far:

class User < ActiveRecord::Base
  before_save :format_values

  private

  def format_values
    self.profit.to_s.delete!('^0-9') unless self.profit.nil?
  end
end

But it keeps saving 0 in database. Looks like it is converting it to decimal before my formatting function.

0

6 Answers 6

8

Try this:

def profit=(new_profit)
  self[:profit] = new_profit.gsub(/[^0-9]/, '')
end
Sign up to request clarification or add additional context in comments.

2 Comments

It just mimics the author's RegExp. Trying not to scary him )
thanks @jdoe i've used your suggestion and it works like i want it.
6

First of all, this:

def format_values
  self.profit.to_s.delete!('^0-9') unless self.profit.nil?
end

is pretty much the same as this:

def format_values
    return if(self.profit.nil?)
    p = self.profit
    s = p.to_s
    s.delete!('^0-9')
end

So there's no reason to expect your format_values method to have any effect whatsoever on self.profit.

You could of course change format_values to assign the processed string to self.profit but that won't help because your cleansing logic is in the wrong place and it will be executed after '$1,000' has been turned into a zero.

When you assign a value to a property, ActiveRecord will apply some type conversions along the way. What happens when you try to convert '$1,000' to a number? You get zero of course. You can watch this happening if you play around in the console:

> a = M.find(id)
> puts a.some_number
11
> a.some_number = 'pancakes'
 => "pancakes"
> puts a.some_number
0
> a.some_number = '$1,000'
 => "1,000"
> puts a.some_number
0
> a.some_number = '1000'
 => "1000"
> puts a.some_number
1000

So, your data cleanup has to take place before the data goes into the model instance because as soon as AR gets its hands on the value, your '$1,000' will become 0 and all is lost. I'd put the logic in the controller, the controller's job is to mediate between the outside world and the models and data formatting and mangling certainly counts as mediation. So you could have something like this in your controller:

def some_controller
    fix_numbers_in(:profit)
    # assign from params as usual...
end

private

def fix_numbers_in(*which)
    which.select { |p| params.has_key?(p) }.each do |p|
        params[p] = params[p].gsub(/\D/, '') # Or whatever works for you
    end
end

Then everything would be clean before ActiveRecord gets its grubby little hands on your data and makes a mess of things.

You could do similar things by overriding the profit= method in your model but that's really not the model's job.

2 Comments

i know i can do that in controller, but i was hoping to automate that process before saving in model itself, since i am calling it from different places. Any ideas?
thanks for the informative answer btw, and a catch with to_s method in a wrong place.
1
class User < ActiveRecord::Base
  before_save :format_values

  private

  def format_values
    self.profit = profit.to_s.gsub(/\D/,'') if profit
  end
end

Comments

0
  def format_values
    self.profit.to_d!
  end

Comments

0

I recommend you to write custom setter for this particular instance variable @profit:

class User
  attr_accessor :profit

  def profit= value    
    @profit = value.gsub(/\D/,'')
  end
end

u = User.new
u.profit = "$1,000"
p u.profit # => "1000"

4 Comments

I guess u.profit returns the instance variable's value. It's a pitfall what you made!
What do you mean? attr_accessor defines two methods - getter and setter, profit method returns the instance variable's value, that right) Where is a pitfall?)
Your User has nothing to do with ActiveRecord::Base. And event if it had, the @pofit variable would have nothing to do with profit DB attribute. After inheriting your version of User from ActiveRecord::Base you should have replaced @profit = with self.profit =. Otherwise you wold mask the profit attribute.
Redefining setter is about Ruby classes, inherited from ActiveRecord::Base or not. But I think you are right and I should gave an example about Rails, thanks for a good advice)
0

I would suggest using the rails helper of number with precision. Below is some code.

Generic Example:

number_with_precision(111.2345, :precision => 1, :significant => true)     # => 100

Rails code Example:

def profit=(new_profit)
  number_with_precision(self[:profit], :precision => 1, :significant => true)
end

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.