nanoc でSQLite3のデータからページを作る
同じようなコンテンツが大量にあるけれど,
静的なHTMLのみで作らなければならないサイトがあったので
nanoc*1 というRuby製のコンテンツビルドツールを使ってみました.
nanocってなにさ?
コンテンツのパーツを作っていき,一気に「コンパイル」してサイトを構成してくれる逸品です.
作ったパーツから同じ「タグ」要素の一覧ページを生成したり,
投稿日ごとのアーカイブを生成するなど,
ブログっぽいサイトを静的ページのみで作れます :-)
ブログサイトを作る過程は以下の記事がとても参考になりました.
前フリ
一日1ページのコンテンツを用意するにも,記事のファイルを一つ一つ書いて....
というのがとても面倒大変で,
しかもHTML書けない人(エンドユーザ)にデータを用意してもらうことも難しいので,
Rails で入力フォームをざっくり作り*2,
SQLite3のデータベースへコンテンツの要素を登録してもらいました.
で,ここからがnanocの話.
「静的なWebサイト」の制約があるのでRailsで公開コンテンツを作れないので
登録してもらったデータを読み込んでnanocでページを生成してみたわけです.
nanocのData Source
Nanoc3::DataSource というクラスを継承して
取得したデータからnanoc用のオブジェクト*3を作ることで,
「コンパイル」の際にデータを埋め込んだページを生成することができます.
Rails のフォームではSQLiteを使っていたので,ActiveRecord*4を使って実装してみました.
ここでは,日報を表す「report」テーブルから1レコード1ページで作る例を示します.
# ./lib 以下に用意 class ReportSource < Nanoc3::DataSource require 'sqlite3' require 'active_record' require 'yaml' # Data Sourceにつける名前.設定ファイルで利用 identifier :reports # nanoc用オブジェクトを配列で返すためのメソッド(オーバーライド) def items items_list = [] # Railsでの接続設定用ファイルを流用 # 作るサイトのディレクトリ直下にYAMLファイルを配置 ActiveRecord::Base.configurations = YAML.load_file("database.yml") # ActiveRecordでDBに接続 ActiveRecord::Base.establish_connection('development') # 全件取得 Reports.find(:all).each do |report| # HTMLファイルを作るためのテンプレート content = "<%= render('_report_page') %>" # ページに埋め込む属性(データ)のHash # {:フィールド名 => 値} というHashが.attributesメソッドで取得できる attributes = report.attributes # metaのtitle要素用に加工 attributes[:title] = "日報: " + report.titile # URLを決めるオブジェクト名 identifier = "/#{report.id}/" # ページの更新日付(デフォルトはnil) mtime = report.updated_at # nanocのコンテンツ用オブジェクトを用意 items_list << Nanoc3::Item.new(content, attributes, identifier, mtime) end items_list end end # ActiveRecord用にデータモデルの空クラスを用意 class Reports < ActiveRecord::Base end
各ページのテンプレート(上記の_report_page)は以下のように,erb形式でデータを埋め込んでいます.
<h2><%= item[:title] %><h2> <p> <b>Reporter:</b> <%= item[:reporter] %> </p> <p> <b>Report at:</b> <%= item[:report_at] %> </p> ....
Nanoc3::Item.new(content, attributes, identifier, mtime)
でセットしたattributesにはDBのレコードの各フィールドが埋めこまれていて,
「item」に格納されているので,上のようにitem[:フィールド名] で値を取り出しています.
「コンパイル」の時にこのDetaSourceを呼び出すために,
config.yamlの以下の部分に追記します.
.... data_sources: - # デフォルトの設定... - # 上記のReportSourceにある"identifier" type: reports # URLのルート.http://hogehoge/reports/id番号/ というページ構成にする items_root: /reports/ # レイアウトの配置 layouts_root: /
ここまでの設定で、コンパイル後に ./output/ ディレクトリに
./output/reports/1/index.html ./output/reports/2/index.html ./output/reports/3/index.html ./output/reports/4/index.html ....
とレコードの数だけコンテンツが生成されるわけです.(しかもRestぽい)
なぜActiveRecord?
SQLite::Databaseを使ってDBに接続しても良かったのですが,
取得したレコードをテンプレートに埋め込むHashを作る際に
以下のようにレコードの配列の番号を指定して...が面倒で短調でバグになりそうな臭いがするので
ActiveRecordでサッとHashを用意しました.
... db = SQLite3::Database.new("hogehoge.db") db.execute("select * from report") do |row| ... attributes = { :reporter => row[1], .... } end
ActiveRecordの使い方は以下を参考にしました.