new middleware: locale redirection (based on the prefix_default_locale site property)

did committed Feb 20, 2015
commit c7dbaaf781d9db03f6302fe4fc85c0727b2f7834
Showing 10 changed files with 198 additions and 20 deletions
locomotive/steam/middlewares.rb b/lib/locomotive/steam/middlewares.rb +1 -14
@@ @@ -1,20 +1,7 @@
require_relative 'middlewares/threadsafe'
require_relative 'middlewares/helpers'
- require_relative 'middlewares/favicon'
- require_relative 'middlewares/site'
- require_relative 'middlewares/default_env'
- require_relative 'middlewares/locale'
- require_relative 'middlewares/timezone'
- require_relative 'middlewares/logging'
- require_relative 'middlewares/path'
- require_relative 'middlewares/entry_submission'
- require_relative 'middlewares/page'
- require_relative 'middlewares/templatized_page'
-
- require_relative 'middlewares/dynamic_assets'
-
- require_relative 'middlewares/renderer'
+ require_relative_all 'middlewares'
module Locomotive::Steam
module Middlewares
locomotive/steam/middlewares/helpers.rb b/lib/locomotive/steam/middlewares/helpers.rb +12 -0
@@ @@ -22,6 +22,18 @@ module Locomotive::Steam
@next_response = [type, { 'Content-Type' => 'text/html', 'Location' => location }, []]
end
+ def modify_path(path = nil, &block)
+ path ||= request.path
+
+ segments = path.split('/')
+ yield(segments)
+ path = segments.join('/')
+
+ path = '/' if path.blank?
+ path += "?#{request.query_string}" unless request.query_string.empty?
+ path
+ end
+
def log(msg, offset = 2)
Locomotive::Common::Logger.info (' ' * offset) + msg
end
locomotive/steam/middlewares/locale.rb b/lib/locomotive/steam/middlewares/locale.rb +8 -1
@@ @@ -2,11 +2,15 @@ module Locomotive::Steam
module Middlewares
# Set the locale from the path if possible or use the default one
+ #
# Examples:
+ #
# /fr/index => locale = :fr
# /fr/ => locale = :fr
# /index => locale = :en (default one)
#
+ # The
+ #
class Locale < ThreadSafe
include Helpers
@@ @@ -30,7 +34,10 @@ module Locomotive::Steam
if _path =~ /^\/(#{site.locales.join('|')})+(\/|$)/
_locale = $1
_path = _path.gsub($1 + $2, '')
- # _path = 'index' if _path.blank? # TODO
+
+ # let the other middlewares that the locale was
+ # extracted from the path.
+ env['steam.locale_in_path'] = true
end
env['steam.path'] = _path
locomotive/steam/middlewares/locale_redirection.rb b/lib/locomotive/steam/middlewares/locale_redirection.rb +62 -0
@@ @@ -0,0 +1,62 @@
+ module Locomotive::Steam
+ module Middlewares
+
+ # Redirect to the same page with or without the locale in the url
+ # based on the "prefix_default_locale" property of the current site.
+ #
+ # See the specs (spec/unit/middlewares/locale_redirection_spec.rb) for more details.
+ #
+ class LocaleRedirection < ThreadSafe
+
+ include Helpers
+
+ def _call
+ if url = redirect_url
+ redirect_to url
+ end
+ end
+
+ protected
+
+ def redirect_url
+ if apply_redirection?
+ if site.prefix_default_locale
+ path_with_default_locale if locale_not_mentioned_in_path?
+ else
+ path_without_default_locale if default_locale? && locale_mentioned_in_path?
+ end
+ end
+ end
+
+ def apply_redirection?
+ site.locales.size > 1 && request.get?
+ end
+
+ def default_locale?
+ locale.to_s == site.default_locale.to_s
+ end
+
+ def locale_mentioned_in_path?
+ env['steam.locale_in_path']
+ end
+
+ def locale_not_mentioned_in_path?
+ !locale_mentioned_in_path?
+ end
+
+ def path_with_default_locale
+ modify_path do |segments|
+ segments.insert(1, site.default_locale)
+ end
+ end
+
+ def path_without_default_locale
+ modify_path do |segments|
+ segments.delete_at(1)
+ end
+ end
+
+ end
+ end
+
+ end
locomotive/steam/repositories/filesystem/models/site.rb b/lib/locomotive/steam/repositories/filesystem/models/site.rb +4 -1
@@ @@ -11,7 +11,10 @@ module Locomotive
attr_accessor :root_path
def initialize(attributes = {})
- super({ timezone: 'UTC' }.merge(attributes))
+ super({
+ timezone: 'UTC',
+ prefix_default_locale: false
+ }.merge(attributes))
end
def default_locale
locomotive/steam/server.rb b/lib/locomotive/steam/server.rb +1 -0
@@ @@ -51,6 +51,7 @@ module Locomotive::Steam
Middlewares::Timezone,
Middlewares::EntrySubmission,
Middlewares::Locale,
+ Middlewares::LocaleRedirection,
Middlewares::Path,
Middlewares::Page,
Middlewares::TemplatizedPage
spec/integration/server/basic_spec.rb +1 -1
@@ @@ -111,7 +111,7 @@ describe Locomotive::Steam::Server do
describe 'translations' do
it 'translates strings' do
- get '/en'
+ get '/'
expect(last_response.body).to include 'Powered by'
get '/fr'
expect(last_response.body).to include 'Propulsé par'
spec/support/helpers.rb +4 -0
@@ @@ -36,5 +36,9 @@ module Spec
def default_fixture_site_path
File.expand_path('../../fixtures/default/', __FILE__)
end
+
+ def env_for(url, opts={})
+ Rack::MockRequest.env_for(url, opts)
+ end
end
end
spec/unit/middlewares/locale_redirection_spec.rb +105 -0
@@ @@ -0,0 +1,105 @@
+ require 'spec_helper'
+
+ require_relative '../../../lib/locomotive/steam/middlewares/threadsafe'
+ require_relative '../../../lib/locomotive/steam/middlewares/helpers'
+ require_relative '../../../lib/locomotive/steam/middlewares/locale_redirection'
+
+ describe Locomotive::Steam::Middlewares::LocaleRedirection do
+
+ let(:prefixed) { false }
+ let(:site) { instance_double('Site', prefix_default_locale: prefixed, default_locale: :de, locales: %w(de fr)) }
+ let(:url) { 'http://models.example.com' }
+ let(:app) { ->(env) { [200, env, 'app'] } }
+ let(:middleware) { Locomotive::Steam::Middlewares::LocaleRedirection.new(app) }
+ let(:locale) { 'de' }
+ let(:locale_in_path) { true }
+
+ subject do
+ env = env_for(url, 'steam.site' => site, 'steam.locale' => locale, 'steam.locale_in_path' => locale_in_path)
+ env['steam.request'] = Rack::Request.new(env)
+ code, env = middleware.call(env)
+ [code, env['Location']]
+ end
+
+ describe 'not prefixed by locale' do
+
+ describe 'strip default locale from root path' do
+ let(:url) { 'http://models.example.com/de' }
+ it { is_expected.to eq [301, '/'] }
+ end
+
+ describe 'strip default locale' do
+ let(:url) { 'http://models.example.com/de/hello' }
+ it { is_expected.to eq [301, '/hello'] }
+ end
+
+ describe 'strip default locale from root path with query' do
+ let(:url) { 'http://models.example.com/de?this=is_a_param' }
+ it { is_expected.to eq [301, '/?this=is_a_param'] }
+ end
+
+ describe 'strip default locale from path with query' do
+ let(:url) { 'http://models.example.com/de/hello?this=is_a_param' }
+ it { is_expected.to eq [301, '/hello?this=is_a_param'] }
+ end
+
+ describe 'dont strip a non-default locale' do
+ let(:locale) { 'fr' }
+ let(:url) { 'http://models.example.com/fr/hello' }
+ it { is_expected.to eq [200, nil] }
+ end
+
+ describe 'dont redirect URL without locale' do
+ let(:locale) { :de }
+ let(:locale_in_path) { false }
+ let(:url) { 'http://models.example.com/hello' }
+ it { is_expected.to eq [200, nil] }
+ end
+
+ end
+
+ describe 'prefixed by locale' do
+
+ let(:prefixed) { true }
+
+ describe 'without locale' do
+
+ let(:locale_in_path) { false }
+
+ describe 'add default locale to root path' do
+ let(:url) { 'http://models.example.com/' }
+ it { is_expected.to eq [301, '/de'] }
+ end
+
+ describe 'add default locale to long path' do
+ let(:url) { 'http://models.example.com/hello/world' }
+ it { is_expected.to eq [301, '/de/hello/world'] }
+ end
+
+ describe 'add default locale to url with path and query' do
+ let(:url) { 'http://models.example.com/hello/world?this=is_me' }
+ it { is_expected.to eq [301, '/de/hello/world?this=is_me'] }
+ end
+
+ end
+
+ describe 'with locale' do
+
+ let(:locale_in_path) { true }
+
+ describe 'dont add default locale if already present' do
+ let(:url) { 'http://models.example.com/de/hello/world' }
+ it { is_expected.to eq [200, nil] }
+ end
+
+ describe 'dont add default locale to localized path' do
+ let(:locale) { 'fr' }
+ let(:url) { 'http://models.example.com/fr/hello/world' }
+ it { is_expected.to eq [200, nil] }
+ end
+
+ end
+
+ end
+
+ end
spec/unit/middlewares/renderer_spec.rb +0 -3
@@ @@ -26,7 +26,4 @@ describe Locomotive::Steam::Middlewares::Renderer do
end
- def env_for(url, opts={})
- Rack::MockRequest.env_for(url, opts)
- end
end