Forum: Ruby on Rails Setter that converts a float attribute to integer

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
F10c5ded4c3f1dffe043cad7d0b9db47?d=identicon&s=25 Carlos Paramio (Guest)
on 2006-06-07 16:43
(Received via mailing list)
Hi,

I have some problems with an application where I'm using custom
accessors to do currency conversions. In my model, I have a price
attribute in the database that stores the value in cents, to avoid
future problems with float arithmetic and round. But at the views, I
would like to show the price in euros, with decimal for the cents. So
I defined a new attribute called price_in_euros, and the corresponding
accesors:

class Report < ActiveRecord::Base
       validates_numericality_of :price, :only_integer => true

       attr_accessor :price_in_euros

       def price_in_euros
               if self.price
                       self.price / 100.0
               else
                       # default price is 1 euro
                       1
               end
       end

       def price_in_euros=(euros)
               self.price = (euros * 100).to_i
       end
end


If I test this model with the rails console, all is working fine:

$ script/console
Loading development environment.
>> r = Report.new
=> #<Report:0xb74a53c0 @new_record=true, @attributes={"precio"=>nil}>
>> r.price_in_euros = 2.45
=> 2.45
>> r.save
=> true
>> r
=> #<Report:0xb74a53c0 @new_record_before_save=true,
@new_record=false, @errors=#<ActiveRecord::Errors:0xb7813264
@errors={}, @base=#<Report:0xb74a53c0 ...>>, @attributes={"id"=>20,
"price"=>245}>


The attribute "price" has been set to 245 cents. Ok, let's check the
database:

mysql> select id, price from reports;
+----+-------+
| id | price |
+----+-------+
| 20 |   245 |
+----+-------+
1 row in set (0.00 sec)


It seems that all is ok. Well, let's prepare a controller with a
couple of actions, insert and edit:

class ReportController < ApplicationController
       def insert
               @report = Report.new(params[:report])
               if @request.post? and @report.save
                       redirect_to :action => 'list'
               end
       end

       def edit
               @report = Report.find(params[:id])
               @report.attributes = params[:report]
               if @request.post? and @report.save
                       redirect_to :action => 'list'
               end
       end
end


Here is part of the view for the insert action:

<% form_for :report, :url => { :action => 'insert' } do |form| %>
Price: <%= form.text_field :price_in_euros -%>
<%= submit_tag 'Insert' -%>


Here is part of the view for the edit action:

<% form_for :report, :url => { :action => 'edit' } do |form| %>
       Price: <%= form.text_field :price_in_euros -%>
<%= submit_tag 'Save' -%>


Ok. Now I use that views to insert a couple of new reports, giving a
value of 2.45 for the first one and 1 for the second one. Here is what
it appears at the database:

mysql> select id,price from reports;
+----+------------+
| id | price      |
+----+------------+
| 24 |          2 |
| 25 | 2147483647 |
+----+------------+
2 rows in set (0.00 sec)


Ouch! That's really strange.

And if I try to edit them, I can see a value of 0.02 on the price
field for the first one, and 21474836.47 for the second one (the price
at the database divided by 100), so the getter accesor seems to be
working well, but not the setter. If I click 'Save' on the edit form,
I then get:

mysql> select id,price from reports;
+----+----------+
| id | price    |
+----+----------+
| 24 |        0 |
| 25 | 21474836 |
+----+----------+
2 rows in set (0.00 sec)

What in the hell I'm doing bad? Thanks in advance.
--
Carlos Paramio
http://www.sinfoniadebits.com/
A2c85dc5ee81b12e3cc0a6522e8d079d?d=identicon&s=25 Chris Hall (Guest)
on 2006-06-08 14:37
(Received via mailing list)
to see why this is happening you have to know that the data you are
submitting from the form is text, so Ruby/Rails is going to see it as a
string...

irb example

irb(main):008:0> ("2.45"*100).to_i
=> 2
irb(main):009:0> ("1"*100).to_i
=>
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

now, if we convert the strings to floats first

irb(main):011:0> ("2.45".to_f*100.0).to_i
=> 245
irb(main):012:0> ("1".to_f*100.0).to_i
=> 100

then you will see the data as you expect it to be.  your tests worked
because you used floats/ints, not strings.

so change your method to:

def price_in_euros=(euros)
  self.price = (euros.to_f * 100.0).to_i
end

and give it another go.
This topic is locked and can not be replied to.