2012-02-13 6 views
1

Интересно, что лучший способ написать модульное расширение существующей библиотеки в рубине, которое изменяет существующие методы. Он не должен вводить повторение кода и должен использоваться только по требованию.Что такое DRYest способ расширения/исправления библиотеки в рубине?

Конкретная задача, которую я пытаюсь выполнить, заключается в расширении модуля ruby ​​Net::FTP для поддержки некоторых not so standards compliant servers. Такое расширение должно быть полностью отделено от библиотеки, соответствующей стандартам 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 сохранить код DRY. Это возможно? Есть ли у вас дополнительные предложения по улучшению этого метода исправления/расширения? Или, может быть, даже совершенно разные подходы?

ответ

4

Это называется «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 за это). В этом случае это не кажется необходимым.

UPDATE: Вслед за ваш комментарий, если вы хотите продлить только конкретный экземпляр в Net::FTP класс, вы можете расширить свои одноэлементные классы:

obj = Net::FTP.new 
class << obj 
    alias_method :original_mkdir, :mkdir 
    def mkdir(dirname) 
    #... 
    original_mkdir(dirname) 
    #... 
    end 
end 
+1

Да, подкласс, когда * возможно *, monkeypatch когда *необходимо*. – tadman

+0

@tadman: Не мог бы сформулировать это лучше (а на самом деле нет). Разум, что я процитировал вас? –

+0

Я уверен, что это было сказано раньше, но обязательно! – tadman

 Смежные вопросы

  • Нет связанных вопросов^_^