Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
  app
  config
  db
  lib
  vendor
  README.md
  Rakefile
Size: Mime:
  README.md

Evergreen

Nestable pages engine for Rails from your pals at Neoteric Design, Inc.

Getting Started

Installation

# Gemfile
gem 'evergreen', '~> 5.0.2'
$ bundle install
$ rails g evergreen:install
$ rake db:migrate db:test:prepare

The install generator:

  • Sets up the evergreen_pages table
  • Bootstraps active_admin integration if ActiveAdmin is detected
  • Sets up an initial Page resource

You can also run the active_admin bootstrapper independently. This adds the appropriate JS/CSS needed for Evergreen's admin UI.

$ rails g evergeen:active_admin

You can generate additional page resources.

$ rails g evergreen:resource MemberPage

This generates:

  • Model
  • Model spec
  • Controller
  • Views
  • Routes
  • ActiveAdmin resource (if activeadmin present)

Usage

Out of the box, you get a Page model and a PagesController configured to use Evergreen. To create additional Page types, inherit a new model from Evergreen::Page

class Page < Evergreen::Page

end

class PagesController < ApplicationController
  include Evergreen::Controller
end

System Pages

Evergreen lets you define special Pages that can be defined and accessed programatically. By default you're set up with a home_page and Error handling pages.

Say you have an Events section and you want to have a page to represent it in the navigation, as well as some evergreen content to be displayed.

class Page < Evergreen::Page
 register_system_page :not_found, "Page Not Found"
 register_system_page :events, "Upcoming Events"
end

Now Page will have a class method events_page, that will either find or initialize a new page instance by that title 'Upcoming Events'. This means anywhere in your application you can consistently work with Page.events_page without worrying if it exists in the database, and users can save and edit its contents with gleeful abandon (as long as the title matches).

Strong parameters

Evergreen provides a helper to help whitelisting its form attributes.

ActiveAdmin:

permit_params Evergreen.params

Standard controller:

params(:page).permit(Evergreen.params)

Routing

Evergreen now just needs one route statement per class. Say you had two kinds of pages, regular Page, and some kind of protected PrivatePage.

root to: 'pages#home'

scope 'private' do
  root to: 'private_pages#home'
  get '*path' => 'private_pages#show', as: :private_page
end

get '*path' => 'pages#show', :as => :page

You can use scopes or prefixes, but scopes are preferred so you can cleanly set a root 'home' page. The routes should use a wildcard path. Naming the route after the model lets Linking Just Work. Pages only store their relative path, so page look up is as easy as find_by_path(params[:path]) with Rails matching the route by model name (unless you explicitly specify it.

Remember routes are read line by line so the more general, root Page should go last!

Linking

<%= link_to page.navigation_label, page %>
<%= link_to page.navigation_label, page_path(page) # Explicitly state route %>

The page model

Instance methods

Method name Description
title Page title
navigation_label Label used for navigation links. Defaults to title
path Path used for Routing
body Body content
hidden Boolean value for whether the page should be listed in nav
state Pubdraft state. "published" or "drafted"
meta_description Meta description for page head
slug Slugified navigation label (or title) for generated paths
update_path! Regenerates path (and updates the DB), returns the new path

Class methods

Method name Description
find_by_path(path) Look up by relative path, can throw RecordNotFound
register_system_path(identifier, title See System Pages

Scopes

Name Description
published Publicly accessible pages, including hidden pages
visible Publicly accessible pages, excluding hidden pages

Helpers

ViewHelper

home?

  • Check if rendering a home page

browser_title(*titles)

  • Build a browser page title.
browser_title('Site Name')
#=> 'Site Name'
browser_title('Site Name', 'Some Section', @page.title)
#=> 'The Page Title - Some Section - Site Name'

Controller actions

class YourPagesController < ApplicationController
  include Evergreen::Controller
end

Provides the following actions:

# page_class is determined from controller name, override if necessary
def home
  @page = page_class.home_page
end

def show
  @page = page_class.visible.find_by_path(params[:path])
end

Error Handling

Evergreen can gracefully display CMS content when your application raises an exception. After running the install generator, you'll have something like this in your ApplicationController:

# app/controllers/application_controller.rb
  include Evergreen::ErrorHandling

  unless Rails.env.development?
    rescue_from 'StandardError' do |exception|
      render_server_error(Page.server_error_page, exception: exception)
    end
    rescue_from 'ActionView::MissingTemplate', 'ActiveRecord::RecordNotFound' do
      render_not_found(Page.not_found_page)
    end
  end

By default it sets you up to capture missing formats or record not found errors and display a 404 page. Everything else is captured and renders a 500 page.

render_not_found(page, exception:, options)

  • Renders the given page, with the given options, defaults to HTTP code 404.

server_error(exception)

  • Renders the given page, with the given options, defaults to HTTP code 500.

log_error(exception)

  • Convenience method called within render_server_error to log the caught exception. Override if you wish.

report_error(exception)

  • Convenience method called within render_server_error to report the exception to an external service (default: Raygun). Override if you wish.

ActiveAdmin Integration

You must have ActiveAdmin installed first!

$ rails g evergreen:active_admin
ActiveAdmin.register Page do
  include Evergreen::Integrations::ActiveAdmin

  menu :priority => 1
  actions :all, except: [:show]
  form partial: 'evergreen/admin/form'

  permit_params Evergreen.params

  sortable_tree
end

See lib/evergreen/integrations/active_admin.rb for everything it's providing under the hood.

Testing

Evergreen provides an RSpec linter for your models

describe Page, type: :model do
  include Evergreen::SpecHelpers::Lint
end

Running the gem's test suite:

$ rspec

TODO

  • Reduce DB thrashing when using sortable tree. Still preferable to the old way of thrashing the DB upon every. single. lookup. But we can do better!
  • Add generating of routing test for post-install

Further Reading and Thanks