Project

seiten

0.0
Low commit activity in last 3 years
A long-lived project that still receives updates
Seiten lets you create static pages for your Rails project and provides you with some cool navigational helper likes breadcrumb or custom navigations.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 2.10
>= 5.2
>= 4.2
 Project Readme

Seiten

Seiten gives your Rails application a static navigation structure for your static and dynamic pages.

Installation

Put the following line into your gem file:

gem 'seiten'

Setup routes

After you've run bundle install, add the following line to the bottom of your config/routes.rb to let your application pick up your seiten routes.

# config/routes.rb

seiten # sets params[:navigation_id] to 'application' by default

# OR

seiten :help # sets params[:navigation_id] to 'help'

# OR combine multiple

scope :help do
  seiten :help
end
seiten :application

The above adds a catch all route to your routes. Make sure your other routes are defined before it, otherwise seiten will swallow them.

If you want to setup a seiten page as your applications root page do the following:

# config/routes.rb
root :to => 'seiten/pages#show'

Routes per locale

A navigation built in every locale from one file (see One navigation file for every locale) gets a route per locale, under the locale and the navigation's own slug:

# config/routes.rb
seiten :help                # /en/help(/*slug), /de/hilfe(/*slug) → Help::PagesController#show
seiten :site, prefix: false # /en(/*slug), /de(/*slug)            → Site::PagesController#show
# config/locales/help.de.yml
de:
  navigations:
    help:
      title: "Hilfe"   # the navigation's slug, like a page's: "hilfe" (or set `slug`)
      account:
        title: "Dein Account"

The routes go to <navigation>/pages#show and are named <navigation>_page_<locale> (help_page_en, help_page_de). They set params[:locale] and params[:navigation_id], so current_navigation and current_page work in your controller. Draw a navigation without a slug (site) last, since it catches everything under the locale.

Every page gets a link helper named after the navigation and its key, and the navigation gets one that takes a key (for keys in variables). They link to the page in I18n.locale or the locale: option and pass other options on (anchor:, host:, query parameters):

help_account_manage_path                        # => "/en/help/your-account/manage-your-account" in English
help_account_manage_url(locale: :de)            # => "http://…/de/hilfe/dein-account/account-verwalten"
help_page_path(page.key)                        # the same; an unknown key raises ArgumentError
help_page_path                                  # => "/en/help", the navigation's root

prefix: false names the page helpers after their key only (pricing_path, account_manage_url). to:, as: (of the navigation's helper) and path: (the same in every locale) replace the defaults, and localized: false draws the single catch-all route instead. Navigations with one file per locale get the single catch-all route unless you pass localized: true. Draw localized routes outside of scope and namespace blocks; constraints blocks are fine.

Setup navigation structure

Seiten needs two things to work:

  • A YAML file where your navigation structure is stored
  • and the page files you defined in your navigation structure

To setup the navigation structure create a config/navigations directory and add a application.en.yml file

# config/navigations/application.en.yml

- title: 'Home'
  root: true
  layout: 'home'

- title: 'Products'
  layout:
    name: 'products'
    inherit: false
  nodes:
    - title: 'Logo Design'
      data:
        header_image: 'logo.jpg'
        description: 'Fuck fiverr. We deliver the most beautiful shit you have ever seen and probably can't afford.'
    - title: 'Web Development'
      data:
        header_image: 'web-development.jpg'
        description: 'PHP? Get the hell out of here.'
    - title: 'Hire us'
      refer: '/contact' # refers to /contact

- title: 'About'
  refer: true # refers to first child node (/about/our-team)
  nodes:
    - title: 'Our Team'
    - title: 'Works'
    - title: 'Partners'
      nodes:
        - title: 'Daniel Puglisi'
          refer: 'http://danielpuglisi.com'
        - title: 'Codegestalt'
          refer: 'http://codegestalt.com'
        - title: 'Kreatify'
          refer: 'http://kreatify.com'

- title: 'Contact'

You can define the following attributes in the your navigation yml file:

  • title: the title of the page
  • url (optional): the url attribute defines the slug of your page. Nested pages will automatically be prefixed with the url of their parent pages.
    • If you define nothing, url automatically uses a paramterize'd version of the title.
    • Prefix the url with a / to use absolute paths.
    • Set url to false to add a navigational element without a slug.
  • refer (optional): lets you link to another page
    • Accepts an absolute or external path.
    • If set to true, links to first child of page.
  • nodes (optional): lets you define the child pages of a page
  • layout (optional):
    • Per default the layout attribute is passed on to its children pages.
    • If you only want to set the layout for a single page and not for its children set inherit: false
  • data (optional):
    • Let's you add data to custom defined keys.
    • Must be a Hash.
    • The data can be accessed through symbols on the page object: current_page.data[:description]
  • html (optional):
    • Let's you add custom html options to page navigations which will be picked up by the seiten_navigation helper.
    • You can use the same options as when you would use content_tag.
    • Must be a Hash.

Make sure to restart your Rails server after changing the configuration file.

Setup navigations and pages

After defining the navigation structure make sure you have your static pages in place.

The Seiten helpers will look for the static pages in the app/pages directory. So the pages need to be placed and ordered in the same hierarchy as defined in your navigation yml config.

Example:

- app/
  |
  |- en/
    |
    |- pages/
      |
      |- contact.html.erb
      |- home.html.erb
      |- products.html.erb
      |
      |- about/
      |  |- our-team.html.erb
      |  |- partners.html.erb
      |  |- works.html.erb
      |
      |- products/
         |- hire-us.html.erb
         |- logo-design.html.erb
         |- web-development.html.erb

I18n and multiple navigations

seiten enables you to define multiple navigations and/or locales per navigation:

- config/
  |
  |- navigations/
    |
    |- application.en.yml
    |- application.de.yml
    |- help.de.yml
    |- help.en.yml

During initialization, seiten will try to find static pages according to your navigation filenames within your app/pages directory:

- app/
  |
  |- pages/
    |
    |- application/
    | |
    | |- en/
    | | |
    | | |- contact.html.erb
    | | |- home.html.erb
    | | |- products.html.erb
    | | |
    | | |- about/
    | | | |- our-team.html.erb
    | | | |- partners.html.erb
    | | | |- works.html.erb
    | | |
    | | |- products/
    | |   |- hire-us.html.erb
    | |   |- logo-design.html.erb
    | |   |- web-development.html.erb
    | |
    | |- de/
    |   |
    |   |- kontakt.html.erb
    |   |- home.html.erb
    |   |- produkte.html.erb
    |   |
    |   |- uber-uns/
    |   | |- unser-team.html.erb
    |   | |- partner.html.erb
    |   | |- arbeiten.html.erb
    |   |
    |   |- produkte/
    |       |- heuere-uns-an.html.erb
    |       |- logo-design.html.erb
    |       |- web-development.html.erb
    |
    |- help/
      |
      |- en/
      | |
      | |- # your help.en.yml pages
      |
      |- de/
        |
        |- # your help.de.yml pages

One navigation file for every locale

Instead of one file per locale, a navigation can be a single structure file without a locale (config/navigations/help.yml). Every page names a key, and seiten builds the navigation in every I18n.available_locales, taking titles and slugs from I18n, nested like the pages:

# config/navigations/help.yml
- key: start
  url: "/"            # options in the structure file apply to every locale
- key: account
  nodes:
    - key: manage
    - key: security
# config/locales/help.en.yml
en:
  navigations:
    help:
      start:
        title: "Start"
      account:
        title: "Your account"
        manage:
          title: "Manage your account"
        security:
          title: "Security"
          slug: "account-security"   # optional, defaults to the parameterized title

The page account.manage is your-account/manage-your-account in English and, with German titles, dein-account/account-verwalten in German. Its full key (account.manage) is the same in every locale, so page.translation(:de) finds it. The I18n scope (navigations) is Seiten.config[:i18n_scope]; title and slug can't be page keys. In development, navigations are built again when code or translations reload.

Linking pages across locales

Pages of navigations with the same name translate each other when they are at the same position in their navigation tree, so help.en.yml and help.de.yml usually mirror each other node by node:

page = current_navigation.pages.find_by(slug: 'introduction/modules')
page.translation(:de)  # => the page at the same position in help.de.yml, e.g. einleitung/module
page.translations      # => { "en" => page, "de" => … }, the page in every locale that has it

Use them for a language switcher or hreflang links. Building the URL stays with your routes.

Where the navigations don't line up (a page that exists in one language only, or pages in a different order), give the matching pages the same key in every language. Pages with a key are matched by their key, wherever they are; the others keep being matched by position:

# help.de.yml
- title: "Rechnungen"
  key: "invoices"

# help.en.yml
- title: "Pricing"  # English only: no translation
- title: "Invoices"
  key: "invoices"

Page#position gives a page's place in its tree, e.g. [2, 0] for the first child of the third top-level page.

Frontend helpers

To use seiten's frontend helpers include Seiten::Helpers::Frontend into your ApplicationHelper:

# app/helpers/application_helper.rb
class ApplicationHelper
  include Seiten::Helpers::Frontend
  ...
end

Navigation

To output your navigation structure as a <ul> list include the following helper into your layout:

<%= seiten_navigation %>

Per default the seiten_navigation method renders links which are nested two levels deep. You can render more levels with the deep parameters:

<%= seiten_navigation deep: 3 %>

If you want to output a subnavigation of a certain page, for example for the 'Partner' page in our example, you can do the following:

<%= seiten_navigation parent_id: current_navigation.find_by(slug: 'about/partners') %>

Breadcrumb

Seiten also has a breadcrumb helper:

<%= seiten_breadcrumb %>

this gives you a breadcrumb navigation of the current page which is separated by a > per default.

You can change the link_separater in the following way:

<%= seiten_breadcrumb link_separator: '>>' %>

Modify CSS Classes

The CSS classes generated by the seiten_navigation and seiten_breadcrumb helpers can be modified for all navigations by modifing the Seiten.config hash:

# config/initializers/seiten.rb

html_options = Seiten.config[:html]
html_options[:navigation][:base] = 'navigation'
html_options[:navigation][:item] = 'navigation__item'
html_options[:navigation][:nodes] = 'navigation__nodes'
html_options[:breadcrumb][:base] = 'breadcrumb'
html_options[:breadcrumb][:item] = 'breadcrumb__item'
html_options[:breadcrumb][:separator] = 'breadcrumb__separator'
html_options[:modifier][:base] = nil # if nil we use the main class combined with the element class
html_options[:modifier][:separator] = '--' # modifier separator
html_options[:modifier][:parent] = 'parent'
html_options[:modifier][:active] = 'active'
html_options[:modifier][:current] = 'current'
html_options[:modifier][:expanded] = 'expanded'

or per navigation using the html parameter:

seiten_navigation(html: { navigation: { base: 'navbar-start' } })

Backend helpers

seiten automatically includes Seiten::Helpers::Current into your ApplicationController by default.

If you have controllers that inherit directly from ActionController::Base, you can include Seiten::Helpers::Current manually:

class MyCustomBaseController < ActionController::Base
  include Seiten::Helpers::Current
  ...
end

This will give you access to the following methods:

current_navigation

current_navigation which returns the current Seiten::Navigation object. seiten tries to automatically find a match using params[:navigation_id] and params[:locale] or I18n.locale.

current_page

current_page which returns the current Seiten::Page object. seiten tries to automatically find a match within the current_navigation using params[:slug].

Integration with your controllers

seiten works with your existing controllers but requires some minor adjustments.

By adding pages to your config/navigations config files that match your existing controller routes, seiten will initially not be able to pick up the current_navigation as your controllers normally do not define params[:navigation_id].

There are two ways to solve this, either by adding default params to your routes or overwriting seiten setter methods in your controllers:

Routes

scope defaults: { navigation_id: 'application' } do
  resources :posts, defaults: { slug: 'blog' }
  # your other routes
end

seiten :application

Controller

class PostsController < ApplicationController
  ...

  private

  def set_current_navigation
    Seiten::Navigation.find_by(navigation_id: 'application', locale: I18n.locale)
  end

  def set_current_page
    current_navigation.pages.find_by(slug: 'blog')
  end
end

Todo

  • Improve documentation
  • Add rails generators (example config file, missing pages)
  • Add a rake task which outputs missing pages in app/pages

License

This project rocks and uses MIT-LICENSE.