Orchid contains middleware which allows your app to redirect or rewrite URLs.
This may be useful if you are moving to Orchid from an older website and are
updating the URL structure, or would like to clean up URLs with .html
and
.php
. Copy the config/redirects.example.yml
file to whatever name you would
like (config/redirects.yml
for our purposes) and enable it in the
config/public.yml
file. You may add more than one redirect file if you wish:
# config/public.yml
default: &default
app_options:
redirect_files:
- config/redirects.yml
- config/redirects_by_section.yml
To get started quickly, follow the examples in
config/redirects.example.yml
.
The file is formatted as a list of associative arrays so each rule begins with
-
and following indented lines have the rule's keys and values. Every rule
must have the method
, from
, and to
keys and values.
method
determines whether the rule is:
- a redirect (and which HTTP Redirect
Status
Code
is returned) if the value is
r301
,r302
,r303
,r307
, orr308
- a rewrite if the value is
rewrite
from
is either a string or regular expression preceded by !ruby/regexp
that
is matched against the request path and query string to determine when redirects
and rewrites occur.
to
is a string which defines the destination URL for the redirect or rewrite.
The string may contain numbered backreferences like $1
which will be replaced
by substrings captured in the from
value if it is a regular expression.
options
may be added for further rule conditions. The available options
keys
are:
scheme
- The request scheme must match this string or regular expression, e.g.http
orhttps
host
- The host for the request must match this string or regular expression, e.g.cdrh.unl.edu
method
- The request must match this HTTP request method, e.g.GET
,POST
,DELETE
, etcnot
- The path and query string must not match this string or regular expression. This allows excluding some matches for thefrom
value.drop_qs
- The query string will be dropped and not added toto
, the destination URL. It will still be matched against thefrom
value though.
(Are you looking for section routes?)
Orchid's routes load after the application's routes by default. This means that
generally you may add routes to the app's config/routes.rb
file as normal.
Orchid will detect and avoid overriding any named routes which it might
otherwise have set.
You may need to add a route to your application after Orchid's routes have been
drawn. For example, you may wish to add a greedy route which would otherwise
capture many following routes, e.g. /:id
would capture paths to Orchid routes
for /about
and /browse
if drawn first. In this case, you will need to call
Orchid's method to draw its routes before yours.
# Draw all of Orchid's routes on-demand, skipping any named routes already drawn
Orchid::Routing.draw
If you are adding a named route normally drawn by Orchid, you will need to
prevent Orchid from drawing that route. Orchid's draw
method accepts a keyword
argument routes
as an array of specific route names to draw.
# Draw specific Orchid routes on-demand, skipping any named routes already drawn
Orchid::Routing.draw(routes: ["home", "item"])
Using Rails's route scoping does not work if wrapped around the Orchid route
drawing. To scope the routes that Orchid draws, an additional keyword parameter
scope
must be passed to the draw
method with the same string value that
would be passed to the Rails scope
method, e.g.
# Fails to scope Orchid routes
scope '/orchid-sub-uri' do
# Other route definitions
…
Orchid::Routing.draw
end
# Successfully scopes Orchid routes
scope '/orchid-sub-uri' do
# Other route definitions
…
Orchid::Routing.draw(scope: '/orchid-sub-uri')
end
The Orchid::Routing.draw(scope: …)
call can be outside a Rails scope
block,
but it will likely be easier to follow to keep it alongside other scoped routes.
If you are adding routes to an app which supports multiple languages, and you would like the routes to be available to both, you will need to set the scope:
scope "(:locale)", locale: locales do
get '/about/team', to: 'general#about_team', as: 'about_team'
end
The app display the same item at a variety of paths, for example, if using sections or using custom URLs to group items according to criteria such as category / subcategory.
This isn't good for a number of reasons, such as SEO. Preferably, each item will only show up at one canonical path. How does Orchid know which URL this is?
First off, it's recommended to lock down your item routes to recognize if they've
been asked to display an item incorrectly. In the below example,
the life/:id
route is only prepared to display four documents. the
writings/books/:id
route is looking for ids such as 0001
or nf060
.
…
get 'life/:id', to: 'life#show', as: :life_item,
constraints: { id: /chronology|shortbio|longbio|woodress/ }
get 'writings/books/:id', to: 'items#show', as: :writings_book_item,
constraints: { id: /(\d{4}|nf006(?:_\d{2})?|nf060|nf061)/ }
…
Typically, we can write links in views and partials which use the named path, such as
life_item_path(id: item_id)
writings_books_item_path(id: item_id)
But when creating links dynamically, such as in search results, we need to set up some rules to help the app decide which path is appropriate.
This is facilitated by overriding the
helper method used to render them in app/helpers/items_helper.rb
:
module ItemsHelper
include Orchid::ItemsHelper
def search_item_link(item)
category = item["category"].downcase
item_id = item["identifier"][/^cat\.(.+)$/, 1]
path = "#"
title_display = item["title"].present? ?
item["title"] : t("search.results.item.no_title", default: "Untitled")
if category == "life"
item_id = item_id[/^life\.(.+)$/, 1]
path = life_item_path(id: item_id)
elsif category == "writing"
path = writings_book_item_path(id: item_id)
end
link_to title_display, path
end
end
The above example uses category
to determine the appropriate route, but your
logic may be quite different depending on how you will be selecting routes for
pages.