URL - Pflege

Aus verschieden Gründen kann es erforderlich sein, bereits bestehende URLs zu verändern (z.B. wegen SEO). Natürlich sollten die alten Routen trotzdem noch funktionieren. Denn immerhin könnte eine URL gespeichert oder gar verlinkt worden sein.

Veraltete Routen

Ein Beispiel könnten Landing-Pages für Städte sein.
Die URLs zu den Städte-Seiten:

# config/routes.rb
Rails.application.routes.draw do
  resource :city, only: :show
end

sehen anhand einer eindeutigen Slug beispielsweise für Berlin so aus: reisen.de/city/berlin.

Veraltete Routen umleiten

Nachdem diese Landing-Pages live gegangen sind, sollen später folgende Routen die Städte anzeigen: reisen.de/nach/berlin. Natürlich sollen die alten Routen immer noch funktionieren.
Im Routes-Generator von Rails steht dafür der Helper #redirect zur Verfügung. Grundsätzlich leitet er die alten Routen auf die neuen Routen um. Außerdem sendet er automatisch einen Status-Code 301 an den Browser. Die meisten Browser entfernen darauf hin die veraltete Route und cachen stattdessen die neue Route.
Der Routes-Generator sieht für das Beispiel nun so aus:

# config/routes.rb
Rails.application.routes.draw do
  # neue Route
  resource :city, as: 'nach', only: :show
  # veraltete Route wird auf neue Route umgeleitet
  get 'city/:slug', to: redirect('nach/%{slug}')
end

Redirects mit Abhängigkeiten

Sobald der redirect von Logik abhängt, bietet es sich an, diese Logik in eine eigene Klasse auszulagern, allein schon um die routes.rb übersichtlich zu behalten. Aber auch, um mehrere gleichartige Umleitungen zu behandeln.
Ein klassisches Beispiel wäre, nur die Städte zu der neuen Route umzuleiten, die überhaupt schon produktiv sind. Also alle Städte, die bis zu einem Zeitpunkt im System erstellt wurden.
Für neu hinzukommende Städte soll verständlicherweise die veraltete Route-Struktur nicht mehr angewendet und stattdessen die 404-Page gerendert werden.
Der erweiterte Routes-Generator:

# config/routes.rb
Rails.application.routes.draw do
  resource :city, as: 'nach', only: :show
  # Der CityRedirector soll nur die Routen
  # für die relevanten Städte umleiten
  get 'city/:slug', to: redirect('CityRedirector')
end

Der CityRedirector lädt einmalig alle Städte-Slugs, die bis zum 01.01.2018 erstellt wurden und prüft, ob der Slug-Parameter der angeforderten Stadt enthalten ist. Falls nicht, wird kein Redirect ausgeführt:

class CityRedirector
  NEW_CITY_SLUG_DATE = Date.new 2018, 1, 1

  def self.call(params, request)
    "nach/#{params[:slug]}" if legacy_city_slugs.include?(params[:slug])
  end

  private

  def self.legacy_city_slugs
    @@legacy_city_slugs ||= City.where('created_at < ?', NEW_CITY_SLUG_DATE)
                                .pluck('slug')
  end
end

Sofern es die Logik zuläßt, kann alternativ die Geschwindigkeit, mit der ein redirect ausgeführt wird, optimiert werden, indem er bereits im Webserver verarbeitet wird. Also zum Beispiel mod_rewrite beim Apache (aktuelle Version 2.4).
Das führt dann allerdings dazu, dass Routen an verschiedenen Stellen gepflegt werden müssen. Außerdem liegt der Fokus bei veralteten Routen eher nicht auf Performance.