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
Hook のリスト(却下)
Redmine における hook 呼び出しのコアの処理は下記のような非常にシンプルな仕組みとなっている:
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
とかも使っていない。
参考文献
- @digdagdag, "Redmine PluginのViewの拡張ポイント(View hook)がどこにあるか?を列挙する", Qiita, 2014-10-07
- "Hooks List", Redmine Wiki
- /lib/redmine/hook.rb, Redmine source code
- ruby - How to prepend classmethods, Stackoverflow, 2017-05-23