Таинственные гемы - Prepend Routes

Хочу поделиться интересной ситуацией, которая у меня возникла при разработке гемов. Вообще в любом случае попробуйте написать гем и обязательно выложите его на рубиджемс, так вы сделаете задачу придумывания имени для следующих коллег более интересной :)
Если говорить серьезно, я сталкивался с дискуссиями о том, что пространство имен рубиджемс очень сильно замусоренно и лучше не создавать гемы для своих внутренних нужд. Мне кажется проблема надуманная. Я не выкидываю бумажки из под мороженного на землю на основании того, что наша планета и так замусоренная, но экономия на именах мне напоминает шуточною статью, которая рекомендовала беречь GUID'ы, так как их осталось очень мало. (Если вы программировали под виндовс, то заметили, что гуиды там используются очень активно. Это смешная шутка, правда?)
Итак гемы. Вообще наибольший интерес для меня представляют так называемые энджины. Они эмитируют маленький рейлс-проект и позволяют выносить в гем контроллеры, модели, миграции, ассеты, рауты, все что захочется. Одна из проблема как раз связана с раутами. Вы можете добавить рауты следующим образом:
# lib/custom_gem/engine.rb
module CustomGem
class Engine < ::Rails::Engine
end
end
# app/controllers/custom_controller.rb
class CustomController
def index
# ...
end
end
# config/routes.rb
Rails.application.routes.draw do
get 'custom/index'
end
Это будет работать прекрасно во всех случаях, кроме одного, если кто-то использует раут со звездочкой для мэтчинга по шаблону (например для статических страниц о компании, контакты и т.д.):
MyApp::Application.routes.draw do
# ...
get '*permalink' => 'home#page'
end
В этом случае кастомный раут у гема будет затерт. Я столкнулся с этим в своем форке rails-footnotes, когда контроллер, который
в девелопменте позволяет посмотреть исходный руби-файл прямо в браузере, перестал работать. Есть очень простая, но недокументированная
возможность - метод prepend
actiondispatch/routing/routeset.rb#L306, с помощью которого можно поместить раут на самый верх:
# 1-й вариант (НЕ ПРАВИЛЬНО)
# ------------
# Вот этот вариант НЕ ЗАРАБОТАЛ! Не знаю почему, мне лень стало разбираться.
# config/routes.rb
# Заменяем draw на prepend
Rails.application.routes.prepend do
get 'custom/index'
end
# 2-й вариант (рабочий)
# ------------
# config/routes.rb - удаляем вообще
# lib/custom_gem/engine.rb
module CustomGem
class Engine < ::Rails::Engine
initializer 'custom' do |app|
ActiveSupport.on_load :after_initialize do
app.routes.prepend do
get 'custom/index'
end
end
end
end
end
С необходимостью расширять рауты (именно расширять, а не делать маунтед-енджин) я сталкивался всего два раза. В обоих случаях я сделал как в варианте 2. Первый вариант выглядит привлекательнее, но у меня пока не дошли руки разобраться почему он не работает.
Tweet