#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
через as11.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})?$/