Perron:「Railsベースの」静的サイトジェネレータ(翻訳)
PerronというRailsベースの静的サイトジェネレータ(SSG)をオープンソースで公開しましたので、晴れてここに紹介いたします。Perronの構築には数年を要しました。といってもずっと開発し続けていたわけではなく、ここ数か月の間に毎週数時間ずつ開発しただけですが、概念として何年もの間温め続けてきたものです。
試してみたい方は、ぜひ以下のリポジトリをチェックアウトするか、ドキュメントを参照してください。
「え、2025年にもなって静的サイトジェネレータをまた作ったの?」「静的サイトジェネレータなんて数百どころか数千個ぐらい作られているのに、同じようなツールをなぜ今になって?」
はい、よい質問ですね!
「私に作れたから作った🤷」という答えもありますが、もっと大事な理由は、Rubyで書かれた既存の(優秀な!)静的サイトジェネレータは、どれも私の製品を構築したフレームワーク(それがたまたまRailsだったわけですが)との相性が良くなかったためです。
私は10何年もの間、自分のためにも顧客のためにも、利益を生み出す製品を楽しみながら多数構築してきました。私は、それらの製品の構成や注意点に加えて、特定の機能をどのように構築するのがベストかというノウハウも知り尽くしています。さらに、Tailwind CSSからViewComponent、果てはRails Iconsに至るまで多くのgemを愛用しています。
しかし、私が構築した製品をマーケティングするための静的サイトが必要になってくると、Railsで培ってきた知識がまるで通用しないことを痛感したのです。既存のどの静的サイトジェネレータを選択したとしても、ベストプラクティスやら構成やらパラダイムやらをゼロから学び直す必要があります。
これは時間の無駄です。たとえ何らかの静的サイトジェネレータをデフォルトで使うとしても(たとえRuby製であっても!)、静的サイトのために頭を切り替えるのが負担になってきます。1人で(もしくは数人のチームの一員として)静的サイトジェネレータと取り組んでいては、時間を吸われる一方です。
私が作ったPerronは、この問題を解決してくれると思います。
原注
先に進む前に(特に英語ネイティブの皆さん向けに😬)、Perronってどういう意味なのかを軽く説明しておきましょう。Perronは、オランダ語・フランス語で「鉄道の駅で乗客が乗り降りする"プラットフォーム"」のことです。ポッポー!🚂
🔗 Perronの使い方
Perronは、Railsアプリと同じように使えます!bin/rails server(最近ならbin/dev)で静的サイトを開発できたら嬉しいと思いませんか?
ルーティングも以下のように書けます。
Rails.application.routes.draw do
resources :posts, module: :content, only: %w[index show]
resources :pages, module: :content, only: %w[show]
root to: "content/pages#root"
end
たとえばPostsControllerは以下のように書けます。
class Content::PostsController < ApplicationController
def index
@resources = Content::Post.all
end
def show
@resource = Content::Post.find(params[:id])
end
end
レイアウトファイルも以下のように書けます。
<!DOCTYPE html>
<html>
<%= render "shared/head" %>
<body>
<%= yield %>
</body>
</html>
「あれ、何だかとってもRailsみたいじゃなくね?」ご明答です。さらに嬉しいのは、Railsそのものだということです。Perronでは、Railsで使えるものは何でも使えます。
🔗 Perronで試す価値のある機能
Perronの良さは、WebサイトをRailsとして構築すれば、後はbin/rails perron:buildを実行するだけで、それを完全な静的サイトとして公開できることです。NetlifyでもStatichostでもDigitalOceanでも、好みの静的サイトプラットフォームにデプロイできます。
Perron自体は、Railsアプリに追加できる軽量なgemです。Perronは、典型的なSaaSサイトやマーケティングサイトを、Railsの豊富な機能を用いて、任意のコンテンツサイトを非常にお手軽に、しかも「楽しく」(とあえて申し上げます)構築できる機能を提供します🛝。Perronには、既存の静的サイトでよく使われているベストプラクティスが多数盛り込まれています。
Perronでは、「リソース」という概念が使われていて、具体的にはPerron::Resourceを継承したクラスのことです。
bin/rails generate content Postコマンドを実行するだけで、新しい「リソース」を作成できます。このとき、以下のファイルも生成されます。
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.
Rails使いならすっかりお馴染みですよね?
さて、Content::Postクラスは以下のような感じになります。
class Content::Post < Perron::Resource
CATEGORIES = %w[hotwire tailwindcss rails]
delegate :category, :title, :description, to: :metadata
validates :title, :description, presence: true
validates :category, inclusion: { in: CATEGORIES }
end
はい、ご覧の通り、Active Modelの典型的なバリデーションも使えるのです!🤓 bin/rails perron:validateコマンドを実行すれば、リソースのバリデーションがすべてパスするかどうかもチェックできます。
ところで上のコードにあるmetadataにお気づきでしょうか?これは、すべてのリソースの"frontmatter"を出力するものです。このfrontmatterは、markdownコンテンツの冒頭部分でyaml形式で定義されます。
詳しくは、frontmatterとmetatagsのドキュメントを参照してください。
Perronではもちろんmarkdownもサポートしています❤️。そのためのmarkdownifyというヘルパーが提供されていますが、特定のパーサーの利用を強いることはありません。bundle addを実行して、commonmarkerでもkramdownでもredcarpetでも好みのパーサーを追加すれば問題なく動きます。
<article>
<h1 class="page-heading">
<%= @resource.title %>
</h1>
<div class="content">
<%= markdownify @resource.content %>
</div>
</article>
markdownについて話しておきましょう。Perronは、生成されたHTMLコンテンツを変換できます。
たとえば、すべてのリンクにtarget=_blank属性を追加することも、すべての画像にloading=lazy属性を追加することも、独自のプロセッサを作成することも、とにかく何でもできます。
例: すべてのコードブロックに"Copy"ボタンを追加する
例: YouTubeやCodePenのリンクを貼り付けるだけでコンテンツに埋め込む
markdownやHTML変換については、ドキュメントで詳しく学べます。
🔗 Perronには他にどんな機能があるか
Perronは、典型的なSaaS・マーケティングサイトを念頭に置いて構築されています。
すなわち、コンテンツの公開をスケジューリングするとか、著者や従業員データなどの構造化データの扱いはもちろん、RSSやJSONのフィードやXMLサイトマップ機能も、最初から利用可能です。
🔗 さまざまなライブラリ: スニペット、コンポーネント、テンプレート
そうした機能に加えて、Perronで利用できる「スニペット」「UIコンポーネント」「テンプレート」ライブラリのちょっとしたコレクションを以下でチェックできます。
参考: Library | Perron
ここには、たとえば以下のようなものが用意されています。
それとも、以下のような完全なテンプレートはいかがでしょうか?
Perronの概要は以上です。
他にもさまざまな機能が搭載されており、新機能、バグ修正、新しいスニペットやテンプレート、マーケティングサイト向けのUIコンポーネントなど、今後もさまざまな追加が予定されています。
よろしければぜひ、PerronのGitHubリポジトリにスターを追加してお試しください。詳しくはPerronのドキュメントサイトを参照してください。

概要
元サイトの許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。