Использование метода отсутствует в Rails - PullRequest
5 голосов
/ 17 января 2012

У меня есть модель с несколькими атрибутами даты. Я хотел бы иметь возможность устанавливать и получать значения в виде строк. Я перебил один из методов (bill_date) примерно так:

  def bill_date_human
    date = self.bill_date || Date.today
    date.strftime('%b %d, %Y')
  end
  def bill_date_human=(date_string)
    self.bill_date = Date.strptime(date_string, '%b %d, %Y')
  end

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

Ответы [ 2 ]

10 голосов
/ 17 января 2012

Поскольку вы уже знаете сигнатуру желаемых методов, может быть лучше определить их вместо использования method_missing. Вы можете сделать это так (внутри определения вашего класса):

[:bill_date, :registration_date, :some_other_date].each do |attr|
  define_method("#{attr}_human") do
    (send(attr) || Date.today).strftime('%b %d, %Y')
  end   

  define_method("#{attr}_human=") do |date_string|
    self.send "#{attr}=", Date.strptime(date_string, '%b %d, %Y')
  end
end

Если перечисление всех атрибутов даты не является проблемой, этот подход лучше, так как вы имеете дело с обычными методами вместо какой-то магии внутри method_missing.

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

column_names.grep(/_date$/)

А вот решение method_missing (не тестировалось, хотя и предыдущее тоже не тестировалось):

def method_missing(method_name, *args, &block)
  # delegate to superclass if you're not handling that method_name
  return super unless /^(.*)_date(=?)/ =~ method_name

  # after match we have attribute name in $1 captured group and '' or '=' in $2
  if $2.blank?
    (send($1) || Date.today).strftime('%b %d, %Y')
  else
    self.send "#{$1}=", Date.strptime(args[0], '%b %d, %Y')
  end
end

Кроме того, приятно переопределить метод respond_to? и вернуть true для имен методов, которые вы обрабатываете внутри method_missing (в 1.9 вместо этого следует переопределить respond_to_missing?).

5 голосов
/ 17 января 2012

Вас может заинтересовать модуль AttributeMethods ActiveModel (который активная запись уже использует для множества вещей), который является почти (но не совсем) тем, что вам нужно.

В двух словах вы должны быть в состоянии сделать

class MyModel < ActiveRecord::Base

  attribute_method_suffix '_human'

  def attribute_human(attr_name)
    date = self.send(attr_name) || Date.today
    date.strftime('%b %d, %Y')
  end
end

Сделав это, my_instance.bill_date_human вызовет attribute_human с attr_name, установленным в 'bill_date'. ActiveModel будет обрабатывать такие вещи, как method_missing, respond_to для вас. Единственным недостатком является то, что эти _человеческие методы существуют для всех столбцов.

...