2017-01-06 10 views
2

Я прочитал blog post, который рекомендует использовать имена ваших патчей для обезьян, чтобы их можно было легко просмотреть и включить.Организация патчей обезьян

Например:

module CoreExtensions 
    module DateTime 
    module BusinessDays 
     def weekday? 
     !sunday? && !saturday? 
     end 
    end 
    end 
end 

идти ли в: файл lib/core_extensions/class_name/group.rb.

Он может быть включен в класс DateTime с помощью метода Module#include экземпляра (который класс наследуется, поскольку Class является Module)

# Actually monkey-patch DateTime 
DateTime.include CoreExtensions::DateTime::BusinessDays 

Мой вопрос куда включаемые заявления идут? Есть ли конвенция?

Например:

У меня есть следующие обезьяны патчи:

# http://www.justinweiss.com/articles/3-ways-to-monkey-patch-without-making-a-mess/ 
module CoreExtensions 
    module String 
    module Cases 
     def snakecase 
     return self if self !~ /[A-Z]+.*/ 
     # http://rubular.com/r/afGWPWLRBB 
     underscored = gsub(/(.)([A-Z])/, '\1_\2') 
     underscored.downcase 
     end 

     def camelcase 
     return self if self !~ /_/ && self =~ /[A-Z]+.*/ 
     split('_').map{ |e| e.capitalize }.join 
     end 
    end 
    end 
end 

, которые живут внутри файла lib/core_extensions/string/cases.rb.

Куда я должен положить заявление String.include CoreExtensions::String::Cases?

Также, чтобы быть понятным, это всего лишь рубиновый проект, разве это имеет значение?

Я попытался положить его в lib/devify.rb

require 'devify/version' 
require 'devify/some_dir' 
require 'devify/scaffold' 
require 'devify/tree_cloner' 
require 'devify/renderer' 
require 'devify/project' 

require 'devify/tasks/some_task' 
require 'devify/tasks/bootstrap' 

require 'core_extensions/string/cases' 

module Devify 
    String.include CoreExtensions::String::Cases 
end 

Это работает, и это имеет смысл, почему это работает. Это потому, что все мое приложение живет внутри модуля или пространства имен Devify.

Этот способ также хорош, потому что я не загрязняю глобальное пространство имен правильно? Потому что я всего лишь обезьяна, исправляющая String s, которые живут внутри Devify?

Просто не уверен, что это правильный путь.

+1

если вы пишете 'String.include' в любом месте, где вы _are_ загрязняете глобальное пространство имен. Вы можете задержать его, поместив этот вызов в метод. Но как только будет вызван метод, все строки будут исправлены. Прочитайте уточнения как менее навязчивую альтернативу. Я добавил несколько примеров [здесь] (http://stackoverflow.com/documentation/ruby/6563/refinements#t=201701060134259018086). –

+1

Кстати, это соглашение об именах файлов является хорошей практикой для программирования Ruby в целом. Он может помочь в автозагрузке зависимостей - например, если вы используете шаблон, вы можете сказать 'Dir.glob (" ./**/*. Rb "). Sort_by {| path | path.count ("/")} .each {| path | require path} ', который будет загружать все файлы, упорядоченные по счету'/'в их имени файла (в соответствии с вашей иерархией констант) –

ответ

2

Не имеет значения, где вы положили звонок include.

Вызов String.include всегда будет включать обезьяну один класс String, который используется всеми строками во всем пространстве объекта. Поэтому лучше всего нарисуйте инструкцию на верхнем уровне, чтобы не вводить в заблуждение читателей кода.

Патч обезьяны всегда глобальный.

Это мощная функция и может использоваться навсегда.

Если вы являетесь автором драгоценного камня, помните, что вы используете глобальное пространство имен с другими. То же самое верно и для модулей верхнего уровня и даже для названия драгоценных камней. Общие пространства имен являются реальностью общего кода.

Если вы ищете лексический контекстную обезьяну патчи выглядеть в новое рафинировании особенность, которая была ввести с Рубином 2.

Уточнения идея, взятая из класса коробки Smalltalk в. Уточнения не лишены собственных проблем, хотя, например, им не хватает поддержки для самоанализа и размышлений. Таким образом, они делают их скрытыми и непригодными для производства.

Если вы хотите ограничить патчи обезьяны только некоторым строковым объектом, рассмотрите либо подклассификацию String, либо вызов extend на экземпляр.

0

Хотя рубин предлагает множество способов изменения содержимого класса или метода динамически, исправление обезьян может привести к большим проблемам и странным ошибкам. Я прочитал этот пост (http://www.virtuouscode.com/2008/02/23/why-monkeypatching-is-destroying-ruby/) о том, почему это плохая идея использовать переключение обезьян.

Таким образом, многие вещи, о которых он говорит, имеют смысл. Когда вы создаете патч для обезьян, вы предполагаете, что он будет работать только в этом проекте, и, возможно, вы можете создавать конфликты и непредсказуемые побочные эффекты, когда объединяются другие библиотеки с аналогичными целями.

Есть случаи, когда польза от обезьяны-заплат были удивительной, как ActiveSupport способ борьбы с манипуляциями дат на обезьяну-Патчение класса Fixnum с ago или from_now методами, или как метод to_json. Однако следует избегать патчей обезьян.

Дело в том, что Ruby является объектно-ориентированным языком, и вы можете достичь своих целей, используя композицию объекта или любые другие шаблоны. Обезьяна-исправление, в некотором роде, ведет вас в противоположном направлении объектно-ориентированной философии, поскольку вы добавляете больше обязанностей перед существующим классом и увеличиваете его публичный интерфейс, чтобы служить новой функциональности.

Кроме того, это не явное поведение этого класса и общедоступных методов. Вы не можете знать, рассматривая определение класса, то, что он делает, и какова его роль в системе, и как она взаимодействует с другими объектами. В конце он делает более сложную задачу.

Monkey patching делает все намного меньше и проще, по-видимому, но, избегая этого, ваш код намного более прост в обслуживании, проще отлаживать, читать и тестировать, а также намного более элегантно, так как он совместим с шаблонами «ООП».