Я строю этот сайт RoR на основе существующей базы данных.Модель пользователя в базе данных имеет столбец с именем «секрет», который представляет собой побитовое целое число, в котором хранится информация о столбцах, которые пользователь установил как секрет (имя, фамилия и т. Д.).
Переменные имеют значениеиз двух, например: фамилия = 1 << 1 = 2, имя = 1 << 2 = 4, электронная почта == 1 << 3 = 8 и т. д. Так что, если пользователь установил имя и адрес электронной почты как секретные,значение столбца становится 4 + 8 = 12. </p>
Теперь я пытаюсь найти обобщенный способ реализации этих виртуальных столбцов в модели Rails.Так что я мог бы сделать (просто фиктивный пример, суть в том, что я хочу получить и сохранить статус):
if user.secret_email?
user.secret_name_last = true
user.secret_name_first = false
end
Как аккуратно реализовать эти виртуальные столбцы в модели (без изменения существующихбаза данных)?Тока у меня есть следующие.Это работает, но это не аккуратно.Поскольку у меня есть 20 секретных столбцов, код выглядит очень безобразно .
SECRET_NAME_LAST = (1 << 1) # 2
attr_accessible :secret_name_last
def secret_name_last; secret & SECRET_NAME_LAST > 0 unless secret.nil?; end
def secret_name_last=(value); secret_set_value(SECRET_NAME_LAST, value); end
SECRET_NAME_FIRST = (1 << 2) # 4
attr_accessible :secret_name_first
def secret_name_first; secret & SECRET_NAME_FIRST > 0 unless secret.nil?; end
def secret_name_first=(value); secret_set_value(SECRET_NAME_FIRST, value); end
SECRET_EMAIL = (1 << 3) # 8
attr_accessible :secret_email
def secret_email; secret & SECRET_EMAIL > 0 unless secret.nil?; end
def secret_email=(value); secret_set_value(SECRET_EMAIL, value); end
***snip (17 more)***
private
def secret_set_value(item, value)
if self.secret.nil?
self.secret = 0
end
if value == "1" || value == true || value == 1
# Add item to secret column (if it doesn't exist)
if self.secret & item == 0
self.secret += item
end
else
# Remove item from secret column (if it exists)
if self.secret & item > 0
self.secret -= item
end
end
end
Было бы замечательно , я мог бы просто сделать что-то вроде:
as_bitwise :secret_name_first, :column=>'secret', :value=>4
as_bitwise :secret_name_last, :column=>'secret', :value=>2
Или даже,
as_bitwise :secret, { :secret_name_last=>4, :secret_name_first=>2 }
РЕДАКТИРОВАТЬ
Исходя из превосходного ответа Брендана, вот что я получил в настоящее время:
module BitwiseColumn
extend ActiveSupport::Concern
module ClassMethods
def bitwise_column(*args)
mapping = args.extract_options!
column_name = args.shift
real_column_name = args.shift
logger.debug "Initializing bitwisecolumn, column: " + column_name.to_s
mapping.each_pair do |attribute, offset|
logger.debug "\tSetting a pair: offset: " + offset.to_s + ", " + attribute.to_s
mask = 2 ** offset
class_eval %{
attr_accessible :#{column_name}_#{attribute}
def #{column_name}_#{attribute}?
#{real_column_name} & #{mask} > 0 unless #{real_column_name}.nil?
end
def #{column_name}_#{attribute}=(value)
if self.#{real_column_name}.nil?
self.#{real_column_name} = 0
end
if value == "1" || value == true || value == 1
if self.#{real_column_name} & #{mask} == 0
self.#{real_column_name} += #{mask}
end
else
if self.#{real_column_name} & #{mask} > 0
self.#{real_column_name} -= #{mask}
end
end
end
}
end
end
end
end
Это позволяет мне использовать:
bitwise_column :secret, :realsecretcolumnatdatabase, :name_last=>1, :name_first=>2, :email=>3, :picture=>5, :dob=>6, :place=>12
После этого я могу позвонить User.first.secret_name_last?и т.д.