Cleber Reizen
Cleber Reizen

Reputation: 475

before_save rails 5 not working as expected

I have a before_save in my model to covert comma to dot that was working fine. But somehow it broke. Example: user input 1,22 and the value get stored without decimal like 1.00

I need to store the value as a decimal, even if the user type comma instead of dot. Because where I live comma is the delimiter.

before_save { self.price.to_s.gsub(',', '.').to_f }

I also tried change to this:

before_save :convert_comma

private

  def convert_comma
      self.price = self.price.to_s.gsub(',', '.').to_f 
  end

I tested the comma replacement above in the console and it works, but with before_save the data get stored rounded like 1.00

Rails console:

2.3.4 :001 > p = Ticket.new("driver_id"=>"3", "origin_id"=>"AP", "origin_city_id"=>"Amapá", "destination_id"=>"GO", "destination_city_id"=>"Abadia de Goiás", "start_date"=>"29/12/2017 19:28", "return_date"=>"29/12/2017 19:28", "annotation"=>"1", "rate"=>"1", "price"=>"1233,22", "status"=>"Aberto") => # <Ticket id: nil, start_date: "2017-12-29 19:28:00", return_date: "2017-12-29 19:28:00", rate: #<BigDecimal:68e22a8,'0.1E1',9(18)>, price: #<BigDecimal:68e21e0,'0.1233E4',9(18)>, annotation: "1", status: "Aberto", department_id: nil, origin_id: "AP", destination_id: "GO", driver_id: 3, created_at: nil, updated_at: nil, origin_city_id: "Amapá", destination_city_id: "Abadia de Goiás", manager: nil, day: nil, user_id: nil, deleted_at: nil>

2.3.4 :002 > p.save (0.2ms) SAVEPOINT active_record_1 SQL (0.6ms) INSERT INTO "tickets" ("start_date", "return_date", "rate", "price", "annotation", "status", "origin_id", "destination_id", "driver_id", "created_at", "updated_at", "origin_city_id", "destination_city_id") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [["start_date", "2017-12-29 19:28:00"], ["return_date", "2017-12-29 19:28:00"], ["rate", 1.0], ["price", 1233.0], ["annotation", "1"], ["status", "Aberto"], ["origin_id", "AP"], ["destination_id", "GO"], ["driver_id", 3], ["created_at", "2017-12-29 21:55:22.119679"], ["updated_at", "2017-12-29 21:55:22.119679"], ["origin_city_id", "Amapá"], ["destination_city_id", "Abadia de Goiás"]] (0.1ms) RELEASE SAVEPOINT active_record_1 => true

This is really strange. Any idea of what could be happening? Thanks!

Upvotes: 1

Views: 470

Answers (3)

Francisco
Francisco

Reputation: 157

The problem here is that active record is coercing the string into a BigDecimal as soon as you write the attribute in the initializer (Ticket.new). Notice that, in your console output, your instance holds a BigDecimal for the price attribute with the value '0.1233E4', rather than '0.123322e4'. This means that when your :convert_comma method runs:

self.price.to_s #=> '1233.0'

One possible solution is to override your attribute writer in your model, writing the following instance method in your ticket.rb file:

def price=(new_price)
  super(BigDecimal.new(new_price.to_s.gsub(',', '.')))
end

Upvotes: 2

Cleber Reizen
Cleber Reizen

Reputation: 475

I have found a workaround for that here https://stackoverflow.com/a/11585385/2188585 and modified to this:

before_save :pricecomma

def pricecomma
  price     
end

def pricecomma=(price)
  self.price = price.gsub(',', '.') unless price.blank?
end

Changed my form to:

<%= f.text_field :pricecomma %>

Still don't get what is going on. I tried the same code without this virtual attribute and don't get the replacement. But this way works fine.

Upvotes: 1

Pietro Allievi
Pietro Allievi

Reputation: 436

I'm not sure to have understood your problem, I think the error is trying to convert ',' in '.' on a number type. You can change the way it's print using NumberHelper methods in your views, for example:

<%= number_with_delimiter(@product.price, delimiter: ",", separator: ".") %>

delimiter is for thousands and separator for decimals, in case of currency maybe its better number_to_currency method

<%= number_to_currency(@product.price, delimiter: ".", separator: ",", unit: "€") %>

EDIT a link to ActionView::Helpers::NumberHelper references

EDIT AGAIN Since you need to allow user to digits comma as delimiter, I suggest you to look for or write by yourself a 18n file to put in your config/locales folder, for Brazil it should be br.yml. At the voice "number:" you can set currency and format, and set delimiter and separator. To set this file by default, on your config/application.rb add this line:

module YourApp
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.
    config.i18n.default_locale = :br
  end
end

then your number_field need to have correct step option:

<%= f.number_field :price, step: 0.01 %>

That should works and also allows you to translate your system messages :)

Upvotes: 1

Related Questions