Project

General

Profile

プラグイン ホック » History » Version 1

Mitsuyoshi Yoshida, 07/04/2011 12:28 AM

1 1 Mitsuyoshi Yoshida
"Redmine plugin hooks":http://www.redmine.org/projects/redmine/wiki/Hooks の日本語訳です。
2
3
h1. プラグイン ホック
4
5
{{>toc}}
6
7
Redmine では *ホック* という考え方が取り入れられています。これはプラグインで記述されたコードを実行させる API で実現され、 Redmine 本体のコードを修正することなく、 Redmine の機能を拡張することが出来るようになっています。
8
ホックを使うと Redmine がある特定コードの地点に到達したとき、プラグインで登録したコールバック関数を順に実行させることができます。
9
10
"使用可能なホックのリスト":http://www.redmine.org/projects/redmine/wiki/Hooks_List が公開されています。しかし、使用したいホックを探す一番いい方法は拡張したい機能が実装されている Redmine のソースの中身を見て、その周辺にあるホックを探すことです。
11
12
ホック以外にも次にあげるような Redmine の機能を拡張するための方法があります。
13
14
* [[プラグイン_インターナル#新しいメソッドの追加|新しいメソッドの追加]]
15
* [[プラグイン_インターナル#既存メソッドのラップ|既存メソッドのラップ]]
16
* [[プラグイン_インターナル#Rails-のコールバックの使用|Rails のコールバックの使用]]
17
18
19
h2. ホックの基本
20
21
前述したようにホックが呼ばれると、前もって登録しておいたコールバック関数が実行されます。このコールバック関数は一つだけ引数を受け取ります。受け取る引数は一つですが、ハッシュになっていてコールバック関数を実行する上で必要な種々の情報(context)が格納されています。後述するコントローラのビューホックの場合では、ビューホックのデフォルトの情報として以下のものが格納されています。
22
23
* :controller => カレントのコントローラのオブジェクト
24
* :project => カレントのプロジェクト (設定されている場合)
25
* :request => カレントの Web リクエストに関する情報を持ったリクエストオブジェクト
26
27
これらに加えてホックごとに決まった情報がハッシュには格納されています。このホックごとのデータは Redmine 本体中のコードの中で @call_hook@ メソッドを実行するときに直接渡されています。
28
29
モデルホックの場合は共通して渡されるデータはありません。各モデルホックはホックごとのデータのみハッシュに格納されています。
30
31
h2. ホックタイプ
32
33
ホックは大きくわけて次の 3 つに分類されます。
34
35
* ビューホック
36
* コントローラホック
37
* モデルホック
38
39
コントローラとモデルのホックはまったく同じ形式なのに対して、ビューホックは他の 2 つと使い方が違っています。
40
41
42
h3. ビューホック
43
44
ビューホックは表示の HTML を作成中に実行されます。このホックでは HTML 内のホックで決められている場所に、[[GuideNewAction#ビューの実装|部分描画]]を使って任意の HTML コードを挿入することが出来ます。単純なものであれば簡単に HTML を挿入する方法もあります。詳しくは次節を見てください。
45
46
47
h3. コントローラホック
48
49
コントローラホックはビューホックに比べるとその数は少ないです。これは "additional filters":http://apidock.com/rails/ActionController/Filters/ClassMethods やモデルクラスの拡張を使えば十分な場合が多いからです。また、コントローラのアクションの処理は簡略にしておくべきですし、多くはそのように実装されており、ホックを使うのはあまりふさわしくありません。しかし、たまに長い処理を行うアクションがあります。こういったものにはホックが使用されています。
50
51
ホックを適切に使用するために理解しておかなければならないことがあります。それは context ハッシュに格納されているオブジェクトは参照であるということです。もしコールバック内でオブジェクトを変更したとしても、その変更内容は実際のコントローラでも、その後のビューでも利用可能です。次にあげる簡単な例で考えてみてください。
52
53
@do_something@ メソッドをホックに登録したとします。
54
55
<pre><code class="ruby">
56
def do_something(context={ })
57
  context[:issue].subject = "Nothing to fix" 
58
end
59
</code></pre>
60
61
この関数が以下のようなコントローラのアクション中で呼ばれた時のことを考えてください。
62
63
<pre><code class="ruby">
64
issue = Issue.find(1)
65
# issue.subject(チケットのタイトル) は "Fix me" 
66
call_hook(:do_something, :issue => issue)
67
# ここでは issue.subject(チケットのタイトル) は "Nothing to fix" 
68
</code></pre>
69
70
見てもらったようにホックメソッドはその時点のチケットオブジェクトの値を変更することが出来ます。しかし、オブジェクトの参照を破壊してしまうため、完全にチケットを置き換えるはできません。
71
72
73
h3. モデルホック
74
75
モデルホックもコントローラホックと同じように使用することが出来ます。しかし、モデルホックの数はさらに少なくなっています。 モデルの場合には [[プラグイン_インターナル#新しいメソッドの追加|新しいメソッドを追加]] したり、 @alias_method_chain@ を使って [[プラグイン_インターナル#既存メソッドのラップ|既存メソッドをラップ]] したりすることによって機能の拡張を行います。
76
77
h2. ホックへのメソッドの追加
78
79
h3. ビューホック
80
81
次の例は @view_issues_form_details_bottom@ ホックにコールバックのメソッドを追加するものです。このホックはチケット編集のフォームにフィールドを追加するために使用されます。この例ではプラグイン名を MyPlugin(my_plugin) としています。
82
83
1. MyPlugin のプラグインの *lib/my_plugin/hooks.rb* で次のようなクラスを作成します。このクラス内で複数のホックに登録することも出来ます。
84
85
<pre><code class="ruby">
86
module MyPlugin
87
  class Hooks < Redmine::Hook::ViewListener
88
    # ここでは単に以下のファイルを部分描画しています。
89
    # app/views/hooks/my_plugin/_view_issues_form_details_bottom.rhtml
90
    #
91
    # context ハッシュとして渡される内容は部分描画時のローカル変数として使用することが出来ます。
92
    # このホックでハッシュに追加される情報は次のものです。
93
    #   :issue  => 編集されたチケット
94
    #   :f      => フィールドを追加するためのフォームオブジェクト
95
    render_on :view_issues_form_details_bottom,
96
              :partial => 'hooks/my_plugin/view_issues_form_details_bottom'
97
  end
98
end
99
</code></pre>
100
101
上で使った簡便な @render_on@ ヘルパーの変わりに次の例ではまったく同じことを別な方法で実装しています。ここで使うメソッドの名前は登録するホック自身と同じ名前にする必要があります。
102
103
<pre><code class="ruby">
104
module MyPlugin
105
  class Hooks < Redmine::Hook::ViewListener
106
    def view_issues_form_details_bottom(context={ })
107
      # コントローラパラメータはカレントの params オブジェクトの一部になっています。
108
      # これは文字列に部分描画を行い、それを返しています。
109
      context[:controller].send(:render_to_string, {
110
        :partial => "hooks/my_plugin/view_issues_form_details_bottom",
111
        :locals => context
112
      })
113
114
      # 上のコードではなく、プラグイン内で生成した文字列を返すことも出来ます。
115
      # その文字列がビューに挿入されます。
116
    end
117
  end
118
end
119
</code></pre>
120
121
122
2. 作成したホック用のファイルを init.rb で明示的に require する必要があります。次のようなコードになります。
123
124
<pre><code class="ruby">
125
require 'redmine'
126
127
# ここが重要な行です。
128
# lib/my_plugin/hooks.rb のファイルを rquire しています。
129
require_dependency 'my_plugin/hooks'
130
131
Redmine::Plugin.register :my_plugin do
132
  [...]
133
end
134
</code></pre>
135
136
137
h3. コントローラ、モデルホック
138
139
ビューホックと同じような方法でコントローラとモデルのホックもメソッドを登録することが出来ます。 作成したクラスは常に init.rb で require する必要があることは忘れないで下さい。
140
以下に例をあげます。
141
142
<pre><code class="ruby">
143
module MyPlugin
144
  class Hooks < Redmine::Hook::ViewListener
145
    def controller_issues_bulk_edit_before_save(context={ })
146
      # set my_attribute on the issue to a default value if not set explictly
147
      context[:issue].my_attribute ||= "default" 
148
    end
149
  end
150
end
151
</code></pre>
152
153
154
h2. 使用例
155
156
redmine_contacts プラグインでは実際にホックが使用されています。
157
158
* ビューホック
159
** "view_layouts_base_html_head_hook.rb":http://github.com/edavis10/redmine_contracts/blob/master/lib/redmine_contracts/hooks/view_layouts_base_html_head_hook.rb
160
** "view_issues_show_details_bottom_hook.rb":http://github.com/edavis10/redmine_contracts/blob/master/lib/redmine_contracts/hooks/view_issues_show_details_bottom_hook.rb
161
** "view_issues_form_details_bottom_hook.rb":http://github.com/edavis10/redmine_contracts/blob/master/lib/redmine_contracts/hooks/view_issues_form_details_bottom_hook.rb
162
** "view_issues_bulk_edit_details_bottom_hook.rb":http://github.com/edavis10/redmine_contracts/blob/master/lib/redmine_contracts/hooks/view_issues_bulk_edit_details_bottom_hook.rb
163
164
* ヘルパーホック
165
** "helper_issues_show_detail_after_setting_hook.rb":http://github.com/edavis10/redmine_contracts/blob/master/lib/redmine_contracts/hooks/helper_issues_show_detail_after_setting_hook.rb
166
167
* コントローラホック
168
** "controller_timelog_available_criterias_hook.rb":http://github.com/edavis10/redmine_contracts/blob/master/lib/redmine_contracts/hooks/controller_timelog_available_criterias_hook.rb
169
** "controller_issues_edit_before_save_hook.rb":http://github.com/edavis10/redmine_contracts/blob/master/lib/redmine_contracts/hooks/controller_issues_edit_before_save_hook.rb
170
** "controller_issues_bulk_edit_before_save_hook.rb":http://github.com/edavis10/redmine_contracts/blob/master/lib/redmine_contracts/hooks/controller_issues_bulk_edit_before_save_hook.rb
171
172
173
h2. TODO
174
175
* HowTo add filters to existing controllers?
176
* HowTo overwrite methods using alias_method_chain
177
** instance methods
178
** class methods
179
** initialize
180
** modules