blog

初めての Middleman シリーズ初めてのMiddleman:データファイルとデータファイルを使った動的ページ生成

    • Ryuichi Nonaka
    この記事は書かれてから1年以上経過しており、内容が古い場合があります。

    前回の記事初めてのMiddleman:Sass MixinライブラリのBourbon, Neatを導入してみるでSassのMixinライブラリであるBourbon / Neatのインストールからコンパイルまで勉強しました。今回は新たにデータファイルについて理解し、動的にページを生成してみたいと思います。

    データファイルを作る

    わかりやすい例を挙げると、ナビゲーションの各リンクに含まれるタイトルやURLをデータファイルで管理することでデータとビューを切り離すことができます。このデータファイルはYAMLやJSON形式で作ることができ、簡単にテンプレートから呼び出せます。詳しくは公式ドキュメントのデータファイルもチェックしてみてください。

    ナビゲーションをまとめたデータファイルを作る

    データファイルのわかりやすい例として、ナビゲーションのデータファイルを作りレイアウトに組み込んでみます。

    nav.yml

    ナビゲーションのデータファイルはdata/nav.ymlで保存します。

    -
      slug: 'home'
      name:
        ja: 'ホーム'
        en: 'home'
      path: ''
    -
      slug: 'blog'
      name:
        ja: 'ブログ'
        en: 'blog'
      path: 'blog/'
    -
      slug: 'about'
      name:
        ja: 'このサイトについて'
        en: 'about'
      path: 'about'
    

    レイアウトにナビゲーションを追加

    レイアウトファイルにナビゲーションに関する指定を追加します。ナビゲーションデータから項目を取り出すにはeachメソッドを使います。

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <title><%= current_page.data.title || "The Middleman" %></title>
      <%= stylesheet_link_tag "common" %>
    </head>
    <body>
      <%= partial('modules/header', :locals => { :description => 'hogehoge' })  %>
      <ul id="nav">
        <% data.nav.each do |item| %>
        <li><%= link_to item.name.ja, item.path %></li>
        <% end %>
      </ul>
      <%= yield %>
    </body>
    </html>
    

    ビルド

    ビルドしてみます。

    $ middleman build
       identical  build/assets/css/common.css
          update  build/index.html
    

    ビルドされたHTMLに下記のようなナビゲーションリストが追加されていれば成功です。

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <title>Hello World</title>
      <link href="/assets/css/common-d7f822e6.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
      <header>
      <h1>example.com</h1>
      <p>hogehoge</p>
    </header>
      <ul>
        <li><a href="/">ホーム</a></li>
        <li><a href="blog/">ブログ</a></li>
        <li><a href="about.html">このサイトについて</a></li>
      </ul>
      <p>Hello World</p>
    </body>
    </html>
    

    これは一例ですが、データファイルは様々な使い方ができる便利な機能です。

    データファイルをもとに動的にページを生成する

    スピーカーの情報をデータファイルにまとめ、スピーカー1人1人に独立したHTMLページを生成する例を紹介します。動的ページをつくるにはProxyという機能を使います。Proxyに対してテンプレートやデータを渡してページを生成します。

    必要なファイル

    動的ページを別のデザインにしたりデータファイルの値を渡して思い通りのページを実現しようとすると下記のようなファイルが必要になります。

    • データファイル:ページ毎のデータを定義する
    • テンプレート:生成するファイルのベースとなるファイル(レイアウトファイルを呼ぶ)
    • レイアウト:タイトルタグやパーシャルに独自の値を渡したい場合に専用のレイアウトが必要

    データファイル

    下記のようにデータファイルにはスピーカーの情報を配列で定義します。

    data/speakers.yml

    この3人のスピーカー毎にページを生成します。

    -
      slug:     'yamada-tarou'
      name:     '山田 太郎'
      company:  '山田株式会社'
      position: '猫'
      profile:  '吾輩わがはいは猫である。名前はまだ無い。どこで生れたかとんと見当けんとうがつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。'
      link:     'http://whiskers.nukos.kitchen/'
    
    -
      slug:     'satou-jirou'
      name:     '佐藤 次郎'
      company:  '佐藤株式会社'
      position: '犬'
      profile:  '吾輩わがはいは犬である。名前はまだ無い。どこで生れたかとんと見当けんとうがつかぬ。何でも薄暗いじめじめした所でワンワン泣いていた事だけは記憶している。'
      link:     'http://whiskers.nukos.kitchen/'
    
    -
      slug:     'suzuki-saburo'
      name:     '鈴木 三郎'
      company:  '鈴木株式会社'
      position: 'ウサギ'
      profile:  '吾輩わがはいは兎である。名前はまだ無い。どこで生れたかとんと見当けんとうがつかぬ。何でも薄暗いじめじめした所で静かに泣いていた事だけは記憶している。'
      link:     'http://whiskers.nukos.kitchen/'
    

    専用のレイアウト

    既存のlayout.erbをそのまま使えれば余計な手間が省けるのだけれど、残念ながらできません。なぜかというと以下のcurrent_page.data.titleにProxyからはデータを渡せないんですね(わかってないだけで方法はあるかもしれない)。今回は別のレイアウトを作って出力ヘルパーを使います。出力ヘルパーとはコンテンツ側からテンプレートやレイアウトにデータを投げることができる便利なヘルパーです。詳しくは公式ドキュメントの出力ヘルパーを確認してください。

    layouts/speaker.erb

    スピーカー用のレイアウトを下記のように作ります。タイトルタグは出力ヘルパーのyield_content(...)を使って投げられたデータを出力します。

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <title><%= yield_content(:title) %></title>
      <%= stylesheet_link_tag "common" %>
    </head>
    <body>
      <ul>
        <% data.speakers.each do |speaker| %>
        <li><%= link_to speaker.name, '/speakers/' + speaker.slug + '.html' %></li>
        <% end %>
      </ul>
      <%= yield %>
    </body>
    </html>
    

    テンプレート

    レイアウトの次に、Proxyを実行する際に指定するテンプレートを作ります。

    templates/speaker.html.erb

    テンプレートではYaml FrontMatterを使ってレイアウトファイルspeakerを指定します。また、出力ヘルパーの投げる側でcontent_for(...)を使います。第一引数の:titleが識別子ですね。speaker.nameでスピーカー名を渡しています。

    ---
    layout: speaker
    ---
    <% content_for(:title, speaker.name) %>
    <h1><%= speaker.name %></h1>
    <p><%= speaker.profile %></p>
    

    これで必要なファイルの準備ができました。

    config.rbにProxyを定義する

    最後にconfig.rbにProxyを定義します。ただの設定ファイルかと思いきやconfig.rbの中でも簡単にデータにアクセスできます。データをeachメソッドを使ってレコードを取り出し、speakerに入れて各所で呼び出します。:localsというのはテンプレートで利用できる変数です。また、:ignore => trueを使ってテンプレートファイル/templates/speaker.html.erb自身がコンパイルされないようにします。

    # create speaker pages
    data.speakers.each do |speaker|
      proxy "/speakers/#{speaker.slug}.html", "/templates/speaker.html",
        :locals => { :speaker => speaker },
        :ignore => true
    end
    

    これですべての準備ができました。

    ビルド

    それではビルドしてみましょう。

    $ middleman build
       identical  build/assets/css/common.css
          create  build/speakers/yamada-tarou.html
       identical  build/index.html
          create  build/speakers/satou-jirou.html
          create  build/speakers/suzuki-saburo.html
    

    speakersディレクトリの中に各スピーカーのslugがファイル名になったHTMLが作られました。 HTMLの中身もタイトルがそれぞれのスピーカー名になり、スピーカー全員のリンクもできています。

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <title>佐藤 次郎</title>
      <link href="/assets/css/common-d7f822e6.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
      <ul>
        <li><a href="/speakers/yamada-tarou.html">山田 太郎</a></li>
        <li><a href="/speakers/satou-jirou.html">佐藤 次郎</a></li>
        <li><a href="/speakers/suzuki-saburo.html">鈴木 三郎</a></li>
      </ul>
      <h1>佐藤 次郎</h1>
    <p>吾輩わがはいは犬である。名前はまだ無い。どこで生れたかとんと見当けんとうがつかぬ。何でも薄暗いじめじめした所でワンワン泣いていた事だけは記憶している。</p>
    </body>
    </html>
    

    これで、データファイルに必要な情報を追記するだけで簡単にページが作れるようになりました。データさえ用意すれば良いのでコンテンツ追加が捗りますね。更新がとても楽になります。

    今回はここまで。
    次回はブログ機能について勉強したいと思います。

    次回:初めてのMiddleman:サイトにブログ機能を追加する

    シリーズ

    1. 初めてのMiddleman:rbenv, bundler 環境でMiddlemanを使ったHello World
    2. 初めてのMiddleman:レイアウト機能でレイアウトとコンテンツを分離する
    3. 初めてのMiddleman:パーシャルを使ったコンテンツのモジュール化
    4. 初めてのMiddleman:SCSSのコンパイルとテンプレートの基礎
    5. 初めてのMiddleman:Sass MixinライブラリのBourbon, Neatを導入してみる
    6. 初めてのMiddleman:データファイルとデータファイルを使った動的ページ生成
    7. 初めてのMiddleman:サイトにブログ機能を追加する
    8. 初めてのMiddleman:Amazon S3にビルドされたファイルを同期する
    9. 初めてのMiddleman:HTML/CSS/JS/画像をビルド時に圧縮する
    10. 初めてのMiddleman:設定について
    11. 初めてのMiddleman:静的サイトのキャッシュとasset_hash拡張を使ったキャッシュのパージ
    12. 初めてのMiddleman:Middleman::S3Syncで使うAWS IAMユーザーのアクセスキー管理方法について
    13. 初めてのMiddleman:Helperを使ってGravatarのアイコンを表示する
    14. 初めてのMiddleman:アセットパイプラインを使った外部アセットファイルの読み込みと結合
    15. 初めてのMiddleman:Bowerを使ってjQueryなどのライブラリをロードする
    16. 初めてのMiddleman:スケルトンを自作する方法
    17. 初めてのMiddleman:Middleman Blogの記事データを使いやすく管理する方法
    18. 初めてのMiddleman:Middleman-blogのシングルブログを前提としたSkeletonを作った
    19. 初めてのMiddleman:Middleman-blogでマルチブログを試してSkeletonを作った
    20. 初めてのMiddleman:Markdown EngineをkramdownからRedcarpetに切り替える
    21. 初めてのMiddleman:RedcarpetとMiddleman::Rougeを使ったシンタックスハイライト
    22. 初めてのMiddleman:Middleman-Syntaxを使ったシンタックスハイライト
    23. 初めてのMiddleman:Middleman-Blogで記事毎に画像を管理する方法
    24. 初めてのMiddleman:Gulpを使ってサムネイル画像を生成する
    25. 初めてのMiddleman:Middleman-OGPのOGP画像指定を相対パスで設定する
    26. 初めてのMiddleman:Middleman-OGPでMiddleman-BLogにOGPを設定する
    27. 初めてのMiddleman:Middleman-Titleでタイトルタグを手軽に設定する
    28. 初めてのMiddleman:多言語化 - 言語ファイルの作成とヘルパーの埋め込み
    29. 初めてのMiddleman:Middleman-blogにカテゴリやシリーズなどのカスタムコレクションを追加する方法
    30. 初めてのMiddleman:Bowerで管理されているフォントファイルをインポートする

    コメント・フィードバック