The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Валидация многопараметровых значений

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Maxim Kulkin

Posts: 58
Nickname: hapk
Registered: Sep, 2006

Maxim Kulkin is developer in Selectosa Systems.
Валидация многопараметровых значений Posted: Jun 26, 2007 12:04 AM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by Maxim Kulkin.
Original Post: Валидация многопараметровых значений
Feed Title: Software development
Feed URL: http://maximkulkin.blogspot.com/feeds/posts/full?alt=rss
Feed Description: Software development
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Maxim Kulkin
Latest Posts From Software development

Advertisement
Работал над тем, чтобы добавить нормальную обработку неправильных значений для дат. У меня одно из требований заказчика - чтобы даты можно было вводить и изменять с клавиатуры. При этом вводить день и особенно год проще текстом, нежели выбирать их из списка. Поэтому элементы для редактирования дат у меня выглядят как текстбокс для дня, список для месяца и текстбокс для года.

Далее, хочется, чтобы пустые значения в любом из тех текст боксов отрабатывались правильно. Как известно, внутри рельсов даты являются многопараметровым (multiparameter) значением (т.е. оно складывается из значений нескольких элементов на форме), и логика преобразования типов там простая: позвать to_i для каждого значения и скормить получившиеся значения в Date.new... При этом (!) там стоит логика, что пустые значения не обрабатываются. Из за чего, если, например, у вас на форме не заполнен год (а он должен идти первым аргументом в конструкторе Date), то в результате его значение обработано не будет и на вход Date.new будет передано не 3, а 2 значения, что означает, что номер месяца (который идет вслед за годом в аргументах Date.new) будет расценен как номер года, а номер дня - как номер месяца... Такая же проблема, насколько я понимаю, происходит и со стандартным date_select (тот, который состоит из трех списков), если включен :include_blank...

Я считаю, такое поведение неоправданным, поэтому я сделал запил N1 - не выбрасывать пустые атрбуты (см код ниже, метод extract_callstack_for_multiparameter_attributes).

Зачем же вообще код в ActiveRecord был написан так, чтобы выбрасывать пустые значения ? Сделано это, по видимому, было для того, чтобы обрабатывать пустые значения (как видно из

if values.empty?
send(name + "=", nil)

в execute_callstack_for_multiparameter_attributes). Вот это очень важно, т.к. не хотелось бы в последствие иметь ошибки на пустых и не обязательных к заполнению датах. Поэтому, (!) запил N2 - в конце обработки параметров проверить, если для любого из атрибутов заготовлен только массив из nil'ов, то заменить этот массив на пустой.

Таким образом, если у вас любой из трех компонент даты окажется пустым, то он так и будет занимать свое законное место в списке аргументов, но будучи сконвертированным в нужный тип данных (в нашем случае - целое число). Конвертация осуществляется вызовом метода, начинающегося с "to_" и продолжающегося одной буквой соответствующего типа (например, "i" - "to_i").. to_i, как известно, пустые строки превращает в 0... Значит, если у вас дата будет пустая, то это превратится в Date.new(0, 0, 0), что не очень хорошо, т.к. если 0 в качестве параметра для года и допускается, то передача 0 для месяца или дня порождает исключение (которое потом порождает "ошибку присвоение атрибута с несколькими параметрами" - MultiparameterAssignmentError)... Хотелось бы и с этим забороться, а также не трактовать пустое поле год как нулевой год.

И тут мне пришел в голову самый аццкий запил (N3):

class String
def to_n
Integer(self) rescue nil
end
end

Если кто не знает, Integer(string) преобразует строковое представление числа в, собссно, число, но кидает исключение, если строка хоть немного не число (содержит посторонние символы, пустая и т.п.)

Далее, модифицируем хелпер date_select (который мы и так уже модифицировали, см. требования заказчика в начале) так, чтобы к названию полей он добавлял не (1i) / (2i) / (3i), а (1n) / (2n) и (3n). Тогда ActiveRecord будет пытаться преобразовать значения этих полей, используя наш метод String#to_n.

Отлично! Теперь у нас вылетает MultiparameterAssignmentError, если данные дата не является корректными. Теперь осталось только обработать эту ошибку. Вообще, мне не понятно, почему такие ошибки не включаются в список ошибок валидации, а выбрасываются отдельным исключением. Некоторые люди даже научились бороться с этим разными извращенными способами (например, так)

Я же хотел, чтобы мне ничего для этого менять не нужно было (ни ловить исключение, ни доставать список ошибок из каких-то дополнительных мест). Поэтому: 1) исключение надо ловить внутри, 2) ошибки аккуратно складывать все в тот же errors. Я уже было начал делать очередной запил =), но наткнулся на этот замечательный плагин .. Добрые люди уже все сделали именно так, как я хотел.

Еще бы мои запилы оформить в виде плагина, а лучше (если нет каких-то концептуальных препятствий) - закомитить в само ядро.

Надеюсь, вам, люди, эта информация пригодится.

Ну и, собссно, мои запилы:

def extract_callstack_for_multiparameter_attributes(pairs)
attributes = { }

for pair in pairs
multiparameter_name, value = pair
attribute_name = multiparameter_name.split("(").first
attributes[attribute_name] = [] unless attributes.include?(attribute_name)

# запил N1: не пропускать пустые значения
#unless value.empty?
attributes[attribute_name] <<
[ find_parameter_position(multiparameter_name), type_cast_attribute_value(multiparameter_name, value) ]
#end
end

# запил N2: заменять массив из только nil'ов на пустой
attributes.each do |name, values|
attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last }
attributes[name] = [] unless attributes[name].detect { |x| !x.nil? }
end
end

Read: Валидация многопараметровых значений

Topic: The Vehicle Previous Topic   Next Topic Topic: Guil. Ty.

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use