middleware to remove the SEO trailing slash

did committed Feb 19, 2015
commit d242687e92b191208c36bdb19223a451a64a4ec9
Showing 10 changed files with 86 additions and 19 deletions
Gemfile.lock +5 -3
@@ @@ -19,6 +19,7 @@ PATH
moneta (~> 0.8.0)
morphine (~> 0.1.1)
rack-cache (~> 1.2)
+ rack-rewrite (~> 1.5.1)
rack_csrf (~> 2.5.0)
sprockets (~> 2.12.3)
sprockets-sass (~> 1.3.1)
@@ @@ -54,14 +55,14 @@ GEM
debugger-linecache (~> 1.2)
slop (~> 3.6)
chronic (0.10.2)
- chunky_png (1.3.3)
+ chunky_png (1.3.4)
codeclimate-test-reporter (0.4.6)
simplecov (>= 0.7.1, < 1.0.0)
coderay (1.1.0)
coffee-script (2.3.0)
coffee-script-source
execjs
- coffee-script-source (1.9.0)
+ coffee-script-source (1.9.1)
colorize (0.7.5)
columnize (0.9.0)
compass (1.0.3)
@@ @@ -142,6 +143,7 @@ GEM
rack (1.6.0)
rack-cache (1.2)
rack (>= 0.4)
+ rack-rewrite (1.5.1)
rack-test (0.6.3)
rack (>= 1.0)
rack_csrf (2.5.0)
@@ @@ -173,7 +175,7 @@ GEM
rspec-mocks (3.1.3)
rspec-support (~> 3.1.0)
rspec-support (3.1.2)
- sass (3.4.11)
+ sass (3.4.12)
simplecov (0.9.1)
docile (~> 1.1.0)
multi_json (~> 1.0)
locomotive/steam/configuration.rb b/lib/locomotive/steam/configuration.rb +20 -1
@@ @@ -22,7 +22,8 @@ module Locomotive
# Manage the list of middlewares used by the rack stack.
#
- # Examples:
+ # Example:
+ #
# Locomotive::Steam.configure do |config|
# ...
# config.middleware.remove Rack::Lint
@@ @@ -98,6 +99,24 @@ module Locomotive
attr_accessor :moneta
def moneta; @moneta.nil? ? { store: Moneta.new(:Memory, expires: true) } : @moneta; end
+ # Lambda called once a Services instance has been built.
+ # It is used when we want to change one of the services
+ #
+ # Example:
+ #
+ # Locomotive::Steam.configure do |config|
+ #
+ # config.services_hook = -> (services) {
+ # require 'my_repositories'
+ # services.repositories = MyRepositories.new
+ # }
+ #
+ # end
+ #
+ # default: nil
+ #
+ attr_accessor :services_hook
+
end
end
locomotive/steam/decorators/template_decorator.rb b/lib/locomotive/steam/decorators/template_decorator.rb +2 -0
@@ @@ -1,3 +1,5 @@
+ require_relative 'i18n_decorator'
+
module Locomotive
module Steam
module Decorators
locomotive/steam/middlewares/stack_proxy.rb b/lib/locomotive/steam/middlewares/stack_proxy.rb +8 -8
@@ @@ -9,16 +9,16 @@ module Locomotive::Steam::Middlewares
instance_eval(&block) if block_given?
end
- def use(*args)
- @list << args
+ def use(*args, &block)
+ @list << [args, block]
end
- def insert_before(index, *args)
- @list.insert(index_of(index), args)
+ def insert_before(index, *args, &block)
+ @list.insert(index_of(index), [args, block])
end
- def insert_after(index, *args)
- @list.insert(index_of(index) + 1, args)
+ def insert_after(index, *args, &block)
+ @list.insert(index_of(index) + 1, [args, block])
end
def delete(index)
@@ @@ -29,7 +29,7 @@ module Locomotive::Steam::Middlewares
def inject(builder)
@list.each do |args|
- builder.use(*args)
+ builder.use(*(args[0]), &args[1])
end
end
@@ @@ -37,7 +37,7 @@ module Locomotive::Steam::Middlewares
if index.is_a?(Integer)
index
else
- @list.index { |args| args[0] == index }
+ @list.index { |args| args[0][0] == index }
end
end
locomotive/steam/server.rb b/lib/locomotive/steam/server.rb +2 -0
@@ @@ -4,6 +4,7 @@ require 'mimetype_fu'
require 'mime-types'
require 'mime/types'
+ require 'rack/rewrite'
require 'rack/csrf'
require 'rack/session/moneta'
require 'rack/builder'
@@ @@ -21,6 +22,7 @@ module Locomotive::Steam
server, configuration = self, self.configuration
-> (stack) {
+ use(Rack::Rewrite) { r301 %r{^/(.*)/$}, '/$1' }
use Middlewares::Favicon
if configuration.serve_assets
locomotive/steam/services.rb b/lib/locomotive/steam/services.rb +5 -1
@@ @@ -7,7 +7,11 @@ module Locomotive
module Services
def self.build_instance(request = nil)
- Instance.new(request)
+ Instance.new(request).tap do |service|
+ if Locomotive::Steam.configuration.services_hook
+ Locomotive::Steam.configuration.services_hook.call(service)
+ end
+ end
end
class Instance < Struct.new(:request)
locomotivecms_steam.gemspec +1 -0
@@ @@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
spec.add_dependency 'httparty', '~> 0.13.3'
spec.add_dependency 'chronic', '~> 0.10.2'
+ spec.add_dependency 'rack-rewrite', '~> 1.5.1'
spec.add_dependency 'rack-cache', '~> 1.2'
spec.add_dependency 'dragonfly', '~> 1.0.7'
spec.add_dependency 'moneta', '~> 0.8.0'
spec/integration/server/basic_spec.rb +12 -0
@@ @@ -60,6 +60,18 @@ describe Locomotive::Steam::Server do
end
+ describe 'seo trailing slash' do
+
+ let(:url) { '/events/' }
+ subject { get url; last_response }
+
+ it 'redirects to the url without the trailing slash' do
+ expect(subject.status).to eq(301)
+ expect(subject.location).to eq('/events')
+ end
+
+ end
+
describe 'snippets' do
it 'includes a basic snippet' do
spec/unit/middlewares/stack_proxy_spec.rb +6 -6
@@ @@ -16,7 +16,7 @@ describe Locomotive::Steam::Middlewares::StackProxy do
it 'adds it to the list' do
expect(subject.list.size).to eq 1
- expect(subject.list.first).to eq [DefaultMiddleware]
+ expect(subject.list.first).to eq [[DefaultMiddleware], nil]
end
end
@@ @@ -28,7 +28,7 @@ describe Locomotive::Steam::Middlewares::StackProxy do
it 'adds it to the operations' do
expect(proxy.list.size).to eq 1
- expect(proxy.list.first).to eq [DefaultMiddleware]
+ expect(proxy.list.first).to eq [[DefaultMiddleware], nil]
end
end
@@ @@ -49,10 +49,10 @@ describe Locomotive::Steam::Middlewares::StackProxy do
it do
is_expected.to eq([
- [DefaultMiddleware],
- [FooMiddleware],
- [FancyMiddleware],
- [BarMiddleware, { answer: 42 }]
+ [[DefaultMiddleware], nil],
+ [[FooMiddleware], nil],
+ [[FancyMiddleware], nil],
+ [[BarMiddleware, { answer: 42 }], nil]
])
end
spec/unit/services_spec.rb +25 -0
@@ @@ -0,0 +1,25 @@
+ require 'spec_helper'
+
+ describe Locomotive::Steam::Services do
+
+ subject { Locomotive::Steam::Services.build_instance(nil) }
+
+ describe 'configuration with a services hook' do
+
+ before do
+ Locomotive::Steam.configure do |config|
+ config.services_hook = -> (services) {
+ services.repositories = MyService.new
+ }
+ end
+ end
+
+ after { Locomotive::Steam.configure { |c| c.services_hook = nil } }
+
+ it { expect(subject.repositories).to be_instance_of(MyService) }
+
+ end
+
+ class MyService; end
+
+ end