torutkのブログ

ソフトウェア・エンジニアのブログ

Redmineプラグイン作成でHookを使ってみる

はじめに

3年前の自分は、Redmineプラグインを作れるようになったと思っていたようです。
このはてな日記に、まず

と、Redmineプラグイン作成に取り組んだけど難しいよ、と書いており、その次に

と、なんかRedmineプラグインの作り方が分かった気になったように書いていました。また、文書番号等の一意な採番をするプラグインを作る過程を執筆していました(仕上げ途中で放置したままですが)。

また、昨年は、よく使うプラグインの一つ用語集プラグインRedmineのバージョンアップに対応していないので、自分でいじってRedmine 3.0対応をしてみた形跡があります。

ところが、いまは、もしプラグインを作ってと言われても「えーっ、むりー」と答えてしまうのが現状です。当時の取り組みが残念ながら身につかなかったのでした。

ここで、ソフトウェアエンジニアがソフトウェア開発ではない仕事にまみれると、プライベートでソフトウェア開発に関する意欲が高まるという現実逃避的モチベーション向上が発生します。その一部がRedmineプラグイン作成に及びました。ということで、これから再びRedmineプラグインが作成できるようになりたいと思います。

参考書籍

Redmineプラグイン作成に関する書籍(洋書)が1冊あり、購入したまま積読になっていました。

Redmine Plugin Extension and Development (English Edition)

Redmine Plugin Extension and Development (English Edition)

これを引っ張り出し、Redmineプラグイン作成能力の獲得をしようと思います。

フック

書籍「Redmine Plugin Extension and Development」の第2章は、フックを使ってRedmineを拡張する、という章題です。ここで、今までフックというのは聞いたことがあるかも、という程度できちんと認識したことはありません。しかし、書籍をみるとどうやらRedmineプラグイン作成としては、最初に考えるべき方法のようです。そこで、まずフックについて学ぶこととします。

Redmineには、あらかじめフックと呼ぶ場所が設けられており、フックにコールバック関数を登録することでRedmineを拡張することができるようになっています。Redmineプラグイン作成用に用意している機能なので、これを使ってやりたいことが実現できれば簡単だし、また今後のRedmineのバージョンアップに対するプラグインの追従についても、フックであればインタフェースを維持してくれる公算が高いので楽なのではと思います。

フックには、View hooks、Controller hooks、Model hooks、Helper hooksの4種類があり、フックの一覧を見るにはRedmineのソースをgrepで次のように調べるとあります。

redmine-3.3.0$ cd app
app$ grep -r call_hook *
  :
views/issues/index.html.erb:<%= call_hook(:view_issues_index_bottom, { :issues => @issues, :project => @project, :query => @query }) %>
  :

今回、チケット一覧画面に追加表示をしたいことがあるので、チケット一覧画面に用意されているView hooksを探してみました。すると、view_issues_index_bottom が該当するようでした。

view_issues_index_bottomフックに固定文字列を表示させるだけのプラグイン

今回は、Redmineが動作するLinux環境で、自分のユーザーのHOME下にredmine-3.3.0を展開し、ここをいじってプラグインを作成します。サーバーはWEBrickRDBMSはsqlite3を使います。(設定方法は省略)

  1. プラグインの雛形を生成
  2. プラグインの情報をinit.rbに記載
  3. フックへ登録するコールバックの定義
  4. フックへ登録(init.rb)
プラグインの雛形を生成

まず最初にプラグインの雛形を生成します。プラグインの名前は、redmine_issues_kpiとします。

~$ cd redmine-3.3.0
redmine-3.3.0$ ruby bin/rails generate redmine_plugin redmine_issues_kpi
  :

実行すると、plguins/redmine_issues_kpiディレクトリの下に雛形が生成されます。

プラグインの情報をinit.rbに記載

生成された雛形の中にプラグイン情報(プラグイン名、作者名、説明、バージョン、プラグインURL、作者URL)を記述するinit.rbがあるので、この記述を修正します。

Redmine::Plugin.register :redmine_issues_kpi do
  name 'Redmine Issues Kpi plugin'
  author 'TAKAHASHI,Toru'
  description 'Show issues summary value'
  version '0.0.1'
  url 'http://github.com/torutk/redmine_issues_kpi'
  author_url 'http://www.torutk.com'
end
フックへ登録するコールバックの定義

まず、フックのコールバックを実装するクラスを定義します。このクラスは、Redmine::Hook::ViewListenerクラスを継承します。クラス定義を記述するファイルは、プラグインのlib下に置きます。

  • lib/hooks.rb
module RedmineIssuesKpi
  class Hooks < Redmine::Hook::ViewListener
    render_on :view_issues_index_bottom,
              :partial => 'issuesIndexBottom'
  end
end

Redmineプラグイン作成の情報を調べていると、moduleを定義している例と定義していない例を見かけますが(定義していないものが多い感触)、書籍「Redmine Plugin Extension and Development」ではmoduleを導入しているのでそれに倣っています。moduleにはプラグイン名をPascalケースにした文字列を指定しています。

クラスの中身ですが、フック名と同じ名前のメソッドを定義して、その中で文字列(HTML)を作成しreturnするサンプルをよく見かけますが、書籍ではrender_onでフック名と部分テンプレートを指定しているのでそれに倣います。

部分テンプレートは、ここで指定した名前の先頭にアンダースコアを付け拡張子を.erbとしたファイルに記述します(拡張子は、.html.erbなどでも可)。ファイルの場所はプラグインのapp/view/issuesの下に置きました。view_issues_index_bottom フックの場合、プラグインのapp/viewの下にissuesというディレクトリを作成し、その中に置く必要があります。

  • app/views/issues/_issuesIndexBottom.html.erb
<h3>HOOKED</h3>
<p>rendered partial html</p>
フックへ登録(init.rb)

init.rbに次を記述します。

require_dependency 'hooks'

libしたのhooks.rbが呼び込まれます。

実行

Redmineを実行します。WEBrickサーバーとして実行します。

redmine-3.3.0$ bundle exec rails server --bind=0.0.0.0
=> Booting WEBrick
=> Rails 4.2.6 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
[2016-07-31 00:46:11] INFO  WEBrick 1.3.1
[2016-07-31 00:46:11] INFO  ruby 2.2.3 (2015-08-18) [x86_64-linux]
[2016-07-31 00:46:11] INFO  WEBrick::HTTPServer#start: pid=11726 port=3000

次は、リモートからブラウザで接続し、チケット一覧を表示させた画面です。フックで追加した内容が一覧の下部にあるのが分かります。


.html.erbの記述(課題)

さて、erbへの記述は難易度が上がるので今後の課題として残します。