Использование метода отсутствует в Rails
У меня есть модель с несколькими атрибутами даты. Я хотел бы иметь возможность устанавливать и получать значения в виде строк. Я переопределил один из методов (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 ответа
Как вы уже знаете сигнатуру желаемых методов, может быть, лучше определить их, чем использовать 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?
вместо).
Вы можете быть заинтересованы в ActiveModel AttributeMethods
модуль (который активная запись уже используется для кучу вещей), который почти (но не совсем), что вам нужно.
Короче говоря, вы должны быть в состоянии сделать
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
для тебя. Единственным недостатком является то, что эти человеческие методы существуют для всех столбцов.