Это плохая идея для динамической перезагрузки маршрутов в Rails? - PullRequest
7 голосов
/ 25 сентября 2008

У меня есть приложение, которое я пишу, где я позволяю администраторам добавлять псевдонимы для страниц, категорий и т. Д., И я хотел бы использовать другой контроллер / действие в зависимости от псевдонима (без перенаправления, и я ' мы обнаружили, что метод render на самом деле не вызывает метод. Я просто отображаю шаблон). Я пробовал перехватывать все маршруты, но я не в восторге от того, что вызываю и ловлю исключение DoubleRender, которое выдается каждый раз.

Решение, которое я нашел, - это динамически генерируемые маршруты при запуске сервера и использование обратных вызовов из модели Alias ​​для перезагрузки маршрутов при создании / обновлении / уничтожении псевдонима. Вот код из моего маршрута .rb:

Alias.find(:all).each do |alias_to_add|
    map.connect alias_to_add.name, 
            :controller => alias_to_add.page_type.controller, 
            :action => alias_to_add.page_type.action,
            :navigation_node_id => alias_to_add.navigation_node.id
end

Я использую обратные вызовы в моей модели Alias ​​следующим образом:

after_save :rebuild_routes
after_destroy :rebuild_routes

def rebuild_routes
    ActionController::Routing::Routes.reload!
end

Это против лучших практик Rails? Есть ли лучшее решение?

Ответы [ 4 ]

11 голосов
/ 18 ноября 2010

Бен,

Я считаю метод, который вы уже используете, лучшим. Используя Rails 3, вам придется немного изменить код:

MyNewApplication::Application.reload_routes!

Вот и все.

5 голосов
/ 25 сентября 2008

Быстрое решение

Иметь общий маршрут внизу маршрутов .rb. Реализуйте любую логику поиска псевдонимов, которую вы хотите, в действии, которое направляет вас.

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

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

Если вы не найдете псевдоним, вы можете выбросить ошибку 404 или 500, если считаете нужным.

Что нужно иметь в виду:

Кэширование: Не зная ваших URL-адресов, априори может сделать кэширование страниц абсолютным преимуществом. Помните, что он кэшируется на основе предоставленного URI, а НЕ на url_for (:action_you_actually_executed). Это означает, что если вы псевдоним

/foo_action/bar_method

до

/some-wonderful-alias

вы получите некий чудесный alias.html, который находится в вашем каталоге кеша. И когда вы пытаетесь пролистать панель foo, вы не будете сканировать этот файл, если не укажете его явно.

Отказоустойчивость: Убедитесь, что кто-то случайно не наложил псевдоним на существующий маршрут. Вы можете сделать это тривиально, заставив все псевдонимы в «каталоге», который, как известно, иначе не будет маршрутизируемым (в этом случае псевдоним, являющийся текстуально уникальным, достаточно, чтобы они никогда не сталкивались), но это не является максимально желательным решение для нескольких приложений, о которых я могу подумать.

2 голосов
/ 29 сентября 2008

Во-первых, как предложили другие, создайте универсальный маршрут внизу маршрутов. Rb:

map.connect ':name', :controller => 'aliases', :action => 'show'

Затем в AliasesController вы можете использовать render_component для рендеринга псевдонима:

class AliasesController < ApplicationController
  def show
    if alias = Alias.find_by_name(params[:name])
      render_component(:controller => alias.page_type.controller, 
                        :action => alias.page_type.action,
                        :navigation_node_id => alias.navigation_node.id)
    else
      render :file => "#{RAILS_ROOT}/public/404.html", :status => :not_found
    end
  end
end
0 голосов
/ 25 сентября 2008

Я не уверен, что полностью понимаю вопрос, но вы можете использовать method_missing в своих контроллерах, а затем искать псевдоним, возможно, так:

class MyController
  def method_missing(sym, *args)
    aliased = Alias.find_by_action_name(sym)
    # sanity check here in case no alias

    self.send( aliased.real_action_name )
    # sanity check here in case the real action calls a different render explicitly
    render :action => aliased.real_action_name
  end

  def normal_action
    @thing = Things.find(params[:id])
  end
end

Если вы хотите оптимизировать это, вы можете поместить define_method в method_missing, так что он будет «пропущен» только при первом вызове и с тех пор будет обычным методом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...