Redmine
redmine_plugin

Redmine PluginのView Hookがどこにあるか列挙、完全版

Redmine plugin には、view hook と呼ばれる View の拡張ポイントがある。
それを視覚的に見る試みとして @digdagdag さんによる研究 があるが、hook 自体は手作業で入れている。これを漏らすことなく自動的にやりたい。

TL;DR

% cd plugins
% git clone https://github.com/cat-in-136/redmine_view_hook_for_dev
% cd ..
% bundle exec rails s

projects_test_issues.png

Hook のリスト(却下)

Redmine における hook 呼び出しのコアの処理は下記のような非常にシンプルな仕組みとなっている:

/lib/redmine/hook.rb
      def hook_listeners(hook)
        @@hook_listeners[hook] ||= listeners.select {|listener| listener.respond_to?(hook)}
      end

# snip

      def call_hook(hook, context={})
        [].tap do |response|
          hls = hook_listeners(hook)
          if hls.any?
            hls.each {|listener| response << listener.send(hook, context)}
          end
        end
      end

すなわち、登録されたリスナの中から hook というメソッドが定義されたリスナに対して順次そのメソッドを呼んで結果を配列に格納していっているだけである。

コンソールなどから Redmine::Hook.listeners でリスナが取れるのでそこからメソッドを取ることは可能ではあると思われる。メソッドの一覧から、例えば to_s といった hook 用途以外のものを取り除いたりする作業が必要であり、これはなかなか面倒である。

そのため、この方法は諦めた。

なお単純にリストアップするだけならば redmine の公式Wikiの Hooks Listとしては、下記の通り grep をしてくれと言っている。普段使いはこれで良いが、機械的に扱うには、これからさらにテキスト処理が必要なのでこれも却下とした。

On Redmine 2.0.0 and up the rake task has been deprecated.
As an alternative you can use either of the following in the Redmine directory (see RE: hook list?).

grep -r call_hook *                                       # list of source lines with hook calls
grep -rohT 'call_hook([^)]*)'                             # list of hooks calls and source files
grep -roh  'call_hook([^)]*)' | sort -u | grep '([^)]*)'  # list of hooks calls only

解決策:パッチ

結局 call_hook をパッチする解決策を取ることにした。

最終結果は非常にシンプルで、call_hook に対して下記のメソッドを prepend することとし、call_hook の戻り値の最初の要素に常に hook 名を表示する HTML コードを含まれるようにした。

    def call_hook(hook, context={})
      [
        "<p class=\"redmine_view_hook_for_dev\">#{hook}</p>",
        *(super(hook, context)),
      ]
    end

なお prepend する際は、若干注意が必要で、ここの call_hook はクラスメソッドのため Redmine::Hook に対して prepend() するのではなくて、Redmine::Hook.singleton_class に対して prepend() する必要がある。

外見

@digdagdag さんによる研究において、

そして黒文字にしてしまったので、見づらい。

とあったので、赤太字とした。

また、開発者ツール(F12)で $(".redmine_view_hook_for_dev") で探し出せる利便性を確保するため CSS クラスを割り当て、CSS でスタイリングすることにした。CSS スタイルの適用そのものは、一般的な方法で view_layouts_base_html_head に hook メソッドを入れることで実現した。

プラグイン化(手抜き)

以上のように述べた内容を簡素なプラグイン化してまとめた。https://github.com/cat-in-136/redmine_view_hook_for_dev

データベースも設定も何もないので、開発に必要なときに plugins フォルダにおいて、不要になったら消すという使い方を想定している。

なお、面倒だったので手抜きな作りにしていて、すべてのロジックを init.rb に入れ込んでいるし、ActionDispatch::Reloader とかも使っていない。

参考文献