プロジェクト

全般

プロフィール

プラグイン インターナル » 履歴 » バージョン 7

Mitsuyoshi Yoshida, 2011/07/02 23:58

1 1 Mitsuyoshi Yoshida
"Plugin Internals":http://www.redmine.org/projects/redmine/wiki/Plugin_Internals の日本語訳です。(結構、意訳しています。よく言えば超訳 ?)
2
3 2 Mitsuyoshi Yoshida
{{>toc}}
4
5 6 Mitsuyoshi Yoshida
h1. プラグイン インターナル
6 1 Mitsuyoshi Yoshida
7
プラグイン開発関連の情報はこのページに書いていきましょう。
8 7 Mitsuyoshi Yoshida
(訳注 : このページは redmine.org のページの翻訳なので、実際にプラグイン開発関連の情報を追加したい場合は [[プラグイン Tips]] の方に追加してください)
9 1 Mitsuyoshi Yoshida
10
11
h2. プラグインが動作する Redmine のバージョンを指定する。
12
13
プラグインで Redmine 本体の機能を使ったり、Redmine のページを改造するようなプラグインを作ったりすると、ある特定のバージョンの Redmine でしかプラグインが動作しなくなることがあります。
14
15
このような場合、プラグインが動作する Redmine のバージョンを指定する必要が出てきます。 "requires_redmine":http://rdoc.info/github/asoltys/redmine/master/Redmine/Plugin:requires_redmine メソッドを使えばこれが実現できます。(この機能は "#2162":http://www.redmine.org/issues/2162 で提案され、"r2042":http://www.redmine.org/projects/redmine/repository/revisions/2042 で実際に実装されました)
16 3 Mitsuyoshi Yoshida
このメソッドを使えば簡単、確実にプラグインが動作する Redmine のバージョンを指定することが出来ます。プラグインがロードされる際、 このメソッドを記述していると要求する Redmine のバージョンを満たしていない場合には、未対応のバージョンですとといったメッセージを出力してロードを中止します。
17 1 Mitsuyoshi Yoshida
18
例えば IIda さんの "Wiki extensions プラグイン":https://bitbucket.org/haru_iida/redmine_wiki_extensions/src/8e162f7a04bb/init.rb では以下のように使用されています。
19
20
<pre><code class="ruby">
21
Redmine::Plugin.register :redmine_wiki_extensions do
22
  name 'Redmine Wiki Extensions plugin'
23
  # :
24
  version '0.3.5'
25
  requires_redmine :version_or_higher => '1.1.0'
26
27
  # :
28
</code></pre>
29
30
h2. Redmine の本体機能のオーバーロード
31
32
Rails は [[GuideRails|MVC構造]] になっています。プラグインで Redmine 本体の機能を変えようした時、MVC のうちビューの場合にはコントローラやモデルと違って Redmine 本体のものをプラグインのもので上書きするオーバーロードの方法をとることになります。
33
34
コントローラやビューをプラグインで書き変えた場合に Redmine/Rails がどのような動作をとるか説明します。ここでのプラグインの名前は @MyPlugin@ としています。
35
コントローラだけ説明していますが、モデルの場合も同じような流れになります。
36
37
*コントローラ(モデル)*
38
39
# Rails の起動の開始
40
# Rails フレームワークをロード
41
# 各プラグインのロード
42
## MyPlugin 内で @IssueController@ を見つけると、その @show@ アクションの定義を見にいきます。
43
# @<redmine_folder>/app@ から Rails アプリケーション(Redmine)をロード
44
## そこで再度 IssueController を見つけ、アプリケーションの @show@ アクションの定義を見にいきます。
45
## ここでプラグインで定義された  @show@ アクションはアプリケーションのものに上書きされてしまいます。これは Rails というよりも Ruby の仕様上そうなるようになっています。
46
# Rails の起動が完了して、サーバが立ち上がる
47
48
*ビュー*
49
50
ビューの場合もコントローラとほぼ同じようにロードされますが、少し違うところがあります。これは Redmine のパッチ機能のためです。
51
52
# Rails の起動の開始
53
# Rails フレームワークをロード
54
# 各プラグインのロード
55
## @<redmine_folder>/vendor/plugins/my_plugin/app/views@ 以下にディレクトリを見つけると、それを views のパッチの *先頭に追加* します。
56
# @<redmine_folder>/app@ から Rails アプリケーション(Redmine)をロード
57
# Rails の起動が完了して、サーバが立ち上がる
58
# サーバに要求がきて、ビューの描画が必要になる
59
# Rails は要求されたアクションに合うテンプレートを探した後、プラグインのテンプレートをロード
60
    これはプラグインのビューがパッチとして *先頭に追加* されていたためです。 
61
# Rails はプラグインのビューを表示
62
63 4 Mitsuyoshi Yoshida
なぜ、 MVC のうちビューだけこのようになっているかというと、 モデルやコントローラの場合には Ruby のモジュールのインクルードを使えば、こちらは簡単に機能を拡張することが出来るからです。
64
プラグインで Redmine 本体の機能を変えたい場合にも Redmine 本体のモデルやコントローラのメソッドなどは上書きするべきではありませし、実際そういった機能の API は Redmine では用意されていません。
65 1 Mitsuyoshi Yoshida
しかし、ビューの場合には Redmine の本体の機能を上書きする方法をとることになります。Rails ではビューはモデルやコントローラと比べるとちょっとトリッキーな方法で機能が実現されいて、拡張するよりも書き換える機能の方が使い勝手がいいためです。 
66
67 5 Mitsuyoshi Yoshida
Redmine 本体の表示を変えるには @<redmine_folder>/app/views@ 以下のファイルと全く同じ名前のファイルをプラグインのディレクトリに置いておくだけです。そうするとそちらが表示の際に使われるようになります。例えばプロジェクトのインデックスページを書き換えたい場合、 @<redmine_folder>/vendor/plugins/my_plugin/app/views/projects/index.rhtml@ のファイルを作成します。
68 1 Mitsuyoshi Yoshida
69
70
h2. Redmine の本体機能の拡張
71
72
先ほどモデルやコントローラはオーバーロードしないと説明しましたが、まれに書き換えくなることはあります。そのような場合、かわりに次の方法をとります。
73
74
* モデルやコントローラに新しいメソッドを追加する
75
* 既存のメソッドをラップする
76
77
78
h3. 新しいメソッドの追加
79
80
新しいメソッドを追加する方法の分かりやすい例は Eric Davi さんの "Budget plugin":https://github.com/edavis10/redmine-budget-plugin/blob/5076b1c88b57c2068aa92cdf694769dbd22d061a/lib/issue_patch.rb にあります。
81
このプラグインではチケットモデルクラスに @deliverable_subject@ というメソッドを追加しています。
82
83
<pre><code class="ruby">
84
module IssuePatch
85
  def self.included(base) # :nodoc:
86
    base.send(:include, InstanceMethods)
87
  end
88
89
  module InstanceMethods
90
    # Wraps the association to get the Deliverable subject.  Needed for the 
91
    # Query and filtering
92
    def deliverable_subject
93
      unless self.deliverable.nil?
94
        return self.deliverable.subject
95
      end
96
    end
97
  end    
98
end
99
</code></pre>
100
101
102
h3. 既存メソッドのラップ
103
104 5 Mitsuyoshi Yoshida
Eric Davis さんの "Rate plugin":https://github.com/edavis10/redmine_rate/blob/4666ddb10e1061ca3ef362735d0d264676b99024/lib/rate_users_helper_patch.rb には既存のメソッドをラップするいい例となる記述があります。
105 1 Mitsuyoshi Yoshida
ここでは "alias_method_chain":http://blog.livedoor.jp/sasata299/archives/51166404.html を使って  @UsersHelper@ の @user_settings_tabs@ メソッドをラップして、 DB にチケットを保存するタイミングで処理を追加しています。 
106
@user_settings_tabs@ が Redmine から呼ばれる時の流れは次のようになります。
107
108
109
# Redmine 本体が UsersHelper#user_settings_tabs を呼び出す
110 5 Mitsuyoshi Yoshida
# user_settings_tabs が実行される (これは実際には user_settings_tabs_with_rate_tab です)
111
# user_settings_tabs_with_rate_tab がもともとの user_settings_tabs を呼び出す。(元のものは user_settings_tabs_without_rate_tab と名前が変更されています)
112 1 Mitsuyoshi Yoshida
# 元のメソッドの結果にプラグイン用のデータを追加
113 5 Mitsuyoshi Yoshida
# user_settings_tabs_with_rate_tab は Redmine 本体の結果にプラグイン用の結果を結合したものを返す
114 1 Mitsuyoshi Yoshida
115
<pre><code class="ruby">
116
module RateUsersHelperPatch
117
  def self.included(base) # :nodoc:
118
    base.send(:include, InstanceMethods)
119
120
    base.class_eval do
121
      alias_method_chain :user_settings_tabs, :rate_tab
122
    end
123
  end
124
125
  module InstanceMethods
126
    # Adds a rates tab to the user administration page
127
    def user_settings_tabs_with_rate_tab
128
      tabs = user_settings_tabs_without_rate_tab
129
      tabs << { :name => 'rates', :partial => 'users/rates', :label => :rate_label_rate_history}
130
      return tabs
131
    end
132
  end
133
end
134
</code></pre>
135
136 5 Mitsuyoshi Yoshida
alias_method_chain は小さな拡張用メソッドですが、とても強力です。
137 1 Mitsuyoshi Yoshida
138
139 5 Mitsuyoshi Yoshida
h2. Rails のコールバックの使用
140 1 Mitsuyoshi Yoshida
141 5 Mitsuyoshi Yoshida
チケットの保存や作成など際にプラグインで処理を追加したい場合、すべてのチケットに対して処理を追加したいならば、 Redmine の "ホック機能":http://www.redmine.org/projects/redmine/wiki/Hooks よりも Rails の "コールバック":http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html を使った方がいいと思います。
142
その主な理由は新しいチケットを作ったとき、:controller_issues_edit_before_save のホックにメソッドを追加したとしてもこちらは呼び出されないためです。
143 1 Mitsuyoshi Yoshida
144
Rails のコールバックを利用した例は Eric Davis さんの *"Kanban plugin"* を見てください。
145
146
* "init.rb#L10":http://github.com/edavis10/redmine_kanban/blob/000cf175795c18033caa43082c4e4d0a9f989623/init.rb#L10
147
* "issue_patch.rb#L13":http://github.com/edavis10/redmine_kanban/blob/000cf175795c18033caa43082c4e4d0a9f989623/lib/redmine_kanban/issue_patch.rb#L13
148
149
このプラグインでは、新規作成や更新を含めたチケットのすべての保存のタイミングで @issue.update_kanban_from_issue@ が確実に実行されるようになっています。
150
151 5 Mitsuyoshi Yoshida
もし、チケットを新規作成したい場合にだけ処理を追加したい場合には、 @before_create@ ではなく @after_save@ コールバックを使用してください。 @after_create@ コールバックではチケットの保存が成功したかどうかにかかわらず処理が呼び出されますが、 after_save を使うと実際に保存が成功された場合にだけ処理が実行されるようになっています。
152 1 Mitsuyoshi Yoshida
153
154 5 Mitsuyoshi Yoshida
h2. マイページへのブロックの追加
155 1 Mitsuyoshi Yoshida
156 5 Mitsuyoshi Yoshida
マイページのブロックに関して次のような質問がよくあります。
157 1 Mitsuyoshi Yoshida
158 5 Mitsuyoshi Yoshida
* マイページで、なぜかブロック選択用のドロップダウンメニューのメニュー項目が翻訳されません。
159 1 Mitsuyoshi Yoshida
160
ドロップダウンメニューの項目用の翻訳メッセージはプラグインのローケルファイルの記述する際、規約でどのエントリ名を使うか決められています。
161 5 Mitsuyoshi Yoshida
そのエントリ名はブロック用のプラグインのファイル名と同じでなければなりません。例えばそのブロック用ファイルの名前が次のようなものだったとします。
162 1 Mitsuyoshi Yoshida
163
<pre>
164
<myplugin_folder>/app/views/my/blocks/<myblocks_view_file_name>.erb
165
</pre>
166
167 5 Mitsuyoshi Yoshida
メニュー項目を翻訳したい場合、プラグインのローケルファイル *<myplugin_folder>/confige/locale/en.yml* などには次の行を追加する必要があります。
168 1 Mitsuyoshi Yoshida
169
<pre><code class="yaml">
170
<myblocks_view_file_name>: <ドロップダウンメニュー項目の表示名をここに記述します>
171
</code></pre>
172
173 5 Mitsuyoshi Yoshida
この決まった名前でローケルファイルに定義が記述されていないとメニュー項目は翻訳されていない状態になってしまいます。
174 1 Mitsuyoshi Yoshida
175
176
h2. 参照情報
177
178
* http://www.redmine.org/boards/3/topics/show/5121 (Which version of Redmine I need to use your plugin?)
179
* http://www.redmine.org/boards/3/topics/show/4283 (Can a plugin modify the view of the projects page?)
180
* http://www.redmine.org/boards/3/topics/show/4095 (Rails Engines and extending the issue model)