Posted at
RubyDay 2

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

More than 3 years have passed since last update.

GraphvizのDOT言語の代わりにRubyのDSLを利用できるGvizからdotファイルを生成します。

生成したdotファイルを利用してgravizoで図を表示します。


前提


  • Rubyのインストール

  • gviz gem のインストール

$ gem install gviz


CUIで描画する利点


  • GUIの描画ツールやホワイトボードを使う場合に比べ少ない手間で作図が可能となる


    • 特に頻繁に変更する場合に作業量の差が顕著



  • バージョン管理で差分を管理しやすくなる

  • gravizo等、ツール間をとりもつツールがあると利用ドキュメントにそのまま図の元テキストを埋め込める


Gvizを利用する利点


  • DOT言語は制御構造を持っていないため複雑な描画が難しい(手間がかかる)という弱点を補うことができる

  • 一定のルールでグラデーションさせるなど、プログラムを利用できる点を活かせる


手順


お題

QiitaのAdvent CalendarということでQiitaの中の@r7kamuraさんが作成した

rubotyのリポジトリのコミット履歴を有向グラフにしてみたいと思います。

ほとんどが@r7kamuraさんのコミットになっているので、

コミットした人物が入れ替わったタイミングで別Edgeで描画してみます。


コミットユーザーを取得

$ git log --pretty="%an" > ruboty_committers

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


Gvizのプログラム

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


出力された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;
}


esaのプレビューでgravizo埋め込みを試行


  • gravizoの呼び出し

![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


  • 出力された画像

gravizo_output.png


Qiitaに埋め込んでみる

このQiitaの記事内にgravizoの画像を埋め込んでみます。

中身が気になる方は当ページの末尾に .md をつけてご確認ください。

Alt text


外部資料