Ruby Regex для определения цены

#ruby-on-rails #ruby #regex #ruby-on-rails-3.1

#ruby-on-rails #ruby #регулярное выражение #ruby-on-rails-3.1

Вопрос:

Это не выполняется, когда в конце находится ноль

12.12 проходит, 5.51 проходит, 12.50 терпит неудачу, 12.60 терпит неудачу

  price_regex = /^d (.d{2})?$/
  

почему? и как мне это исправить?

Еще немного информации

в _form.html.erb

   <p>
    <%= f.label :price %><br />
    <%= f.text_field :price %>
  </p>
  

в menu_item.rb

   price_regex = /^d (.d{2})?$/
  validates :price, :presence => true,
                :format => {  :with => price_regex  }
  

в menu_items_controller.rb

   def create
    @menu_item = MenuItem.new(params[:menu_item])

    if @menu_item.save
     respond_with @menu_item, :location => menu_items_url
   else
     flash[:notice] = "Not Saved"
       end
   end
  

цена — это десятичное число в базе данных с точностью до 2.

Комментарии:

1. '12.50' отлично соответствует этому регулярному выражению, вы уверены, что у вас не n = 12.50; s = n.to_s что-то происходит?

2. Я добавил к своему вопросу дополнительную информацию. это работает на rubular, но по какой-то причине оно отключается, когда я отправляю форму

Ответ №1:

Вы говорите, что price это «десятичное число в базе данных с точностью до 2». Это означает, что price в Ruby оно представлено в виде десятичного числа большого размера, и проверка регулярного выражения будет выполняться в строковой форме этого десятичного числа большого размера. Небольшое экспериментирование прояснит ситуацию:

 > p = BigDecimal.new('12.50')
 => #<BigDecimal:12a579e98,'0.125E2',18(18)> 
> p.to_s
 => "12.5" 
  

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

 /^d (.d{1,2})?$/
  

Комментарии:

1. Если я не хочу использовать регулярное выражение, каков наилучший способ проверить вводимые пользователем данные, ограничив их двумя знаками после запятой?

2. @ctilley79: Если бы p это была цена, вы могли бы проверить (p*100).floor == p*100 , это все равно пропускало бы 11.500 через as 11.5 , но я бы беспокоился об этом не больше, чем о том, что 11 после десятичной дроби нет ровно двух цифр.

Ответ №2:

Я использую Rails 3 с драгоценным камнем client_side_validations, что означает, что мне нужно регулярное выражение, которое работает как в Ruby, так и в Javascript. У меня также есть четкое разграничение между форматом интерфейса и бэкэнда — пользователь никогда не должен иметь возможности вводить «$ 12.5», но как только он попадает на сервер, меня не волнует значение 0 в конце.

Моим решением было добавить расширение core (в моем случае для Float, но BigDecimal, вероятно, был бы более подходящим в большинстве случаев):

 class Float
  def can_convert_to_i_with_no_loss_of_precision
    (self % 1).zero?
  end
  alias_method :to_s_with_loss_of_trailing_zeroes, :to_s
  def to_s
    if can_convert_to_i_with_no_loss_of_precision
      to_i.to_s
    else
      "#{to_s_with_loss_of_trailing_zeroes}#{0 if (self * 10 % 1).zero?}"
    end
  end
end
  

Теперь я могу использовать это в модели, и это прекрасно работает во внешнем интерфейсе (Javascript не преобразует его в значение с плавающей запятой, поэтому пользователь всегда будет вынужден вводить 2 цифры после десятичной дроби) и во внутреннем интерфейсе (где будет вызван FormatValidator ActiveModel to_s , и расширение ядра будет знать, когда добавить завершающий 0):

 validates :price, :format => { :with => /^d (.d{2})?$/, :allow_blank => true }
  

Ответ №3:

На мой взгляд, регулярное выражение выглядит нормально. Я протестировал его в Rubular с упомянутыми вами входными данными и еще несколькими, и он правильно фиксирует их все.

Проблема, вероятно, в какой-то другой части кода. Возможно, вы используете price = <regex> , в то время как вы должны использовать price =~ <regex> для сопоставления строки с регулярным выражением.

Комментарии:

1. Я получаю довольно неприятную неопределенную локальную переменную, когда использую price_regex = ~ /^ d (.d{2})?$/