Динамический URL → Отображение контроллера для маршрутов в Rails
Я хотел бы иметь возможность сопоставлять URL-адреса контроллерам динамически на основе информации в моей базе данных.
Я хочу сделать что-то функционально эквивалентное этому (предполагая модель View
):
map.route '/:view_name',
:controller => lambda { View.find_by_name(params[:view_name]).controller }
Другие предложили динамически перестроить маршруты, но это не сработает для меня, так как там могут быть тысячи просмотров, которые сопоставляются с тем же контроллером
Ответы
Ответ 1
Этот вопрос старый, но я нашел его интересным. Полностью работающее решение может быть создано в Rails 3 с использованием возможности маршрутизатора для маршрутизации на конечную точку Rack.
Создайте следующий класс Rack:
class MyRouter
def call(env)
# Matched from routes, you can access all matched parameters
view_name= env['action_dispatch.request.path_parameters'][:view_name]
# Compute these the way you like, possibly using view_name
controller= 'post'
my_action= 'show'
controller_class= (controller + '_controller').camelize.constantize
controller_class.action(my_action.to_sym).call(env)
end
end
В маршрутах
match '/:view_name', :to => MyRouter.new, :via => :get
Подсказка взята из http://guides.rubyonrails.org/routing.html#routing-to-rack-applications, в которой говорится: "Для любопытных" posts # index "фактически расширяется до PostsController.action(: index), который возвращает действительное приложение Rack."
Вариант, проверенный в Rails 3.2.13.
Ответ 2
Итак, я думаю, что вы просите, чтобы у вас есть таблица Views и модель View для нее, где таблица выглядит как
id | name | model
===================
1 | aaa | Post
2 | bbb | Post
3 | ccc | Comment
Вы хотите, чтобы URL-адрес/aaa указывал на Post.controller - это правильно?
Если нет, то то, что вы предлагаете, кажется хорошим, если оно работает.
Вы можете отправить его на все действия и посмотреть, как действие будет выглядеть по URL-адресу, запустите find_by_name, а затем вызовите правильный контроллер.
def catch_all
View.find_by_name('aaa').controller.action
end
Update
Вы можете использовать redirect_to и даже отправлять параметры. В приведенном ниже примере вы отправляем параметры поиска
def catch_all
new_controller = View.find_by_name('aaa').controller
redirect_to :controller => new_controller, :action => :index,
:search => params[:search]
end
Ответ 3
Вот хорошее решение Rack Routing для SEO, предоставленное zetetic и Steve ross
Тестирование Rack Routing с использованием rSpec
Он показывает вам, как писать настраиваемый диспетчер (где вы можете выполнить поиск в db, если необходимо), а также с ограничениями и тестированием.
Ответ 4
Как было предложено в вопросе Rails маршрутизации для обработки нескольких доменов в одном приложении, я думаю, вы могли бы использовать Rails Routing - Advanced Constraints, чтобы построить то, что вам нужно.
Если у вас ограниченное пространство контроллеров (с неограниченными видами, указывающими на них), это должно сработать. Просто создайте ограничение для каждого контроллера, который проверяет соответствие текущего представления.
Предполагая, что у вас есть место из 2-х контроллеров (PostController и CommentController), вы можете добавить следующее к вашим маршрутам. rb:
match "*path" => "post#show", :constraints => PostConstraint.new
match "*path" => "comment#show", :constraints => CommentConstraint.new
Затем создайте файл lib/post_constraint.rb:
class PostConstraint
def matches?(request)
'post' == Rails.cache.fetch("/view_controller_map/#{request.params[:view_name]}") { View.find_by_name(request.params[:view_name]).controller }
end
end
Наконец, создайте lib/comment_constraint.rb:
class CommentConstraint
def matches?(request)
'comment' == Rails.cache.fetch("/view_controller_map/#{request.params[:view_name]}") { View.find_by_name(request.params[:view_name]).controller }
end
end
Вы можете сделать некоторые улучшения, например, определить класс супер ограничений, который извлекает кеш, поэтому вам не нужно повторять код и не рискуете получить неправильное имя ключа кеша в одном из ограничений.