A Rails-based static site generator.
Sponsored By Rails Designer
Start by adding Perron:
bundle add perron
Then generate the initializer:
rails generate perron:install
This creates an initializer:
Perron.configure do |config|
config.site_name = "AppRefresher"
end
Perron can operate in two modes, configured via config.mode
. This allows you to build either a full static site or integrate pages into a dynamic Rails application.
Mode | :standalone (default) |
:integrated |
---|---|---|
Use Case | Full static site for hosts like Netlify/Vercel | Add static pages to a live Rails app |
Output | output/ directory |
public/ directory |
Asset Handling | Via Perron | Via Asset Pipeline |
Perron is, just like Rails, designed with convention over configuration in mind. Content is stored in app/content/*/*.{erb,md}
. Content is backed by a class, located in app/models/content/
that inherits from Perron::Resource
.
The controllers are located in app/controllers/content/
. To make them available, create a route: resources :posts, module: :content, only: %w[index show]
.
bin/rails generate content Post
This will create the following files:
app/models/content/post.rb
app/controllers/content/posts_controller.rb
app/views/content/posts/index.html.erb
app/views/content/posts/show.html.erb
- Adds route:
resources :posts, module: :content, only: %w[index show]
To set a root page, include Perron::Root
in your Content::PagesController
and add a app/content/pages/root.[md,erb]
file (make sure to set slug: "/"
in its frontmatter).
This is automatically added when you create a Page
collection.
Perron supports markdown with the markdownify
helper.
There are no markdown gems bundled by default, so you'll need to add one of these:
- CommonMarker
- Kramdown
- Redcarpet
bundle add {commonmarker,kramdown,redcarpet}
Perron can post-process the HTML generated from your Markdown content.
Apply transformations by passing an array of processor names or classes to the markdownify
helper via the process
option.
<%= markdownify @resource.content, process: %w[target_blank lazy_load_images] %>
The following processors are built-in and can be activated by passing their string name:
target_blank
: Addstarget="_blank"
to all external links;lazy_load_images
: Addsloading="lazy"
to all<img>
tags.
You can create your own processor by defining a class that inherits from Perron::HtmlProcessor::Base
and implements a process
method.
Then, pass the class constant directly in the process
array.
# app/processors/add_nofollow_processor.rb
class AddNofollowProcessor < Perron::HtmlProcessor::Base
def process
@html.css("a[target=_blank]").each { it["rel"] = "nofollow" }
end
end
<%= markdownify @resource.content, process: ["target_blank", AddNofollowProcessor] %>
Perron can consume structured data from YML, JSON, or CSV files, making them available within your templates. This is useful for populating features, team members, or any other repeated data structure.
To use a data file, instantiate Perron::Site.data
with the basename of the file and iterate over the result.
<% Perron::Site.data.features.each do |feature| %>
<h4><%= feature.name %></h4>
<p><%= feature.description %></p>
<% end %>
By default, Perron looks up app/content/data/
for files with a .yml
, .json
, or .csv
extension.
For a features
call, it would find features.yml
, features.json
, or features.csv
. You can also provide a path to any data file, via Perron::Data.new("path/to/data.json")
.
The wrapper object provides flexible, read-only access to each record's attributes. Both dot notation and hash-like key access are supported.
feature.name
feature[:name]
The feeds
helper automatically generates HTML <link>
tags for your site's RSS and JSON feeds.
In your layout (e.g., app/views/layouts/application.html.erb
), add the helper to the <head>
section:
<head>
…
<%= feeds %>
…
</head>
To render feeds for specific collections, such as posts
:
<%= feeds only: %w[posts] %>
Similarly, you can exclude collections:
<%= feeds except: %w[pages] %>
Feeds are configured within the Resource
class corresponding to a collection:
# app/models/content/post.rb
class Content::Post < Perron::Resource
configure do |config|
config.feeds.rss.enabled = true
# config.feeds.rss.path = "path-to-feed.xml"
# config.feeds.rss.max_items = 25
config.feeds.json.enabled = true
# config.feeds.json.max_items = 15
# config.feeds.json.path = "path-to-feed.json"
end
end
The meta_tags
helper automatically generates SEO and social sharing meta tags for your pages.
In your layout (e.g., app/views/layouts/application.html.erb
), add the helper to the <head>
section:
<head>
…
<%= meta_tags %>
…
</head>
You can render specific subsets of tags:
<%= meta_tags only: %w[title description] %>
Or exclude certain tags:
<%= meta_tags except: %w[twitter_card twitter_image] %>
Values are determined with the following precedence, from highest to lowest:
Define a @metadata
instance variable in your controller:
class Content::PostsController < ApplicationController
def index
@metadata = {
title: "All Blog Posts",
description: "A collection of our articles."
}
@resources = Content::Post.all
end
end
Add values to the YAML frontmatter in content files:
---
title: My Awesome Post
description: A deep dive into how meta tags work.
image: /assets/images/my-awesome-post.png
author: Kendall
---
Your content here…
Set site-wide defaults in the initializer:
class Content::Post < Perron::Resource
# …
config.metadata.description = "AI-powered tool to keep your knowledge base articles images/screenshots and content up-to-date"
config.metadata.author = "Rails Designer"
end
Set site-wide defaults in the initializer:
Perron.configure do |config|
# …
config.metadata.description = "AI-powered tool to keep your knowledge base articles images/screenshots and content up-to-date"
config.metadata.author = "Rails Designer"
end
The related_resources
method allows to find and display a list of similar resources
from the same collection. Similarity is calculated using the TF-IDF algorithm on the content of each resource.
To get a list of the 5 most similar resources, call the method on any resource instance.
# app/views/content/posts/show.html.erb
@resource.related_resources
# Just the 3 most similar resources
@resource.related_resources(limit: 3)
A sitemap is an XML file that lists all the pages of a website to help search engines discover and index content more efficiently, typically containing URLs, last modification dates, change frequency, and priority values.
Enable it with the following line in the Perron configuration:
Perron.configure do |config|
# …
config.sitemap.enabled = true
# config.sitemap.priority = 0.8
# config.sitemap.change_frequency = :monthly
# …
end
Values can be overridden per collection…
# app/models/content/post.rb
class Content::Post < Perron::Resource
configure do |config|
config.sitemap.enabled = false
config.sitemap.priority = 0.5
config.sitemap.change_frequency = :weekly
end
end
…or on a resource basis:
# app/content/posts/my-first-post.md
---
sitemap_priority: 0.25
sitemap_change_frequency: :daily
---
When in standalone
mode and you're ready to generate your static site, run:
RAILS_ENV=production rails perron:build
This will create your static site in the configured output directory (output
by default).
Sites that use Perron.
This project uses Standard for formatting Ruby code. Please run be standardrb
before submitting pull requests. Run tests with rails test
.
Perron is released under the MIT License.