プラグイン ホック » 履歴 » バージョン 1
Mitsuyoshi Yoshida, 2011/07/04 00:28
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 |