graphvizの最近のブログ記事

Wiki Graphviz macro プラグイン
例によって無保証です。
説明はその2の方に。

今回はハマったことというか知ったこととか。
以下、redmine-0.8.0RC1前提で。


redmineのwikiマクロに関して

  • ・{{macro_list}}マクロがデフォルトで入っていて、使えるマクロの一覧を表示できる。知らんかった。。
    • マクロ名と登録時に「desc」で指定した内容が表示される
  • {{include(Foo)}}マクロがデフォルトで入っていて、別ページをincludeできる。知らんかった。。
  • wikiマクロの登録は、Redmine::WikiFormatting::Macros.registerにブロックを渡す。
    • descにはwiki記法が使えるが、今にして思えばtextile以外のフォーマッタを使っている場合にマズイか・・・。
  Redmine::WikiFormatting::Macros.register do
    desc <<'EOF'
Render graph image from the wiki page which is specified by macro-args.

  !{{graphviz(Foo)}}
  !{{graphviz(option=value...,Foo)}}

* options are:
** format={png|jpg}
** layout={dot|neato|fdp|twopi|circo}
** target={_blank|any}
** with_source
EOF
    macro :graphviz do |wiki_content_obj, args|
(略)
  • マクロ本体はmacroにシンボルとブロックを指定する
    • ブロックの引数はWikiContentモデルとマクロの引数に指定した文字列を「,」で分割したArray
      • {{マクロ(引数)}}
  • wiki中の{{マクロ}}の都度ブロックが呼び出される
    • ブロックが返した文字列がマクロ展開後のHTMLとして出力される。
      nilを返すと元のマクロ記述がそのまま表示される
    • プレビュー時、ブロックが呼び出されたときに「プレビュー中のテキスト」を得られない
      • graphviz_meマクロで編集/プレビューしながら図を確認できたら便利だよなぁと思ったら渡ってくるのは保存済みのWikiContentモデルだった。
      • 新しいwikiページをプレビューするとobjにはnilが渡ってくる
    • ブロックはActionView::Baseのインスタンスから呼び出される
      • ブロック内でself.to_sしたら"#<ActionView::Base:0xxxxxxxxx>"だった
      • なのでself.controllerからキャッシュメソッドを呼び出したり出来る
      • graphvizマクロの場合、マクロ展開時(Viewのコンテキスト)、画像表示時(コントローラのコンテキスト)のコードをできるだけ共有したかったので都合がよかった
  • ブロックから例外を飛ばすとwikiビュー内にエラー表示できる
    Error executing the マクロ名 macro (メッセージ)

redmineプラグインに関して

  • プラグインROOTのinit.rbで登録すると利用可能になる
    Redmine::Plugin.register :wiki_graphviz_plugin do
  • プラグイン固有のコントローラのrouteはプラグインROOTのroutes.rbで追加定義できる
    connect 'wiki/:id/:page/graphviz', :controller => 'wiki_graphviz', :action => 'graphviz'

プラグイン設定関係

  • 管理→プラグイン→(プラグインごとの)設定で表示されるプラグイン設定画面
  • Redmine::Plugin.register時に、settingsで:partialに指定した設定画面を表示できる。
    settings :default => {'cache_seconds' => '0'}, :partial => 'wiki_graphviz/settings'
  • テンプレートはvendor/plugins/プラグイン/app/views/に置く
    • 簡単で便利だけど、:partialの指定を他のプラグインと同じにしてしまうと別のプラグインのテンプレートが適用されてしまうことがある。
    • extra/sample_pluginをセットアップした状態で:partialに同じ値を設定しておいたらsample_pluginの設定ビューが出てきてかなり悩んだ。 
    • わざと存在しないテンプレート名を指定しておくとテンプレートの検索パスがログに出る。これを見る限り、プラグインの検索パスが連結された状態になっているようだ。
      Missing template wiki_graphviz/_settingsi.html.erb in view path 
      ・・・・/redmine-0.8.0_RC1/vendor/plugins/wiki_graphviz_plugin/app/views:
      ・・・・/redmine-0.8.0_RC1/vendor/plugins/sample_plugin/app/views:
      ・・・・/redmine-0.8.0_RC1/app/views 
    • stylesheet_link_tagみたいに:pluginを指定してなんとかしてくれんだろうか
  • プラグインの名前が長すぎると、設定値を保存できるが再表示した際に値が正しく表示されない(デフォルト値が表示される)
mysql> desc settings;
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| id         | int(11)     | NO   | PRI | NULL    | auto_increment |
| name       | varchar(30) | NO   |     |         |                |
| value      | text        | YES  |     | NULL    |                |
| updated_on | datetime    | YES  |     | NULL    |                |
+------------+-------------+------+-----+---------+----------------+
  • 以前は「redmine_wiki_graphviz_plugin」という名前にしていた。redmine内部で「plugin_」が前置されて35文字になり、切れているのが原因っぽいが深追いしてない。プラグイン名を短くした。
mysql> select * from settings;
(中略)
| 24 | plugin_redmine_wiki_graphviz_p | --- !map:HashWithIndifferentAccess
cache_seconds: "2300"
| 25 | plugin_wiki_graphviz_plugin    | --- !map:HashWithIndifferentAccess
cache_seconds: "3600"
(後略)

Railsに関して
※redmine-0.8.0RC1同梱のもの。2.1?

キャッシュ関係
  • config/environments/環境.rbに設定することで有効になる
    config.action_controller.perform_caching             = true
    config.action_controller.cache_store = :mem_cache_store, "localhost"
  • cache_storeの挙動が微妙に違う。
    • デフォルトのActiveSupport::Cache::MemoryStoreはrubyオブジェクトを格納/取り出しできる。当然といえば当然。
    • ActiveSupport::Cache::FileStoreは文字列前提
    • ActiveSupport::Cache::MemCacheStoreは、(内部で使っているMemCacheの挙動で)値の格納/取り出し時にマーシャリングが行われるので結果的にrubyオブジェクトを(大体)格納できる。
      • :rawオプションでマーシャリングをナシにできる。
  • Catalyst::Plugin::CacheのイメージでいたのでFileStoreからHashが復元されず「おっかしいな~?」てことになった。結局Graphvizマクロプラグインではmemcachedだけ対応とした。

  • ・viewからHEADに何か差し込みたい場合はcontent_forが使える。これは便利な仕組みだな~。
    <% content_for :header_tags do %>
       <%= stylesheet_link_tag "wiki_graphviz.css",
           :plugin => "wiki_graphviz_plugin", :media => :all %>
    <% end %>
  • 画像を出力するときは:text&:content_type指定かつlayoutナシのrender。
    render :text => img, :layout => false, :content_type => "image/png"
  • しかし、charsetをつけられてしまう。。。
    ActionController::Baseのassign_default_content_type_and_charsetを見るにContent-Transfer-Encodingがbinaryに設定されていないとデフォルトのcharsetがついてしまうようだ・・。



wikimacro2.png前回のつづき。

クリッカブルマップを吐き出すところまでいけたので公開。
Wiki Graphviz macro プラグイン
例によって無保証です。

主なfeature

  • wikiマクロ、{{graphviz}}と{{graphviz_me}}を追加
  • wikiページをdot形式で記述し、これらのマクロから呼び出すことでグラフ画像をレンダリング
  • ノードにURL属性を指定するとクリッカブルマップに

{{graphviz}} マクロ

{{graphviz(Foo)}}
{{graphviz(option=value,Foo)}}
{{graphviz(layout=neato,target=_blank,with_source,Foo)}}
  
  • 指定したwikiページをdotテキストとみなしてグラフ画像をレンダリング。IMGタグを差し込む
  • オプションは以下のとおり
    • format={png|jpg}
      • 画像形式。
      • 現在のところpngとjpegだけ
    • layout={dot|neato|fdp|twopi|circo}
      • レイアウトエンジン指定。
      • デフォルトはdot
    • target={_blank|any}
      • クリッカブルマップを生成する際のTARGET属性に指定する値
      • 別ウィンドウで開くなら_blank
    • with_source
      • 画像の直後に読みこんだdotの内容をPREで出力

{{graphviz_me}} マクロ

// {{graphviz_me}}
// {{graphviz_me(option=value)}}
// {{graphviz_me(layout=neato,target=_blank,with_source)}}
  
  • 別のwikiページを指定する代わりにこのマクロを含むwikiページをレンダリング対象とする。これ以外は{{graphviz}}マクロと同じ。
  • 必然的に、dotフォーマット上のコメントアウトが必要。コメントアウトしないとレンダラがエラー終了する

dotの書式や指定できる値については、Graphvizチュートリアルがまとまっていて便利。


必要なもの
  • Redmine 0.8.0 RC1 or later.
  • Graphvizとrubyバインディング。本家でrpmを配布している。  http://www.graphviz.org
        graphviz-2.20.3-1.el5
        graphviz-gd-2.20.3-1.el5
        graphviz-ruby-2.20.3-1.el5
  • memcached (optional)


インストール
  • redmine公式サイトの説明にもあるように、プラグインのアーカイブを展開してvendor/plugins/に配置してredmineを再起動するだけ(僕のところはmongrel+mod_proxy_balancerなので)
オプション
  • レンダリングした画像をキャッシュしておくことができます。キャッシュを用いる場合は、memcachedとcache_storeを設定します。
  • cache_storeの設定例
         e.g.) config/environments/production.rb
         config.action_controller.perform_caching = true
         config.action_controller.cache_store = :mem_cache_store, "localhost"
  • redmineを再起動
  • プラグインの設定ページ(管理→プラグイン→Wiki graphviz macro plugin)で、キャッシュ秒数を1以上に設定

僕の環境は以下のとおり。

CentOS 5.2
  ruby-1.8.5-5.el5_2.6
    mongrel-1.1.5
    mongrel_cluster-1.0.5
    redmine-0.8.0_RC1
  memcached-1.2.6-1.el5.rf
  MySQL-server-5.0.51a-tritonn.1.0.10
  graphviz-2.20.3-1.el5
  graphviz-gd-2.20.3-1.el5
  graphviz-ruby-2.20.3-1.el5

既知の問題
  • wikiのプレビューで編集中の内容をレンダリングできない
    (wikiマクロに編集中の内容を受け取るI/Fがない。。。)
とりあえず上記の環境では動くみたい。



graphviz_macro.pngredmineのwikiでgraphvizが使いたい、と思ったのだった。

どんだけgraphviz好きなんだって感じだけど、まぁ好きだ。

実際は、別途webdot的なサービスを作って画像挿入のtextile記法、ってやり方で利用してはいたんだけどredmineプラグイン作成の習作も兼ねてやってみた。

wikiにdotを記述して、別のwikiページからgraphvizマクロで呼び出すとIMGに置き換わる、という流れ。

Wiki Graphviz macro プラグイン
例によって無保証です。
説明はその2の方に。

環境は以下のとおり
CentOS 5.2
  MySQL-server-5.0.51a-tritonn.1.0.10
  ruby-1.8.5-5.el5_2.6
    mongrel-1.1.5
    redmine-0.8.0_RC1


WikiControllerから地味に追いかけていったら元々マクロを拡張するI/Fを持っていて、lib/redmine/wiki_formatting/macros.rbに説明があった。
        #   Redmine::WikiFormatting::Macros.register do
        #     desc "This is my macro"
        #     macro :my_macro do |obj, args|
        #       "My macro output"
        #     end
        #   end
ということらしい。

次はどこでマクロ登録を行うか。
redmineのプラグイン機構に乗っかってプラグインの初期化時にやったらよさそう、ということでscript/generateを使ってひな形を作成。

% script/generate redmine_plugin wiki_graphviz_plugin
      create  vendor/plugins/redmine_wiki_graphviz_plugin/app/controllers
      create  vendor/plugins/redmine_wiki_graphviz_plugin/app/helpers
      create  vendor/plugins/redmine_wiki_graphviz_plugin/app/models
      create  vendor/plugins/redmine_wiki_graphviz_plugin/app/views
      create  vendor/plugins/redmine_wiki_graphviz_plugin/db/migrate
      create  vendor/plugins/redmine_wiki_graphviz_plugin/lib/tasks
      create  vendor/plugins/redmine_wiki_graphviz_plugin/assets/images
      create  vendor/plugins/redmine_wiki_graphviz_plugin/assets/javascripts
      create  vendor/plugins/redmine_wiki_graphviz_plugin/assets/stylesheets
      create  vendor/plugins/redmine_wiki_graphviz_plugin/lang
      create  vendor/plugins/redmine_wiki_graphviz_plugin/test
      create  vendor/plugins/redmine_wiki_graphviz_plugin/README.rdoc
      create  vendor/plugins/redmine_wiki_graphviz_plugin/init.rb
      create  vendor/plugins/redmine_wiki_graphviz_plugin/lang/en.yml
      create  vendor/plugins/redmine_wiki_graphviz_plugin/test/test_helper.rb
extraのsample_pluginを真似して上記プラグイン名にしたのだけど、これが元で後で痛い目に。「redmine_」が前置された結果、プラグインの名前が30文字を超えてしまいプラグイン設定の更新・参照に問題が発生。
※settingsテーブルのname属性がvarchar(30)なことが原因っぽい

なので、手作業で「wiki_graphviz_plugin」に変更した。
※今にしてみれば_pluginを削ったらよかったのか。。。。

拡張マクロの登録は、プラグインのinit.rbでプラグイン登録と同時にやってみた。これが正しいかはわからない。
require 'redmine'

RAILS_DEFAULT_LOGGER.info 'Starting wiki_graphviz_plugin for Redmine'

Redmine::Plugin.register :wiki_graphviz_plugin do
name 'Graphviz Wiki-macro Plugin'
author 'tckz'
description 'Wiki macro to render the graph using graphviz'
version '0.0.1'
settings :default => {'cache_seconds' => '0'}, :partial => 'wiki_graphviz/settings'

Redmine::WikiFormatting::Macros.register do

desc "Render the wiki page into graph image using graphviz.\n\n" +
" !{{graphviz(Foo)}}\n" +
" {{graphviz(format={png|jpg},Foo)}}\n" +
" {{graphviz(layout={dot|neato|fdp|twopi|circo},Foo)}}\n"
macro :graphviz do |obj, args|

end
end
end

こんな感じで登録しておくと、wikiページ(view)のレンダリング中に呼び出される。マクロ展開後のHTMLを返せばOK。

dotの構文に誤りがあったときはwikiマクロのエラーとして表示する形にしたいからIMG要素出力時点で一度画像をレンダリングする方向で。dot解釈時点で一旦レンダリングしたものをrailsのキャッシュに置いておいて実際の出力時はキャッシュから返す(ない場合は改めて生成)という感じ。

その2につづく
FC6→CentOS5.1に変えてから、graphvizを使うプログラムでエラーログが出てた。
最初は↓なメッセージだったので、
Error: Layout was not done.  Missing layout plugins?
graphviz本家からEL5用のrpmをダウンロードしてきてインストールした。
そしたら今度は、↓なメッセージ
Format: "png" not recognized.
んー、なんでじゃ。
と「graphviz format: "png" not recognized.」でgoogle検索したら2chのスレッドがヒット。
曰く、
743 名前:login:Penguin:2007/07/18(水) 04:04:48 ID:8oFxpWqq
    >>740
    CentOS4にRHEL4用のrpm入れて使ってるけど
    そんな問題起こってない。まったく問題なし。
    文字コードをUTF8に変換してTrueTypeフォント指定すれば
    日本語も出てるしね。Vine-2.6時代に四苦八苦してたのが嘘のよう。

    まさかgraphviz-gd入れてないとか言わないよな?
あ、ソレだ。
ということで、graphviz-gdもダウンロードしてきてインストール。

2chって、こういうノウハウもあるからあなどれんな~

pukiwikiにGraphvizプラグインというのがある。
graphvizはdot言語(?)で記述した構造を画像化するソフトだ。これをwikiのプラグインから使うというもの。 wikiで編集したdot言語が図に置き換わる。

ここのブログの以下のエントリで使っている図はgraphvizで描いたものだ。

導入

  • graphvizとlibrsvg2をインストールする。FC3、FC5の場合yumなりでインストールできちゃうはず。
    librsvg2はSVG出力しないか、pngでアンチエイリアスを使わない場合不要っぽいが、うちの環境では何かの依存関係ですでにインストールされていた。
  • GraphvizPluginをダウンロードする。
    今回使用したのは0.9
  • ダウンロードしたアーカイブをどこか適当な場所に展開する。
  • 展開先/graphvizplugin/0.9/ というディレクトリがあるはずなのでcdし「python setup.py bdist_egg」
    展開先/graphvizplugin/0.9/dist/graphviz-0.6.9-py2.3.egg というファイルが出来たはずなので、tracプロジェクトのplugins/ディレクトリにコピーする。 細かいことはREADMEに書いてある。
  • Graphviz Pluginはdotから生成した画像を保存する場所(=IMGのSRCのリンク先)が必要なのでこれを用意する。
    tracdもしくはwebサーバから書き込み可能にしなければならない。環境に応じて適宜。
    うちの環境では以下のディレクトリを使うことにした。
    tracプロジェクト/htdocs/graphviz/
  • trac.iniに[graphviz]セクションを追加する。
    うちの環境では以下の通り。他にもいろいろ設定できるが環境によって必須なのはgraphvizなどのコマンドのpath。
    [graphviz]
    cache_dir = tracプロジェクト/htdocs/graphviz
    png_antialias = 1
  • ここの環境ではtracdを使っているので再起動した。
Graphvizプラグインを導入し、tracのwikiに以下のように記述すると、
{{{
#!graphviz
digraph G {
    graph [rankdir="LR" size="6,4"]
    t1 [ label="明日は" ]
    t2 [ label="明日の" ]
    t3 [ label="風が" ]
    t4 [ label="吹く" ]
    t5 [ label="吹けば" ]
    t6 [ label="桶屋が" ]
    t7 [ label="儲かる" ]
    t1 -> t2 -> t3 -> t4
    t3 -> t5 -> t6 -> t7
    a->b->c
}
}}}

という画像が生成される。日本語も問題ない。

dot言語については本家Graphviz チュートリアルがわかりやすい。
Powered by Google App Engine

このアーカイブについて

このページには、過去に書かれたブログ記事のうちgraphvizカテゴリに属しているものが含まれています。

前のカテゴリはgaeです。

次のカテゴリはhardwareです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

Powered by Movable Type 5.04