Я много раз слышал, как Руби рекламируют свои супер-впечатляющие возможности метапрограммирования, и мне было интересно, кто-нибудь может мне помочь с этой проблемой.
У меня есть класс, который работает как своего рода «архив», с внутренними методами, которые обрабатывают и выводят данные на основе входных данных. Однако элементы в архиве в самом классе представлены и обработаны целыми числами для повышения производительности. Фактические элементы вне архива известны их строковым представлением, которое просто равно number_representation.to_s (36).
Из-за этого я подключил каждый внутренний метод к «прокси-методу», который преобразует входные данные в целочисленную форму, которую распознает архив, запускает внутренний метод и преобразует выходные данные (либо один другой элемент, либо их коллекция) обратно в строки.
Соглашение об именах таково: внутренние методы представлены как _method_name; соответствующий им прокси-метод представлен method_name без начального подчеркивания.
Например:
class Archive
## PROXY METHODS ##
## input: string representation of id's
## output: string representation of id's
def do_something_with id
result = _do_something_with id.to_i(36)
return nil if result == nil
return result.to_s(36)
end
def do_something_with_pair id_1,id_2
result = _do_something_with_pair id_1.to_i(36), id_2.to_i(36)
return nil if result == nil
return result.to_s(36)
end
def do_something_with_these ids
result = _do_something_with_these ids.map { |n| n.to_i(36) }
return nil if result == nil
return result.to_s(36)
end
def get_many_from id
result = _get_many_from id
return nil if result == nil # no sparse arrays returned
return result.map { |n| n.to_s(36) }
end
## INTERNAL METHODS ##
## input: integer representation of id's
## output: integer representation of id's
private
def _do_something_with id
# does something with one integer-represented id,
# returning an id represented as an integer
end
def do_something_with_pair id_1,id_2
# does something with two integer-represented id's,
# returning an id represented as an integer
end
def _do_something_with_these ids
# does something with multiple integer ids,
# returning an id represented as an integer
end
def _get_many_from id
# does something with one integer-represented id,
# returns a collection of id's represented as integers
end
end
Есть несколько причин, по которым я не могу просто преобразовать их, если id.class == Строка в начале внутренних методов:
- Эти внутренние методы являются в некоторой степени вычислительно-рекурсивными функциями, и я не хочу, чтобы накладные расходы проверялись несколько раз на каждом шаге
- Невозможно без добавления дополнительного параметра указать, следует ли преобразовывать в конце
- Я хочу воспринимать это как упражнение в понимании метапрограммирования в ruby
У кого-нибудь есть идеи?
редактировать
Я бы предпочел решение, которое могло бы принимать массив имен методов
@@PROXY_METHODS = [:do_something_with, :do_something_with_pair,
:do_something_with_these, :get_many_from]
итерируйте их, и на каждой итерации выведите прокси-метод. Я не уверен, что будет сделано с аргументами, но есть ли способ проверить аргументы метода? Если нет, то подойдет и простая утка / аналогичная концепция.
Я придумал собственное решение, используя #class_eval
@@PROXY_METHODS.each do |proxy|
class_eval %{ def #{proxy} *args
args.map! do |a|
if a.class == String
a.to_i(36)
else
a.map { |id| id.to_i(36) }
end
end
result = _#{proxy}(*args)
result and if result.respond_to?(:each)
result.map { |r| r.to_s(36) }
else
result.to_s(36)
end
end
}
end
Однако #class_eval
кажется немного ... грязным? или не элегантный по сравнению с тем, что "должно" быть.