Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

前回の内容: Redmine3.2プラグイン開発入門 (2) - 既存画面に項目を追加する

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

Redmineのメソッドをカスタマイズ

前回チケットの参照画面にviewを追加しましたが、チケットに紐づいたテストデータの読込/更新が実装されていませんでした。
そこで今回はRailsの機能であるalias_method_chainを使って既存メソッドをカスタマイズし、チケットデータを読み込む際にテストデータも読み込むようにします。
alias_method_chainの説明はこちらの記事によく書かれているので、本稿ではRedmineのプラグイン開発におけるalias_method_chainの利用方法に説明を絞ります。

modelの作成

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

# 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メソッドに処理を加えます。

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で読み込みます。

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を作成します。

bundle exec rails g redmine_plugin_controller sample tests
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
config/routes.rb
Rails.application.routes.draw do
  resources :issues do
    post "tests/:id" => "tests#update"
  end
end

viewの編集

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

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のリポジトリに置いていますので、よろしければ参考にしてください。

終わりに

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

参考

caratinc
2016年12月に創業したスタートアップです。レコメンド型AI転職アプリGLIT(グリット)の開発・運営を行っています。
https://www.caratinc.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away