スライドショーサーバー メニュー作成スクリプト + Hubot 連携

# スライドショーサーバー メニュー作成スクリプト + Hubot 連携
## 概要
内部利用向けスライドサーバーの各種スライドショーのメニューを生成するスクリプトを作成し、
Hubot から呼び出し可能にします。

* Kandan でメニュー更新を呼び出し ( `@hubot update_slide_menu` )
* Hubot がスライドサーバーのメニュー更新処理をリモート呼び出し
* スライドサーバーでメニュー更新実行
* Kandan に表示されたトップページのリンクからメニュー最新化を確認

## 内部利用向けスライドショー全体の構成
以下を参照

* [Reveal.js を利用した内部公開スライドサーバー構成について](http://qiita.com/tbpgr/items/f9a0c3a04044918b2ace)
* [Ruby | Gviz で Reveal.js を利用したスライドサーバーの開発・運用構成図を作成する](http://qiita.com/tbpgr/items/4fff34ac86935e90372d)

## メニュー生成実装言語
* Perl 5

私個人は Ruby が好きなのですが、スライドショーサーバーには
Ruby をインストールしていないため Perl で実装します。
このツールのためだけに環境を複雑にしたくないので。

Perl はよくわからないので、コードは動くだけで適当です。
気が向いたらリファクタリングするかも、しないかも。

## ディレクトリ構成

こんな構成。
slides_root = Web Root。

各 slide1, slide2 ・・ slideN が Reveal.js で作成された個別のスライドだと思ってください。
slides_root/index.html が今回生成するメニューです。
slides_root/generate_slide_index.pl がメニュー生成スクリプト。

~~~
slides_root
┣css
┣node_modules
┣index.html
┣generate_slide_index.pl
┣slide1
｜  ┗slide_contents
┣slide2
｜  ┗slide_contents
：
┣slideN-1
｜  ┗slide_contents
┗slideN
    ┗slide_contents
~~~

## スライド情報

* 更新日
./slideN/index.html の更新日を利用。
`grunt dist` を利用して reveal.js のスライドを作成した場合、少なくとも index.html を
更新するはずなのでこのファイルの更新日を利用します。

* スライドタイトル
./slideN のディレクトリ名をタイトルとします。
ディレクトリ名=URLにもなるし、命名に気を使う必要がありそういった面でも
この名前を利用するのが良い気がする。
css / node_modules はスライドのプロジェクトとは関係ないディレクトリなので、
一覧に表示されないように注意。

* URL
基本URL/ディレクトリ名

要は ./slides_root 配下のディレクトリのリストを出力するだけ。

## 表示イメージ
index.html に表形式で出力

Sample

|title|updated_at|
|:--|:--|
|slide1|2014/09/10 22:57:16|
|slide2|2014/09/09 20:57:16|

※slide1、slide2の部分はリンクにする

## スライドメニュー生成スクリプト
※ Perl スキルが低いので汚いコード注意

~~~perl:generate_slide_index.pl
#!/usr/bin/perl
use strict;
use warnings;

my $base_url = 'http://id_or_domain:your_port/';
my @excludes = ('css/', '.git/', 'node_modules/');
my @slides = ();
foreach (<*/>) {
  my $index = $_."index.html";
  my $dir = $_;
  if (grep {$_ eq $dir} @excludes) {
    next;
  }

  my $updated_at = (stat $index)[9];
  (my $sec, my $min, my $hour, my $day, my$month, my$year) = localtime($updated_at);
  $year = $year + 1900;
  $month= $month + 1;
  my $formated_updated_at = sprintf("%04d/%02d/%02d %02d:%02d:%02d", $year, $month, $day, $hour, $min, $sec);

  my %slide;
  $slide{'0:title'} = "<a href='".$base_url.$index."' target='_blank'>".substr($_, 0, -1)."</a>";
  $slide{'1:updated_at'} = $formated_updated_at;
  @slides = (@slides, \%slide);
}

my @html = ();
@html = (@html, "<!DOCTYPE html>");
@html = (@html, "<html>");
@html = (@html, "<head>");
@html = (@html, "<meta charset=\"UTF-8\">");
@html = (@html, "<title>Slide List</title>");
@html = (@html, "<link rel=\"stylesheet\" href=\"./css/common.css\" type=\"text/css\" />");
@html = (@html, "</head>");
@html = (@html, "<h1>Slide List</h1>");
@html = (@html, "<hr>");
@html = (@html, "<table>");
@html = (@html, "<tr><th>title</th><th>updated_at</th></tr>");
foreach (@slides) {
  @html = (@html, "<tr>");
  my $title = '';
  my $updated_at = '';
  while (my ($key, $value) = each($_)){
    if ($key eq '0:title') {
      $title = "<td>".$value."</td>";
    } else {
      $updated_at = "<td>".$value."</td>";
    }
  }
  @html = (@html, $title);
  @html = (@html, $updated_at);
  @html = (@html, "</tr>");
}
@html = (@html, "</table>");
@html = (@html, "</html>"."\n");
print join("\n", @html);
~~~

## 実行

~~~bash
$ perl generate_slide_index.pl > index.html
~~~

~~~html:index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Slide List</title>
<link rel="stylesheet" href="./css/common.css" type="text/css" />
</head>
<h1>Slide List</h1>
<hr>
<table>
<tr><th>title</th><th>updated_at</th></tr>
<tr>
<td>
<a href='http://ip_or_domain:your_port/slide_server/index.html' target='_blank'>slide_server</a>
</td>
<td>
2014/09/10 10:57:16
</td>
</tr>
</table>
</html>
~~~

## Hubotスクリプトから呼び出すシェル

~~~bash
#!/bin/bash
cat << EOF > /tmp/CMD.txt
cd /home/vagrant/slides
perl generate_slide_index.pl > index.html
EOF
CMD=`cat /tmp/CMD.txt`
rm /tmp/CMD.txt
ssh user_name@ip_or_domain -i /home/vagrant/.ssh/dsdesxi_sshkey "$CMD"
~~~

## Hubotスクリプト

~~~coffeescript:generate_slide_index.coffee
# Description
#   Update slide server menu page.
#
# Dependencies:
#   None
#
# Configuration:
#   None  
#
# Commands:
#   hubot update_slide_menu ... - Update slide server menu page.
child_process = require 'child_process'

module.exports = (robot) ->
  say = (message) ->
    user = {
      room :
        id : 11 # Slide
    }

    robot.send user, message

  robot.respond /update_slide_menu$/i, (msg) ->
    child_process.exec ". /path/to/hubot_root/shells/generate_slide_index.bash", (error, stdout, stderr) ->
      if !error
        say "please confirm menu page"
        say "http://ip_or_domain:your_port/index.html"
      else
        say "error"
    res.send 200
~~~

## 出力イメージ
* メニューが作成されました (css は好きなものに差し替えてください)

![slide_index.png](https://qiita-image-store.s3.amazonaws.com/0/43480/5b1b0722-b2dd-55e5-7f29-c1e7dd8f0819.png)

* メニューを選ぶとスライドに遷移します

![reveal_slide.png](https://qiita-image-store.s3.amazonaws.com/0/43480/6718c747-24fb-76c0-ff78-9585ce76504b.png)

* Kandan に Chat が表示されました

![update_slide_kandan.png](https://qiita-image-store.s3.amazonaws.com/0/43480/9ab177e4-9305-a5ba-fada-2ceb8f35eb1e.png)

## 参照
* [Reveal.js を利用した内部公開スライドサーバーでスライドの自動デプロイを行う](http://qiita.com/tbpgr/items/107d75bc8a4732d9c294)
* [Reveal.js を利用した内部公開スライドサーバー構成について](http://qiita.com/tbpgr/items/f9a0c3a04044918b2ace)
* [Ruby | Gviz で Reveal.js を利用したスライドサーバーの開発・運用構成図を作成する](http://qiita.com/tbpgr/items/4fff34ac86935e90372d)