Идите нахуй со своим манки-патчингом.

</content>

Вообще, если разобраться

Надо начинать с ответа на вопрос “почему”. Или, если точнее, “какого хрена?” В двух словах: руби свободный язык и каждый волен нарушать правила здравого смысла в удобных ему пределах. Идиоматический руби, таким образом, не должен его использовать вообще, а благодарные программисты должны поклястся оберегать его от всяческих бед и невзгод. Для этого в руби, вроде бы, заготовлены такие отличные абстракции, как модули.

MyPerfectFramework.log("I can log with this API")
extend MyPerfectFramework
log("And with this api")
log("I am fucking logging god")

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

Ебанный полиморфизм

Руби претендует на звание языка с поддержкой инкапсуляции, полиморфизма и наследования. Например, я могу определить метод String.present?, а потом Hash.present? и вызвать на толи строке, толи хэше .present?. Упс, я заманкипатчил объекты, от которых даже наследоваться то не стоит. Тогда пойдем другим путем

def present?(x)
  case x
  when String then x != ""
  when Hash then x != {}
  when nil then false
  end
end

Охуенно, не правда ли? А теперь пользователь захотел переиспользовать API present? для своего красивого класса.

def present?(x)
  case x
  when String then x != ""
  when Hash then x != {}
  when nil then false
  when Present then x.custom_present?
  end
end

Тут Present это какой то интефрейс. Получается довольно таки ебано, но будет работать. Вторая проблема: Полиморфизм в руби задизайнен так, что вынуждает манки-патчить.

Кстати, в руби скорее предпочли бы третий вариант:

def present?(x)
  return x.custom_present? if x.respond_to?(:custom_present?)

  case x
  when String then x != ""
  when Hash then x != {}
  when nil then false
  end
end

Называется утиный тип и он сам по себе нереально ебаный. Манки-патчинг и утиные типы это две стороны одной медали. Ты манки-патчишь объект, чтобы наделить его новым утиным типом, в итоге ебешься в жопу. Но проблема не столько в том, что они сочитаются так же идеально, как базилик и помидоры, а в том, что оба этих паттерна засирают нейспейс. Представляешь себе, насколько ты обосрешься, если завтра в ActiveSupport появится метод, который называется так же, как один из методов твоего любимого типа? А единственная твоя вина в том, что ты пишешь на руби. И никаким разумным способом этого не обойти, даже если ты напишешь жирненьким, что это не утиный тип, и чтобы его имплементить, надо подключить кошерно именованный модуль, для ActiveSupport твой метод останется утиным типом и ты два часа будешь ебаться в поиске баги. Либо залочишь предыдущую версию ActiveSupport и пошлешь всех нахуй, но это на любителя, в любой нормальнйо конторе это назовут техническим долгом.

def present?(x)
  if x.respond_to?(:__my_best_framework_ever__custom_present? )
    return x.__my_best_framework_ever__custom_present?
  end

  case x
  when String then x != ""
  when Hash then x != {}
  when nil then false
  end
end

Вам, блядь, смешно? А я так и пишу, когда просто хочу добавить приватный метод в библиотеке, чтобы избежать конфликта имен. Мало ли кто захочет его переопределить

Да, приватных методов в руби нет

В нормальных языках рубишному private соответствует protected — только себе и наследникам. А если private то наследникам хуй, а не переопределить. Впрочем, меня занесло, к манки-патчам это не относится

Зато это, блядь, относится

В руби нет нормального способа сократить вызов типа Sequel.lit(x) до lit(x). Правда, нет. Если прописать include Sequel, класс засрется публичными методами Sequel, что заставит охуеть любого, кто напишет в консоли pry ls MyClass.new. Да даже если приватными, Третья проблема: это просто не принято Вот такое вот суровое резюме ставит крест на идее руби вообще без манки-патчей. Кстати, я пишу Sequel.lit(x) и горжусь этим. Потому что а) верю в идеоматический руби

б) манки патчи - это пиздец нахуй

Я тут развел беседу на тему того, почему нам приходится манки-патчить, теперь я напишу о том, почему лично мне похуй на то, что что-то кому-то приходится. Потому что я лучше сдохну ебучим нонеймом, чем прославлюсь и стану тобой

Сегодня ты манки-патчишь

А завтра переписываешь родину на rails.

В руби говно вместо неймспейсинга. Вот, предположим, захотел ты написать свою охуенную систему валидации и заюзать ее в ActiveRecord. Как ты назовешь метод validate? А? А? И хуй с ним с ActiveRecord, можешь послать нахуй всех пользователей rails и юзать либу один. Но завтра твой друг, который с детства за Hanami (не является фреймворком для web-приложений) ловит конфликт имен на своей родненькой библиотеке, которую сам написал. У нас как то был веселый момент с гемом tainbox кстати, не проходим мимо, ставим звездочку на гитхабе, когда на вход отдавался параметр attributes и все факапил. Но это лирика, когда у тебя конфликт имен в модулях, ты всегда можешь в одном конкретном месте заюзать смекалочку™. А ЕСЛИ В KERNEL?????

Представляешь себе, из-за одного ебаного названия метода

кто-то не сможет заюзать твою библиотеку. Например, ты сам через 2 года. Охуенно жить, да? Так что всю свою такую хуйню лучше засунь в модуль, а для ебланов можешь в ридми написать “засуньте Kernel.include(MyFramework) в инишалайзер”. Серьезно, мир будет лучше, если вместо {}.to_camelback_keys.to_json кто то напишет JSON.dump(Awrence.to_camelback_keys({})), жаль что разработчик даже не подумал запилить этот API.

Модули стимулируют писать Single Responsibility классы,

а манки-патчи стимулируют простату. Только убеки из rails могли придумать засунуть валидацию прямо в модель, но еще не позно все исправить - пиши валидатор отдельно и еби в рот манки-патчи, у тебя класс юзает только один модуль, которому прилично иметь метод validate. Но почему бы не пойти дальше, и не использовать человеческое наследование вместо примешивания всякого говна?

Да, мы говорили о манки-патчах, но я постоянно ссылаюсь на модули

Потому что тоже накипело. Самое страшно слово в ruby это include. Даже prepend и то лучше, ты честно говоришь, что хочешь сделать хуйню. А с include непонятки, оно с одной стороны хуйней не является, а с другой норовит выебать в жопу миксинами. Миксины пореже используйте, да. И удачи, вы все-таки на руби пишете, она вам нужна

<Footer>

Дисклеймер: предложенный вариант публичного API тоже хуевый

class Foo
  MyFuckingBestValidationLibrary.validate(self) { errors.add "ты хуй" }
end