RailsCasts Pro episodes are now free!

Learn more or hide this

Paul Fioravanti's Profile

GitHub User: paulfioravanti

Comments by Paul Fioravanti

Avatar

I managed to get rid of the redirect loop mentioned, and make the routing a bit more robust and friendly-forwarding-ish, by doing the following (I used this example when I attempted to internationalize the Rails Tutorial:

config/routes.rb

ruby
SampleApp::Application.routes.draw do
  scope ":locale", locale: /#{I18n.available_locales.join("|")}/ do
    # ...
    match '/about',   to: 'static_pages#about'
    # ...

    # handles /valid-locale
    root to: 'static_pages#home', as: "locale_root"
    # handles /valid-locale/fake-path
    match '*path', to: redirect { |params, request| "/#{params[:locale]}" }
  end

  # handles /
  root to: redirect("/#{I18n.default_locale}")
  # handles /bad-locale|anything/valid-path
  match '/*locale/*path', to: redirect("/#{I18n.default_locale}/%{path}")
  # handles /anything|valid-path-but-no-locale
  match '/*path', to: redirect("/#{I18n.default_locale}/%{path}")
end

Since there are two root_paths, I renamed the one inside the :locale scope so there would be no conflicts in the app and tests. I tested the routes using RSpec as follows:

spec/routing/routing_spec.rb

ruby
require 'spec_helper'

describe "Routes" do

  describe "locale scoped paths" do
    I18n.available_locales.each do |locale|

      describe "routing" do
        it "should route /:locale to the root path" do
          get("/#{locale.to_s}").
            should route_to("static_pages#home", locale: locale.to_s)
        end
      end

      describe "redirecting", type: :request do

        subject { response }

        context "fake paths" do
          let(:fake_path) { "fake_path" }

          before { get "/#{locale.to_s}/#{fake_path}" }
          it { should redirect_to(locale_root_path(locale)) }
        end
      end

    end
  end

  describe "non-locale scoped paths" do

    describe "redirecting", type: :request do

      subject { response }

      context "no path given" do
        before { get "/" }
        it { should redirect_to(locale_root_path(I18n.default_locale)) }
      end

      context "a valid action" do
        let(:action) { "about" }
        let!(:default_locale_action_path) { about_path(I18n.default_locale) }

        context "with a valid but unsupported locale" do
          let(:unsupported_locale) { "fr" }

          before { get "/#{unsupported_locale}/#{action}" }
          it { should redirect_to(default_locale_action_path) }
        end

        context "with invalid information for the locale" do
          let(:invalid_locale) { "invalid" }

          before { get "/#{invalid_locale}/#{action}" }
          it { should redirect_to(default_locale_action_path) }
        end

        context "with no locale information" do
          before { get "/#{action}" }
          it { should redirect_to(default_locale_action_path) }
        end
      end

      context "invalid information" do
        let(:invalid_info) { "invalid" }

        before { get "/#{invalid_info}" }
        it { should redirect_to("/#{I18n.default_locale}/#{invalid_info}") }
        # This will then get caught by the "redirecting fake paths" condition
        # and hence be redirected to locale_root_path with I18n.default_locale
      end
    end
  end
end

I know that redirects are technically requests and their tests should really be under the spec/requests/ directory, but I figured they'd be more appropriate under routing since they're really just tests for exceptions and catch-alls. Anyway, hope this helps or is of interest to someone.