Решение, которое я выбрал, было вдохновлено ответом @ dsander и комментарием @ Glex и использует обратные вызовы .Сначала мне пришлось создать поле name_encoded
(по умолчанию: false
) для таблицы file_spaces
в базе данных, поскольку уже сохраненные файловые пространства не кодируются.
Затем я создал модель дляиспользуйте для обратных вызовов (к сожалению, не самый чистый код):
class EncodingWrapper
require 'base64'
def initialize(attribute)
@attribute = attribute.to_s
@get_method = @attribute
@set_method = @attribute + "="
@get_encoded_method = @attribute + "_encoded"
@set_encoded_method = @attribute + "_encoded="
end
def before_save(record)
set_encoded(record)
encode(record)
end
def after_save(record)
decode(record)
end
# Rails dislikes the after_find callback because it has a potentially
# severe performance penalty. So it ignores it unless it is created a
# particular way in the model. So I have to change the way this method
# works. So long, DRY code. :-/
def self.after_find(record, attribute)
# Ugly...but hopefully relatively fast.
a = attribute.to_s
if record.send(a + '_encoded')
record.send(a + '=', Base64.decode64(record.send(a)))
end
end
private
def is_encoded?(record)
record.send(@get_encoded_method)
end
def set_encoded(record)
record.send(@set_encoded_method, true)
end
def encode(record)
record.send(@set_method, Base64.encode64(record.send(@get_method)))
end
def decode(record)
record.send(@set_method, Base64.decode64(record.send(@get_method)))
end
end
Наконец, подключите обратные вызовы к модели FileSpace:
class FileSpace < ActiveRecord::Base
...
before_save EncodingWrapper.new(:name)
after_save EncodingWrapper.new(:name)
# Have to do the after_find callback special, to tell Rails I'm willing to
# pay the performance penalty for this feature.
def after_find
EncodingWrapper.after_find(self, :name)
end
...
end