Redirecionando rotas de ID antigas para novos slugs

Acabei de atualizar meus modelos para usar slugs instalando a gema Stringex . Agora eu queria redirecionar aqueles URLs antigos que meus usuários conhecem e usam para os novos URLs de slug.

/answers/3 => /answers/everything

Mas, uma vez que alguém poderia propor algum título de resposta com dígitos completos (qualquer que seja a pergunta), ainda devemos permitir URLs com dígitos completos como:

/answers/42

Claro, no caso improvável de alguém clicar em um link desatualizado como / answers / 42 com 42 não sendo um slug, mas um ID de antes da mudança de rota – enquanto, nesse ínterim, outra pessoa criou uma nova resposta com aquele título de dígito, ele não obterá a resposta que procurava. Mas hey, ainda será uma resposta!

Solução

Desative a rota #show padrão e crie uma #show rota que aceite um ID ou slug.

# config/routes.rb
resources
:answers, :except => :show
get "answers/:id_or_slug" => 'answers#show', :as => :answer

Em seguida, aprimore a ação #show do seu controlador para redirecionar com um 301 (movido permanentemente) se nenhuma instância foi encontrada usando o slug, mas usando o ID. E gere um RoutingError se nenhuma instância for encontrada.

# app/controllers/answers_controller.rb
def show
@answer = Answer.find_by_url(params[:id_or_slug])
if @answer.nil?
@answer = Answer.find_by_id(params[:id_or_slug])
if @answer.nil?
raise ActionController::RoutingError.new('Not Found')
else
redirect_to
@answer, :status => 301
end
end
end

Observação: se você estiver usando carregadores de recursos como CanCan, será necessário desativar o carregamento de recursos para a ação #show:

# app/controllers/answers_controller.rb
load_and_authorize_resource
:except => :show, :find_by => :url
authorize_resource
:only => :show