RubyのDSLの表現力と生産性で有向グラフの作図効率を上げつつ, Graphvizをインストールせずにgravizo経由で利用する

GraphvizのDOT言語の代わりにRubyのDSLを利用できるGvizからdotファイルを生成します。
生成したdotファイルを利用して[gravizo](http://www.gravizo.com/)で図を表示します。

## <i class="fa fa-check-circle" style="font-size:1em;"></i> 前提
* Rubyのインストール
* gviz gem のインストール

```bash
$ gem install gviz
```

## <i class="fa fa-terminal" style="font-size:1em;"></i> CUIで描画する利点
* GUIの描画ツールやホワイトボードを使う場合に比べ少ない手間で作図が可能となる
    * 特に頻繁に変更する場合に作業量の差が顕著
* バージョン管理で差分を管理しやすくなる
* gravizo等、ツール間をとりもつツールがあると利用ドキュメントにそのまま図の元テキストを埋め込める

## <i class="fa fa-diamond" style="font-size:1em;"></i> Gvizを利用する利点
* DOT言語は制御構造を持っていないため複雑な描画が難しい（手間がかかる）という弱点を補うことができる
* 一定のルールでグラデーションさせるなど、プログラムを利用できる点を活かせる

## <i class="fa fa-list" style="font-size:1em;"></i> 手順
### <i class="fa fa-file-text" style="font-size:1em;"></i> お題
QiitaのAdvent CalendarということでQiitaの中の@r7kamuraさんが作成した
rubotyのリポジトリのコミット履歴を有向グラフにしてみたいと思います。

ほとんどが@r7kamuraさんのコミットになっているので、
コミットした人物が入れ替わったタイミングで別Edgeで描画してみます。

### <i class="fa fa-git-square" style="font-size:1em;"></i> コミットユーザーを取得
```bash
$ git log --pretty="%an" > ruboty_committers
```

処理の都合上、[a-zA-Z]の名前のみを扱いたかったため `Aydın Doğan` 氏のコミットだけ除外しました。

### <i class="fa fa-diamond" style="font-size:1em;"></i> Gvizのプログラム
```ruby
require 'gviz'

class String
  def normalize_gviz_id
    self.gsub(/[ _¥(¥)]/, "")
  end
end

file = File.open("ruboty_committers", "r:utf-8"){|f|f.read}
committers = file.each_line.map(&:chomp).map(&:normalize_gviz_id).reverse.map.with_index(1){|e, i|"#{e}#{i}"}
committers.unshift("start")
committers.push("goal")

Graph do
  committers.each_slice(2) do |a, b|
    break if b.nil?
    edge :"#{a}_#{b}"
  end
  save :sample
end
```

### <i class="fa fa-terminal" style="font-size:1em;"></i> 出力されたdotファイル
```
digraph G {
  start;
  RyoNakamura1;
  RyoNakamura174;
  RyoNakamuraV175;
  TaikiONO176;
  TaikiONO177;
  RyoNakamura178;
  RyoNakamura183;
  ShotaFukumorisorah184;
  RyoNakamura185;
  RyoNakamura203;
  parroty204;
  RyoNakamura205;
  RyoNakamura206;
  rochefort207;
  RyoNakamura208;
  amacou209;
  RyoNakamura210;
  RyoNakamura220;
  amacou221;
  amacou223;
  RyoNakamura224;
  RyoNakamura236;
  TakehiroSUGITA237;
  RyoNakamura238;
  RyoNakamura240;
  TaikiOno241;
  RyoNakamura242;
  goal;
  start -> RyoNakamura1;
  RyoNakamura174 -> RyoNakamuraV175;
  RyoNakamuraV175 -> TaikiONO176;
  TaikiONO177 -> RyoNakamura178;
  RyoNakamura183 -> ShotaFukumorisorah184;
  ShotaFukumorisorah184 -> RyoNakamura185;
  RyoNakamura203 -> parroty204;
  parroty204 -> RyoNakamura205;
  RyoNakamura206 -> rochefort207;
  rochefort207 -> RyoNakamura208;
  RyoNakamura208 -> amacou209;
  amacou209 -> RyoNakamura210;
  RyoNakamura220 -> amacou221;
  amacou223 -> RyoNakamura224;
  RyoNakamura236 -> TakehiroSUGITA237;
  TakehiroSUGITA237 -> RyoNakamura238;
  RyoNakamura240 -> TaikiOno241;
  TaikiOno241 -> RyoNakamura242;
  RyoNakamura242 -> goal;
}
```

### <i class="fa fa-chrome" style="font-size:1em;"></i> esaのプレビューでgravizo埋め込みを試行
* gravizoの呼び出し

```markdown
![Alt text](http://g.gravizo.com/g?
digraph G {
  start;
  RyoNakamura1;
  RyoNakamura174;
  RyoNakamuraV175;
  TaikiONO176;
  TaikiONO177;
  RyoNakamura178;
  RyoNakamura183;
  ShotaFukumorisorah184;
  RyoNakamura185;
  RyoNakamura203;
  parroty204;
  RyoNakamura205;
  RyoNakamura206;
  rochefort207;
  RyoNakamura208;
  amacou209;
  RyoNakamura210;
  RyoNakamura220;
  amacou221;
  amacou223;
  RyoNakamura224;
  RyoNakamura236;
  TakehiroSUGITA237;
  RyoNakamura238;
  RyoNakamura240;
  TaikiOno241;
  RyoNakamura242;
  goal;
  start -> RyoNakamura1;
  RyoNakamura174 -> RyoNakamuraV175;
  RyoNakamuraV175 -> TaikiONO176;
  TaikiONO177 -> RyoNakamura178;
  RyoNakamura183 -> ShotaFukumorisorah184;
  ShotaFukumorisorah184 -> RyoNakamura185;
  RyoNakamura203 -> parroty204;
  parroty204 -> RyoNakamura205;
  RyoNakamura206 -> rochefort207;
  rochefort207 -> RyoNakamura208;
  RyoNakamura208 -> amacou209;
  amacou209 -> RyoNakamura210;
  RyoNakamura220 -> amacou221;
  amacou223 -> RyoNakamura224;
  RyoNakamura236 -> TakehiroSUGITA237;
  TakehiroSUGITA237 -> RyoNakamura238;
  RyoNakamura240 -> TaikiOno241;
  TaikiOno241 -> RyoNakamura242;
  RyoNakamura242 -> goal;
}
)
```

* esaのプレビューの様子

![gtavizo_output.gif](https://qiita-image-store.s3.amazonaws.com/0/43480/59d87838-671e-9206-8cf2-429af915c4de.gif)

* 出力された画像

![gravizo_output.png](https://qiita-image-store.s3.amazonaws.com/0/43480/134d5e2e-71f1-7733-17d1-91df1cdd78ed.png)

### <i class="fa fa-image" style="font-size:1em;"></i> Qiitaに埋め込んでみる
このQiitaの記事内にgravizoの画像を埋め込んでみます。
中身が気になる方は当ページの末尾に `.md` をつけてご確認ください。

![Alt text](http://g.gravizo.com/g?
digraph G {
  start;
  RyoNakamura1;
  RyoNakamura174;
  RyoNakamuraV175;
  TaikiONO176;
  TaikiONO177;
  RyoNakamura178;
  RyoNakamura183;
  ShotaFukumorisorah184;
  RyoNakamura185;
  RyoNakamura203;
  parroty204;
  RyoNakamura205;
  RyoNakamura206;
  rochefort207;
  RyoNakamura208;
  amacou209;
  RyoNakamura210;
  RyoNakamura220;
  amacou221;
  amacou223;
  RyoNakamura224;
  RyoNakamura236;
  TakehiroSUGITA237;
  RyoNakamura238;
  RyoNakamura240;
  TaikiOno241;
  RyoNakamura242;
  goal;
  start -> RyoNakamura1;
  RyoNakamura174 -> RyoNakamuraV175;
  RyoNakamuraV175 -> TaikiONO176;
  TaikiONO177 -> RyoNakamura178;
  RyoNakamura183 -> ShotaFukumorisorah184;
  ShotaFukumorisorah184 -> RyoNakamura185;
  RyoNakamura203 -> parroty204;
  parroty204 -> RyoNakamura205;
  RyoNakamura206 -> rochefort207;
  rochefort207 -> RyoNakamura208;
  RyoNakamura208 -> amacou209;
  amacou209 -> RyoNakamura210;
  RyoNakamura220 -> amacou221;
  amacou223 -> RyoNakamura224;
  RyoNakamura236 -> TakehiroSUGITA237;
  TakehiroSUGITA237 -> RyoNakamura238;
  RyoNakamura240 -> TaikiOno241;
  TaikiOno241 -> RyoNakamura242;
  RyoNakamura242 -> goal;
}
)

## <i class="fa fa-book" style="font-size:1em;"></i> 外部資料
* <i class="fa fa-external-link" style="font-size:1em;"></i> [gravizo](http://www.gravizo.com/)
* <i class="fa fa-external-link" style="font-size:1em;"></i> [gviz - GitHub](https://github.com/melborne/Gviz)
* <i class="fa fa-external-link" style="font-size:1em;"></i> [Gvizの目次 - Rubyの世界からGraphvizの世界にこんにちは！ - hp12c](http://melborne.github.io/2014/02/27/gviz-posts/)
* <i class="fa fa-external-link" style="font-size:1em;"></i> [Graphviz関連まとめ - Tbpgr - Qiita](http://qiita.com/tbpgr/items/76d0379bb83ec64fbdb3)