• redmineの wikiマクロで graphviz その4

    その3」を書いたのが3年以上前なのか。
    redmineのwikiにgraphvizを使った画像を埋め込めるようにするマクロ。
    https://github.com/tckz/redmine-wiki_graphviz_plugin

    graphviz_meマクロは、このマクロが埋め込まれているwikiページ全体をdotとみなして画像描画する。
    wiki編集中のプレビューの際に、wikiマクロに渡ってくるモデル(WIkiContent)が保存済のもののため、今現在編集している内容で描画できていなかった。(一旦保存する必要があった)
    よくよく考えてみれば、wikiマクロが呼び出されたときのselfがviewを指しているのでリクエストのパラメータも取得できるのだった。
    マクロの挙動がformの構造に依存するのはイヤだと思いつつも、保存しないでプレビューできたほうがマシか。

    編集中テキストのプレビューの場合は画像をdataスキームでimg@srcに直接入れちゃうことにした。

    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg・・・TkSuQmCC">
    古いブラウザだと表示できないけど。
    画像部分の通信サイズが大きくなるけどレスポンス一回で画像もHTMLも返せると、取得した編集中テキストをまるごと受け取って画像を返すactionを新設しなくていい。


    環境:
    CentOS 6.2 x64
    ruby-1.8.7-p352
    mysql-server-5.1.61
    graphviz-2.26.0
    redmine-1.3.0
     

  • redmineの wikiマクロで graphviz その3

    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がついてしまうようだ・・。



  • redmineの wikiマクロで graphviz その2

    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がない。。。)
    とりあえず上記の環境では動くみたい。



  • redmineの wikiマクロで graphviz その1

    graphviz_macro.png
    redmineの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につづく
  • graphvizで Format: "png" not recognized.

    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って、こういうノウハウもあるからあなどれんな~