slimmer: what-slimmer-does
Slimmer is a piece of Rack Middleware that is inserted near the top of the middleware stack. Note that Slimmer is used in frontend apps such as finder-frontend, rather than publishing apps.
It takes a response from the Rails app, and a template from Static, and combines these into a single response. This allows us to do things like add 'Sign in' / 'Sign out' links to the header (or, more accurately, remove whichever link is not applicable) prior to rendering the page. This can't be done natively in Static because it would mean we can't cache responses from Static, which is a hosted service (i.e. templates are downloaded over HTTP). Slimmer is local to the app, so just a means of sharing the code that interfaces with Static, and can cache the fetched templates, locales and components.
How Slimmer works
- The Rails app defines the 'slimmer' gem as a dependency in the Gemfile.
- Slimmer bundles all its
lib
files in the gem, so that they're available to the Rails app. - The gem (and all its
lib/
files) isrequire
'd in to the Rails app via Bundler. As an aside, Rails'boot.rb
calls Bundler's setup script, which modifies theGEM_PATH
, meaning we don't need to explicitlyrequire
our Slimmer classes before we include them in the Rails app. - One of the pulled in classes is
Slimmer::Railtie
, which adds initialization steps to the Rails boot process to automatically invokeSlimmer::App
, which in turn initialisesSlimmer::Skin
where much of the functionality happens. - Back to the Rails app now. The Rails app defines what Slimmer template it needs, in the
ApplicationController
. - Every request to the app goes through the
ApplicationController
, and every response from the app therefore gets proxied throughSlimmer::App
. The response is passed to itscall
method, which delegates toSlimmer::Skin
to rewrite the response. -
Slimmer::Skin
lists all of the processors to be applied to the response. Each processor has its own class (e.g.Slimmer::Processors::AccountsShower
) responsible for defining the HTML to be removed, replaced or added. -
Slimmer::Skin
also determines which template to mix the response with. The template name is determined via mappings inSlimmer::Headers
, which is ultimately set in the Rails app via itsslimmer_template
call earlier, defaulting to "gem_layout". - The template name is converted to a URL that points to Static.
- Requests to Static get routed to the RootController, which route to the chosen template (e.g.
views/root/gem_layout.html.erb
). - The template is fetched and then cached using the Rails cache.
- The processors are mixed with the template, the headers recalculated and the response returned.
Templates
There are a few different templates in Static so that apps can render consistent error pages, core layout pages, pages without footer links and so on.
Apps can choose to look for templates somewhere other than Static by specifying the asset_host
.
Processors
TitleInserter
Takes the <title>
content from the Rails response and copies it into the template.
TagMover
Copies <script>
, <link>
, and <meta>
tags from the Rails response into the template's <head>
.
For <script>
and <link>
tags it only copies tags with a src
and href
attribute respectively, and only if a tag with a matching attribute doesn't already exist in the template.
For <meta>
tags, it only copies tags with a name
, and content
attribute, and only if a tag with matching attributes (and a matching http-eqiv
attribute) doesn't already exist.
ConditionalCommentMover
Takes any conditional comments from the Rails response, and appends them to the template's <head>
BodyInserter
Takes the entirety of the element #wrapper
(by default), and replaces the corresponding #wrapper
element in the template with it. Both the source and destination selector can be configured.
BodyClassCopier
Takes any classes applied to the <body>
element in the Rails response, and adds them to the <body>
element in the template. This will add to any classes already existing in the template.
HeaderContextInserter
Takes an element in the Rails response with a selector of .header-context
(by default), and replaces the corresponding element in the template with it. Does nothing unless the selector exists in both.
The .header-context
element typically wraps the 'breadcrumb' trail on the site.
SearchPathSetter
If there is an X-Slimmer-Search-Path
header in the Rails response, it finds the search form (form#search
) in the template, and replaces the action attribute with the value of the header.