Redmine3.2プラグイン開発入門 (3) - Redmineのメソッドをカスタマイズをする

Redmine3.2プラグイン開発入門の第3弾です！

前回の内容： [Redmine3.2プラグイン開発入門 (2) - 既存画面に項目を追加する](http://qiita.com/saitoxu/items/45c2d66541126660790a)

今回は**Redmine本体のメソッドをカスタマイズ**します。

# Redmineのメソッドをカスタマイズ
前回チケットの参照画面にviewを追加しましたが、チケットに紐づいたテストデータの読込/更新が実装されていませんでした。
そこで今回はRailsの機能であるalias_method_chainを使って既存メソッドをカスタマイズし、チケットデータを読み込む際にテストデータも読み込むようにします。
alias_method_chainの説明は[こちら](http://www.techscore.com/blog/2013/02/27/rails-alias_method_chain-%E6%97%A2%E5%AD%98%E3%81%AE%E5%87%A6%E7%90%86%E3%82%92%E4%BF%AE%E6%AD%A3%E3%81%99%E3%82%8B%E5%B8%B8%E5%A5%97%E6%89%8B%E6%AE%B5/)の記事によく書かれているので、本稿ではRedmineのプラグイン開発におけるalias_method_chainの利用方法に説明を絞ります。

# modelの作成
まずは読み込み元となるmodelを作成します。

```bash:
# modelの作成
# issue_id: チケットID, content: テスト内容, result: テスト結果
bundle exec rails g redmine_plugin_model sample Test issue_id:integer content:text result:integer
# マイグレーション
bundle exec rake redmine:plugins:migrate RAILS_ENV=production
```

# patchモジュールの作成
チケットの参照画面の読み込み時にテストデータも同時に読み込むよう、
IssuesControllerクラスのpatchモジュールを作成しshowメソッドに処理を加えます。

```rb:lib/sample/issues_controller_patch.rb
module Sample
  module IssuesControllerPatch
    def self.included(base)
      base.send(:include, InstanceMethods)

      base.class_eval do
        unloadable

        alias_method_chain :show, :test
      end
    end

    module InstanceMethods
      def show_with_test
        # チケットに紐づいたテストデータがあれば取得
        @test = Test.find_by(issue_id: @issue.id)
        if @test.nil?
          # なければ新規作成
          @test = Test.create!(issue_id: @issue.id, content: "", result: 0)
        end
        show = show_without_test
        return show
      end
    end
  end
end
```
# モジュールの読み込み
作成したモジュールをinit.rbで読み込みます。

```rb:init.rb
Rails.configuration.to_prepare do
  require_dependency 'issues_controller'

  # patchモジュールの読み込み
  unless IssuesController.included_modules.include? Sample::IssuesControllerPatch
    IssuesController.send(:include, Sample::IssuesControllerPatch)
  end
end
```

これでチケットの参照画面にアクセスした際、チケットに紐づいたテストデータがあればそれがロードされ、なければ新規作成されるようになりました。

# controllerの作成
次にテストデータを更新する際のcontrollerを作成します。

```bash:
bundle exec rails g redmine_plugin_controller sample tests
```

```rb:app/controllers/tests_controller.rb
class TestsController < ApplicationController
  unloadable

  def update
    test = Test.find_by(issue_id: params[:issue_id].to_i)

    column = "content"
    value = params[:value]

    if params[:pk].to_i == 2
      column = "result"
      value = value.to_i
    end

    if test.update(column => value)
      render :nothing => true, :status => 200
    else
      render :nothing => true, :status => 404
    end
  end
end
```

```rb:config/routes.rb
Rails.application.routes.draw do
  resources :issues do
    post "tests/:id" => "tests#update"
  end
end
```

# viewの編集
最後に前回作成したviewを、テストデータの読込/更新に対応するよう修正します。

```html+erb:app/views/hooks/sample/_view_issues_show_description_bottom.html.erb
<hr>
<p><strong>Easy Test</strong></p>
<table id="easy-test">
  <tr>
    <td>
      <p>Content</p>
      <a href="#" id="test-content"><% if @test.content == "" %>Empty<% else %><%= @test.content %><% end %></a>
    </td>
    <td>
      <p>Result</p>
      <a href="#" id="test-result">
        <% if @test.result == 0 %>-<% elsif @test.result == 1 %>OK<% else %>NG<% end %>
      </a>
    </td>
  </tr>
</table>
<input id="test-id" type="hidden" value="<%= @test.id %>">
<link href="//cdnjs.cloudflare.com/ajax/libs/x-editable/1.5.0/jquery-editable/css/jquery-editable.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/x-editable/1.5.0/jquery-editable/js/jquery-editable-poshytip.min.js"></script>
<style type="text/css">
table#easy-test {
  width: 100%;
}
table#easy-test tr td:first-child {
  width: 66%;
}
table#easy-test tr td:last-child {
  width: 34%;
  vertical-align: top;
}
div.editable-input textarea {
  width: 500px;
}
div.editable-input select {
  width: 100px;
}
</style>
<script>
$(function() {
  $.fn.editable.defaults.url = window.location.href + '/tests/' + $("#test-id").val();
  $.fn.editable.defaults.mode = 'inline';
  $.fn.editable.defaults.showbuttons = 'bottom';

  $('#test-content').editable({
    type: 'textarea',
    pk: 1
  });
  $('#test-result').editable({
    type: 'select',
    pk: 2,
    source: [
      { value: 0, text: '-'  },
      { value: 1, text: 'OK' },
      { value: 2, text: 'NG' }
    ]
  });
});
</script>
```

以上で完成です！
第2,3回全体のソースはGitHubの[リポジトリ](https://github.com/saitoxu/HookTest)に置いていますので、よろしければ参考にしてください。

# 終わりに
駆け足でRedmineのプラグイン開発方法について紹介しましたが、
Railsのコールバックを利用する方法等、まだまだ紹介できていないことがあります。
ぜひご自分で調べて、実際に使ってみてください！

# 参考

- [Plugin Internals - Redmine](http://www.redmine.org/projects/redmine/wiki/Plugin_Internals#Wrapping-an-existing-method)
- [» Rails: alias_method_chain: 既存の処理を修正する常套手段 TECHSCORE BLOG](http://www.techscore.com/blog/2013/02/27/rails-alias_method_chain-%E6%97%A2%E5%AD%98%E3%81%AE%E5%87%A6%E7%90%86%E3%82%92%E4%BF%AE%E6%AD%A3%E3%81%99%E3%82%8B%E5%B8%B8%E5%A5%97%E6%89%8B%E6%AE%B5/)