Обновить

Комментарии 18

А что мешает в контроллере сделать вот так:
class SomeController
  attr_writer :external_service

  def index
    authorize!
    external_service.accounts
  end

  def external_service
    @external_service ||= ExternalService.build
  end
end
Хороший подход, вполне позволяет писать тесты на контроллер. Но есть и минусы: когда в публичном интерфейсе класса есть attr_writer, можно решить, что где-то кто-то собирается поменять значение поля (или, по крайней мере, может это сделать). Особенно эти проблемы проявляют себя тогда, когда код сервиса начинает расширяться с помощью плагинов, хранящихся в отдельных репозиториях (например, при использовании модификаций одного и того же сервиса для разных компаний-заказчиков).
В общем, писать код с оглядкой на тесты — это одно. Писать код, который будет использоваться только в тестах, уже другое.
НЛО прилетело и опубликовало эту надпись здесь
Недостаток такого подхода в том, что он не очень хорошо работает, когда в контроллере более одного экшена. Код становится сложно понимать.
НЛО прилетело и опубликовало эту надпись здесь
Ну скажем, много лет назад натыкались, что mongoid что-то перекрыл в Object и nil? возвращал true на объекте (не NilClass).

Сейчас есть проблемка с гемом packable (да, я понимаю, что он не очень популярен, но все же факт есть факт).
Отдельно отлично работает.
В рельсовом проекте с большим количеством гемов, при некоторых условиях, есть исключения (причем сам packable, там казалось бы, ни к чему, но поскольку манкипатч… ну вы поняли). Причем выкурить, с чем именно конфликтует не удалось, т.к. усилий нужно неоправданно много, запихнул в group :manual_load в Gemfile и подключаю вручную там, где нужен. Пока работает.

Я отдаю себе отчет в этом:

> А mock в rspec — это monkey-patching. Оп-па.

И лично считаю rspec лучшим тулом для TDD. Хотя я скорее явист, чем рубист.
Минусы истекают из плюсов и наоборот.

ИМХО манкипатч 5.days.ago (в платформе!) это одно дело, и куда бы ни шло, а вот когда мелкие гемы лезут со своей ерундой в стандартные классы — совсем другое. Но тут уж ничего не поделаешь.
НЛО прилетело и опубликовало эту надпись здесь
>> Но это же все из серии «давайте запретим ножи, они острые».

Разумеется, просто неправильно выбранный инструмент.
Проблема манки-патчинга не в том, что после его использования у вас сразу выпадут волосы и зубы, а в том, что манки-патчинг по своей сути это слабо управляемый инструмент. Его не стоит сравнивать с ножом, скорее с топором, которым вы пытаетесь разрезать торт. Цели своей вы добъетесь, но выглядеть это будет диковато, даже если топор наточен очень остро. Но допустим, что руби это такой странный мир людей, предпочитающих такие методы решения проблем.
Манки-патчинг это большая головная боль для разработчиков гемов, потому что получается, что кто угодно может непредсказуемо поменять рантайм языка, в итоге выйдет, что какие-то гемы окажутся несовместимыми. Это вовсе не мифы, чем дольше руби развивается, тем больше таких проблем происходит. Вот пример с переполнением стека из-за переопределения базовых классов: https://github.com/txus/kleisli/pull/17. Хочу заметить, что ошибки с переполнением стека очень неудобно отлаживать, буквально на днях такая ошибка уронила у меня интерпретатор с ошибкой адресации, никакого руби-стека я не увидел вообще.
Дальше — больше. Есть рельсы, которые построены на activesupport. Если какое-то из поведений в AS ломает ваш гем, то вам ничего не остается, кроме как поменять вашу библиотеку для совместимости с AS, потому что иначе никто не будет ей пользоваться. Это уже не ножи, это пики из задачи двух стульев.
Стоит присмотреться повнимательнее какую же проблему на самом деле решает манки-патчинг. На деле это оказывается банальное отсутствие нужных абстракций или инструментов, вместо создания которых, люди патчат рантайм. Точка зрения dry-rb-коммьюнити заключается в том, что так быть не должно. Нужно разрабатывать библиотеки и подходы, которые будут удобны и функциональны без необходимости изменять поведение стандартных классов, для этого в руби есть все необходимое. Мы неоднократно видели, как продуманный подход преподносил много приятных сюрпризов впоследствии, что еще больше усилило нашу уверенность в правильности выбранного пути. Простите, если это звучит слишком пафосно :)
НЛО прилетело и опубликовало эту надпись здесь
Странно, что вы отказываете мне в наличии собственного мнения. В любом случае, я имел в первую очередь позицию core team.

BasicObject создан для использования в качестве контекста для DSL, так он и используется, разве нет? Ну, конечно, если что-то не так с этим подходом, и вы знаете об этом, нет смысла держать в себе, откройте тикет, где это можно будет обсудить в «рабочем порядке» :) Равно как и про «остальные смешные вещи».

refinements не взлетели потому что они на самом деле не решают основную проблему — отсутствие абстракций. Ну, я не исключаю, что если бы они работали как-то по-другому, то это позволило бы хотя бы снять вопрос об изменении рантайма.

Ерунда. Открытые классы — мощнейший инструмент, который позволяет сделать очень много, если делать это по уму. Если же делать что-нибудь не по уму, то даже в насквозь казалось бы безопасном Delphi в 1994 году мне удалось наступить на грабли с левой библиотекой, которая обращалась к приватным полям чужих объектов по смещению. В любом языке можно отравить жизнь всем своим пользователям, и проблема не в манкипатчинге.

Это пример ложной дилеммы.
НЛО прилетело и опубликовало эту надпись здесь
Кхм. Ну, допустим. Хотя избавляться от неймспесинга и внедренных зависимостей можно и поизящнее, не разламывая в щепы pry, которая не ожидает от объектов такой подлости, как отсутствие `puts`. Делегировать пару методов из `Kernel` уж точно можно было бы. Впрочем, я в целом не понимаю, в чем прелесть `BasicObject` именно там, кроме демонстрации «идеологии».

Я все же настаиваю на обсуждении этого на гитхабе, там более подходящее место. Но насчет пробрасывания методов из Kernel идея хорошая.

Что касается остального обсуждения, то я написал тут не с целью переубедить кого-то, а чтобы обозначить позицию. Манкипатчинг по нашим представлениям крайне редко решает проблему адекватно задаче, при этом создает значительные неудобства, потому что люди не могут предвидеть последствия изменения стандартных классов, это слишком сложная задача для человека. Поэтому мы пользуемся другими инструментами для решения задач.
НЛО прилетело и опубликовало эту надпись здесь
5.days может сломать, я разбирался с такой проблемой, когда Duration притворялся числом, его нельзя было распознать никак, кроме как вызвать метод parts, который внезапно оказывался/не оказывался у объекта, так можно было отличить целое число от Duration. Это было в рельсах 3.2, с тех пор этот момент улучшили, но я лично потратил час или два на разбирательства с той проблемой, какой-нибудь новичок, по-моему просто погиб бы. Но это я в качестве примера, на самом деле я согласен, что патчинг бывает разный, но беда в том, что вместе в рельсами вы получаете все и сразу, нет способа отказаться. А когда вы на это жалуетесь, вам рекомендуют использовать другой язык программирования под предлогом того, что вы не умеете в руби, такие дела.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Да я не про вас, это аргумент dhh :)

Про duration я ничего уже не помню, но проблема при дебаге была в том, что квакание там было неподходящим, is_a? и .class тоже не работали, выдавая Fixnum, на самом деле им не являясь. Конечно, если там поразбираться, наверняка была проблема в некорректном обращении с типами в руби, но вот разбираться с такими вещами тяжеловато.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации