Какой самый сухой способ расширения / исправления библиотеки в ruby?
Интересно, как лучше написать модульное расширение существующей библиотеки в ruby, которое изменяет существующие методы. Он не должен вводить повторение кода и должен использоваться только по требованию.
Конкретная задача, которую я пытаюсь выполнить, это расширение рубина. Net::FTP
модуль для поддержки некоторых не соответствующих стандартам серверов. Такое расширение должно быть полностью отделено от совместимой со стандартами библиотеки IMHO.
Я думал, что требовать дополнительного файла было бы неплохо, так как это даже не создавало бы необходимость какого-то переключения в исходном коде. Так что дополнительный require 'net/ftp/forgiving'
сделало бы оригинальную библиотеку немного более щадящей в отношении наших менее талантливых собратьев по FTP-серверу.
Соответствующий файл может затем использовать открытый класс ruby и архитектуру модуля для исправления класса FTP. Для исправления примера причудливого поведения, связанного выше, мне нужно будет исправить Net::FTP#mkdir
, который будет выглядеть так:
#content of net/ftp/forgiving
require 'net/ftp'
module Net
class FTP
# mkdir that will accept a '250 Directory created' as a valid response
def mkdir(dirname)
begin
original_mkdir(dirname)
rescue FTPReplyError => e
raise unless e.message.start_with? '250 Directory created'
return ""
end
end
end
end
Однако для этого потребуется как-то кешировать оригинал Net::FTP#mkdir
как Net::FTP#original_mkdir
держать код СУХОЙ. Это возможно? Есть ли у вас какие-либо дальнейшие предложения о том, как улучшить этот метод исправления / расширения? Или, может быть, даже совершенно разные подходы?
1 ответ
Это называется "monkeypatching" и это именно тот случай использования, который alias_method
был сделан для:
alias_method :original_mkdir, :mkdir
def mkdir(dirname)
begin
original_mkdir(dirname)
rescue FTPReplyError => e
raise unless e.message.start_with? '250 Directory created'
return ""
end
end
Хотя это часто встречающаяся "идиома" в Ruby, это нарушит существующий код (возможно, даже код внутри Net
) который опирается на mkdir
поднимая исключение в этом случае. Вы не можете ограничить эти изменения файлами, которые require 'net/ftp/forgiving'
только. Таким образом, было бы намного чище создать подкласс, а не открывать исходный класс:
module Net
class ForgivingFTP < FTP
# mkdir that will accept a '250 Directory created' as a valid response
def mkdir(dirname)
begin
super(dirname)
rescue FTPReplyError => e
raise unless e.message.start_with? '250 Directory created'
return ""
end
end
end
end
Или, что еще лучше, поместите его в собственное пространство имен! Хорошее эмпирическое правило:
Подкласс, когда это возможно, Monkeypatch, когда это необходимо.
(Спасибо @tadman за это). В этом случае это не кажется необходимым.
ОБНОВЛЕНИЕ: После вашего комментария, если вы хотите расширить только конкретный экземпляр Net::FTP
класс, вы можете расширить их одноэлементные классы:
obj = Net::FTP.new
class << obj
alias_method :original_mkdir, :mkdir
def mkdir(dirname)
#...
original_mkdir(dirname)
#...
end
end