Десятичные числа и запятые при вводе числа в форму Ruby on Rails

Какой лучший способ Ruby/Rails позволяет пользователям использовать десятичные знаки или запятые при вводе числа в форму? Другими словами, я хотел бы, чтобы пользователь мог ввести 2000.99 и не получить 2.00 в моей базе данных.

Есть ли наилучшая практика для этого?

- Обновить ---

Работает ли gsub с поплавками или bigintegers? Или рельсы автоматически вырезают номер при входе в форму float или ints? Я попытался использовать self.price.gsub( ",", "), но получим метод undefined `gsub 'для 8: Fixnum", где 8 - это номер, который я ввел в форму.

Ответ 1

У меня была аналогичная проблема, пытаясь использовать локализованный контент внутри форм. Вывод локализации относительно прост с использованием встроенных методов ActionView::Helpers::NumberHelper, но анализ локализованного ввода не поддерживается ActiveRecord.

Это мое решение, пожалуйста, скажите мне, что я делаю что-то неправильно. Мне кажется слишком простым, чтобы быть правильным решением. Благодарю!:)

Прежде всего, добавьте метод String.

class String
  def to_delocalized_decimal
    delimiter = I18n::t('number.format.delimiter')
    separator = I18n::t('number.format.separator')
    self.gsub(/[#{delimiter}#{separator}]/, delimiter => '', separator => '.')
  end
end

Затем добавьте метод класса в ActiveRecord::Base

class ActiveRecord::Base
  def self.attr_localized(*fields)
    fields.each do |field|
      define_method("#{field}=") do |value|
        self[field] = value.is_a?(String) ? value.to_delocalized_decimal : value
      end
    end
  end
end

Наконец, пусть объявить, какие поля должны иметь локализованный вход.

class Article < ActiveRecord::Base
  attr_localized :price
end

Теперь в вашей форме вы можете ввести "1.936,27", а ActiveRecord не будет вызывать ошибки при недопустимом числе, потому что он станет 1936.27.

Ответ 2

Вот несколько кодов, которые я скопировал у Грега Брауна (автора Ruby Best Practices) несколько лет назад. В вашей модели вы определяете, какие предметы "очеловечены".

class LineItem < ActiveRecord::Base
  humanized_integer_accessor :quantity
  humanized_money_accessor :price
end

В ваших шаблонах просмотров вам нужно ссылаться на гуманизированные поля:

= form_for @line_item do |f|
  Price:
  = f.text_field :price_humanized

Это определяется следующим образом:

class ActiveRecord::Base
  def self.humanized_integer_accessor(*fields)
    fields.each do |f|
      define_method("#{f}_humanized") do
        val = read_attribute(f)
        val ? val.to_i.with_commas : nil
      end
      define_method("#{f}_humanized=") do |e|
        write_attribute(f,e.to_s.delete(","))
      end
    end
  end

  def self.humanized_float_accessor(*fields)
    fields.each do |f|
      define_method("#{f}_humanized") do
        val = read_attribute(f)
        val ? val.to_f.with_commas : nil
      end
      define_method("#{f}_humanized=") do |e|
        write_attribute(f,e.to_s.delete(","))
      end
    end
  end

  def self.humanized_money_accessor(*fields)
    fields.each do |f|
      define_method("#{f}_humanized") do
        val = read_attribute(f)
        val ? ("$" + val.to_f.with_commas) : nil
      end
      define_method("#{f}_humanized=") do |e|
        write_attribute(f,e.to_s.delete(",$"))
      end
    end
  end
end

Ответ 3

Вы можете попытаться удалить запятые before_validation или before_save

К сожалению, вы хотите сделать это в текстовом поле до его преобразования. Вы можете использовать виртуальный атрибут:

def price=(price)
   price = price.gsub(",", "")
   self[:price] = price  # or perhaps price.to_f
end

Ответ 4

Мне не удалось реализовать более раннее предложение def price=(price) виртуального атрибута, потому что метод, похоже, называет себя рекурсивно.

Я закончил удаление запятой из хэша атрибутов, так как вы подозреваете, что ActiveRecord, кажется, усекает ввод с запятыми, которые попадают в поля DECIMAL.

В моей модели:

before_validation :remove_comma

def remove_comma
  @attributes["current_balance"].gsub!(',', '')  # current_balance here corresponds to the text field input in the form view

  logger.debug "WAS COMMA REMOVED? ==> #{self.current_balance}"
end

Ответ 5

Здесь что-то простое, что позволяет правильно считывать ввод номера. Выход будет по-прежнему с точкой вместо запятой. Это не красиво, но по крайней мере не критично в некоторых случаях.

Для этого требуется один вызов метода в контроллере, где вы хотите включить разделитель запятой. Может быть, не идеальный с точки зрения MVC, но довольно простой, например:

class ProductsController < ApplicationController

  def create
    # correct the comma separation:
    allow_comma(params[:product][:gross_price])

    @product = Product.new(params[:product])

    if @product.save
      redirect_to @product, :notice => 'Product was successfully created.'
    else
      render :action => "new"
    end
  end

end

Идея состоит в том, чтобы изменить строку параметров, например:

class ApplicationController < ActionController::Base

  def allow_comma(number_string)
    number_string.sub!(".", "").sub!(",", ".")
  end

end

Ответ 6

Взгляните на i18n_alchemy для анализа и локализации даты и числа.

I18nAlchemy направлена ​​на обработку синтаксиса даты, времени и числа на основе текущего формата I18n. Основная идея состоит в том, чтобы ORM, например ActiveRecord, автоматически принимать даты/числа, указанные в текущем формате локали, и также возвращать эти значения.