Если вам никогда не приходилось делать какие-то взломы или мартышки, то, скорее всего, вы не написали или не развернули автономное или веб-приложение на JRuby. Хорошо, это немного драматично, но за последний год, работая с JRuby, мне пришлось внедрить немало патчей. Сегодня я хотел бы поделиться некоторыми проблемами, с которыми я столкнулся и как я их исправил.
Что такое патч обезьян
Исправления «обезьяны» , «гориллы» или «утки» — все это относится к одному и тому же процессу расширения или изменения кода во время выполнения без изменения исходного кода. Динамические языки, такие как Ruby, делают это особенно простым для выполнения. Тем не менее, это не все солнце и лепестки роз в земле обезьян. Фактически, в сообщении блога Авди Гримм в 2008 году под названием « Обезьянья патчировка уничтожает Ruby» , он назвал некоторые проблемы с ним, такие как устойчивость.
Конечно, патч обезьяны может быть полезен или иногда необходим, но почему? Как мы доберемся до точки, где мы должны полагаться на создание этих диких патчей в первую очередь?
Почему Hack и Monkey Patch
Как и следовало ожидать, существует множество причин, по которым вам может понадобиться взломать и исправить. Лично я столкнулся со следующими категориями проблем, которые привели меня к применению различных исправлений за последний год:
- Ошибка в жемчужине
- Ошибка в JRuby
Вероятно, стоит упомянуть, что моя рабочая среда не позволяет мне загружать и использовать новые выпуски кода (гемы, платформы и т. Д.) Так же легко / быстро, как вы привыкли, когда находитесь дома (или у многих других работодателей). Такого рода ограничения заставляют меня взламывать и исправлять, когда это необходимо для выполнения работы, когда я не могу просто получить что-то из Github или Rubygems по прихоти.
В следующих разделах я рассмотрю примеры, которые попадают в одну из категорий, упомянутых выше.
Ошибка в жемчужине
Все разработчики знают, что будут ошибки, это неизбежно. Я должен признать, что меня впечатлило количество программного обеспечения с открытым исходным кодом, которое работает «из коробки». Но бывают случаи, когда мы сталкиваемся с ошибками или другими проблемами, которые нам нужно обойти.
Недавно я столкнулся с проблемой с гемом mongo_session_store, который предоставляет Rails-совместимые хранилища сессий для MongoMapper и Mongoid. У меня была конкретная проблема: mongo_session_store
пытался загрузить MongoMapper, который я ранее не устанавливал. Это сразу показалось мне, потому что я использовал Mongoid, поэтому я не ожидал, что MongoMapper потребуется.
Я должен немного перемотать, чтобы объяснить, как я сузил проблему до mongo_session_store. В журналах я просто следил за трассировкой стека исключения, которое привело меня сюда . На данный момент я знал, откуда возникла проблема, но я не был уверен, почему. Я вырыл немного больше, чтобы увидеть, как код был настроен и требовался в mongo_session_store
. Именно тогда я обнаружил модуль mongo session store-rails3 , который использует функцию автозагрузки Ruby. Вот код для большей части модуля:
module MongoSessionStore | |
def self.collection_name=(name) | |
@collection_name = name | |
if defined?(MongoStore::Session) | |
MongoStore::Session.reset_collection | |
end | |
if defined?(MongoMapperStore::Session) | |
MongoMapperStore::Session.set_collection_name(name) | |
end | |
if defined?(MongoidStore::Session) | |
MongoidStore::Session.store_in(name) | |
end | |
end | |
# default collection name for all the stores | |
self.collection_name = «sessions» | |
end | |
autoload :MongoMapperStore, ‘mongo_session_store/mongo_mapper_store’ | |
autoload :MongoidStore, ‘mongo_session_store/mongoid_store’ | |
autoload :MongoStore, ‘mongo_session_store/mongo_store’ |
Похоже, что второй блок if
(который проверяет наличие MongoMapperStore::Session
) вызывает MongoMapperStore
класса MongoMapperStore
который пытается require 'mongo_mapper'
. Я использую этот гем в приложении JRuby on Rails, которое развертывается как файл Java WAR ) в Apache Tomcat . Эта конкретная ошибка появляется только при развертывании приложения в Tomcat. Если я запускаю приложение с помощью WEBrick, у меня не было проблем.
На данный момент я не совсем уверен, что вызывает MongoMapperStore
при запуске в Tomcat против WEBRick (даже при работе с JRuby), поэтому я открыл эту проблему на Github. Так как эта проблема фактически мешала моему приложению работать, мне пришлось быстро взломать его, пока я жду, пока автор гема (надеюсь) воспроизведет и исправит проблему.
Я просмотрел историю модуля MongoSessionStore
и увидел, что автозагрузка была добавлена несколько месяцев назад. Я по существу удалил автозагрузку и добавил обратно стандартные требования, завернутые в блоки begin-rescue. После того, как я внес эти изменения, я изменил gemspec, чтобы я мог перестроить гем под немного другим именем (чтобы сделать его немного более очевидным, нам понадобится обновленная версия оригинального гема в будущем). После этого я собрал гем и загрузил его в частный репозиторий, используемый на работе, и все заработало просто отлично.
Ошибка в JRuby
Я использовал JRuby в прошлом году, начиная с версии 1.5.6, а теперь до 1.6.5. Фактически, некоторые из моих недавних патчей для обезьян были применены для решения проблем, которые были исправлены в JRuby 1.6.6 (хотя у меня пока нет его на работе).
Первое, что я делаю, когда настраиваю Tomcat для размещения моих Rails-приложений, это активирую двусторонний SSL. Возможно, вы знакомы с TLS / SSL (как используется с HTTPS), в котором веб-сайт представляет сертификат вашему браузеру. Браузер проверяет, является ли сертификат действительным (то есть, не истек ли он, подписан ли доверенным центром сертификации и некоторыми другими вещами), а затем завершает настройку HTTPS-соединения (если только сертификат недействителен).
Однако двусторонний SSL (также известный как взаимная аутентификация ) делает шаг вперед. С двусторонним SSL сервер представляет сертификат, но также требует, чтобы пользователь также представил сертификат. Все пользователи приложений Rails, которые я разрабатываю, также имеют свой личный сертификат.
В начале 2011 года я был очень взволнован, когда я был готов развернуть свое первое приложение Ruby on Rails в качестве WAR на сервере Tomcat (настроенном с двусторонним SSL). Я запустил сервер, завел логи, просмотрел сайт и БАМ!
undefined method `read_nonblock‘ for #<OpenSSL::SSL::SSLSocket:0x246adb31> (NoMethodError) |
Я включил Google и наткнулся на JRUBY-5529, который должен был быть исправлен в JRuby 1.6.6 (в данный момент недоступно). Тем не менее, это были не все плохие новости, потому что кто-то (по имени Карл Баум) опубликовал суть, чтобы обезьяна BufferedIO
класс BufferedIO
:
Одна из замечательных особенностей JRuby заключается в том, что вы можете делать практически все, что можете в Ruby, включая классы Java Monkey Patch.
Заметки
Я только показал несколько примеров, но я столкнулся с несколькими другими (nokogiri, send_file
при использовании JRuby и другие). Дело в том, что вам, вероятно, придется взламывать и обезьяны патч, особенно когда вы будете использовать больше сторонних гемов или более новые версии JRuby.
Одно из предложений, которое я хотел бы закончить, — это поддержание последовательности в ваших проектах. Независимо от того, работаете ли вы в одиночку или с командой, рекомендуется придерживаться того, где и как вы применяете хаки и патчи для обезьян. В приложениях Rails мне нравится создавать каталог patches
каталоге config/initializers
. Документирование исправлений (или взломов) в README, вики или на каком-либо другом сайте, доступном для вас, вашей команды или других пользователей, поможет избежать путаницы и потенциальных проблем в будущем.