Подтвердить что ты не робот

Как Ruby on rails многопараметрические атрибуты * действительно * работают (datetime_select)

Я считаю, что datetime_select - черная магия. То, что я действительно пытаюсь понять, это целые 1i, 2i, 3i, 4i... несколько параметров. В частности, как это обрабатывается в задней части (activerecord, что-то еще?). Что с "i" после номера заказа? Это спецификатор типа? Если да, то какие другие типы доступны? Я прочитал источник date_helper.rb, и он довольно непрозрачен.

Здесь моя мотивация:

У меня есть столбец :datetime в моей модели, и я хочу ввести в представление через два text_field s: один для даты и один для времени. Они должны быть проверены, объединены вместе, а затем сохранены в столбце datetime. В конечном счете, я буду использовать календарь javascript для ввода дат в поле даты.

Так кто-нибудь это сделал? Я пробовал использовать виртуальные атрибуты (невероятно недокументированные, кроме рудиментарного railscast), и проблема заключалась в том, что когда создается новый объект activerecord и имеет атрибуты nil, виртуальный атрибут терпит неудачу (undefined метод strftime для класса nil, что имеет смысл).

У кого-нибудь есть предложения или рекомендации? Спасибо!

4b9b3361

Ответ 1

У меня был тот же вопрос. Вот что я нашел...

http://apidock.com/rails/ActiveRecord/Base/assign_multiparameter_attributes

assign_multiparameter_attributes (пар) частный

Создает объекты для всех атрибутов классы, которым требуется больше одного конструктор. Это делается путем вызов new для типа столбца или тип агрегации (через составленный) объект с этими параметрами. Так с парами, записанными в (1) = "2004", written_on (2) = "6", written_on (3) = "24", будет создавать экземпляр write_on (тип даты) с Date.new( "2004", "6", "24" ). Ты можешь также укажите тип-тип в скобки должны иметь параметры Привилегированные до их использования в конструктор. Используйте я для Fixnum, f для Float, s для String и a для массива. Если все значения для данного атрибут пуст, атрибут будет установлено значение nil.

Таким образом, цифры относятся к параметрам типа данных, которые относятся к столбцу. "I" должен преобразовать его в целое. Другие типы поддерживаются как "f" для float и т.д.

Здесь вы можете увидеть execute_callstack_for_multiparameter_attributes, что он обнаруживает целевой тип и передает его значения.

Итак, чтобы напрямую ответить на поставленный вопрос, я не думаю, что вы можете использовать его, чтобы переопределить, как создается DateTime, потому что он использует конструктор, который не поддерживает формат, который вы хотите передать. вокруг было разделение столбца DateTime на один столбец "Дата" и "Один раз". Тогда пользовательский интерфейс мог работать так, как я хотел.

Интересно, можно ли создавать атрибутные атрибуты класса, которые представляют дату и время отдельно, но сохранить один столбец DateTime. Затем форма может ссылаться на эти отдельные аксессоры. Это может быть осуществимо. Другим вариантом может быть использование composed_of для дальнейшей настройки типа.

Надеюсь, что это поможет кому-то другому.:)

Ответ 2

Вот что я в итоге придумал. Если у кого есть какие-либо комментарии, дайте мне знать. Мне бы очень хотелось получить обратную связь.

  validate :datetime_format_and_existence_is_valid  
  before_save :merge_and_set_datetime   

  # virtual attributes for date and time allow strings
  # representing date and time respectively to be sent
  # back to the model where they are merged and parsed
  # into a datetime object in activerecord
  def date
    if (self.datetime) then self.datetime.strftime "%Y-%m-%d"
    else @date ||= (Time.now + 2.days).strftime "%Y-%m-%d" #default
    end
  end
  def date=(date_string)
    @date = date_string.strip
  end
  def time
    if(self.datetime) then self.datetime.strftime "%l:%M %p"
    else @time ||= "7:00 PM" #default
    end
  end
  def time=(time_string)
    @time = time_string.strip
  end

  # if parsing of the merged date and time strings is
  # unsuccessful, add an error to the queue and fail
  # validation with a message
  def datetime_format_and_existence_is_valid    
    errors.add(:date, 'must be in YYYY-MM-DD format') unless
      (@date =~ /\d{4}-\d\d-\d\d/) # check the date format
    errors.add(:time, 'must be in HH:MM format') unless # check the time format
      (@time =~ /^((0?[1-9]|1[012])(:[0-5]\d){0,2}(\ [AaPp][Mm]))$|^(([01]\d|2[0-3])(:[0-5]\d){0,2})$/)
    # build the complete date + time string and parse
    @datetime_str = @date + " " + @time
    errors.add(:datetime, "doesn't exist") if 
      ((DateTime.parse(@datetime_str) rescue ArgumentError) == ArgumentError)
  end

  # callback method takes constituent strings for date and 
  # time, joins them and parses them into a datetime, then
  # writes this datetime to the object
  private
  def merge_and_set_datetime
    self.datetime = DateTime.parse(@datetime_str) if errors.empty?
  end