Redmineプラグイン開発するならgeneratorsを自社向けに書き換えるとべんり

Redmineプラグイン開発していますか?

## TL; DR

- Redmineのプラグインは`bin/rails generate redmine_plugin <plugin_name>`で生成できる
- 小さなプラグインをいくつも開発する場合、定型の作業はジェネレータを使って効率よく行えるようにするとべんり
- 標準のgeneratorで生成される内容を書き換えるやりかたの紹介

## Redmineプラグイン開発ではRails generatorを使います

だいたいここに書いてある
http://www.redmine.org/projects/redmine/wiki/Plugin_Tutorial

注目すべきは最初にかいてあるこのコマンド

```
bundle exec ruby bin/rails generate redmine_plugin <plugin_name>
```

## `bundle exec ruby bin/rails generate redmine_plugin <plugin_name>`は何をしているのか

Ruby on Railsでは定型のファイルを生成するための仕組み、Generatorがあります。
https://guides.rubyonrails.org/generators.html

Redmineではプラグイン開発がべんりになる独自のGeneratorを備えています。
http://www.redmine.org/projects/redmine/repository/show/trunk/lib/generators

## 標準のGeneratorに足りないもの

標準のGeneratorで生成されるファイルには物足りなさがあります。私はだいたい以下の内容を既存のRedmineプラグインからコピーして書き換えています。

- init.rbの内容を社名などで埋める
- CIでテストを実行するための設定を既存のプロジェクトからコピーして書き換える
- RSpecでテストをするための設定をコピーして書き換える
- factory_botの定義ファイル
- 開発でつかう便利gemを書いたGemfile

## 標準のGeneratorを改造する

Generatorで生成されるファイルを自社向けに書き換えて、毎回書かなくていいようにしましょう。

生成されるファイルのテンプレートは`lib/generators/redmine_plugin/templates/`にあります。

たとえば`init.rb`を書き換えたいときは`lib/generators/redmine_plugin/templates/init.rb.erb`を書き換えて使います。
http://www.redmine.org/projects/redmine/repository/entry/trunk/lib/generators/redmine_plugin/templates/init.rb.erb


### init.rb

標準では以下のような内容です。

```lib/generators/redmine_plugin/templates/init.rb.erb
Redmine::Plugin.register :<%= plugin_name %> do
  name '<%= plugin_pretty_name %> plugin'
  author 'Author name'
  description 'This is a plugin for Redmine'
  version '0.0.1'
  url 'http://example.com/path/to/plugin'
  author_url 'http://example.com/about'
end
```

私は毎回authorとurl/author_urlを書き換えているので、プレースホルダーが入っている部分を自分の名前で書き換えていきます。

```lib/generators/redmine_plugin/templates/init.rb.erb
Redmine::Plugin.register :<%= plugin_name %> do
  name '<%= plugin_pretty_name %> plugin'
  author 'Seiei Miyagi'
  version '0.0.1'
  url 'https://github.com/hanachin'
  author_url 'https://www.hanach.in/'
end
```

試しに実行してみましょう。

```
% bin/rails g redmine_plugin i_am_drinking_premium_malts
      create  plugins/i_am_drinking_premium_malts/app
      create  plugins/i_am_drinking_premium_malts/app/controllers
      create  plugins/i_am_drinking_premium_malts/app/helpers
      create  plugins/i_am_drinking_premium_malts/app/models
      create  plugins/i_am_drinking_premium_malts/app/views
      create  plugins/i_am_drinking_premium_malts/db/migrate
      create  plugins/i_am_drinking_premium_malts/lib/tasks
      create  plugins/i_am_drinking_premium_malts/assets/images
      create  plugins/i_am_drinking_premium_malts/assets/javascripts
      create  plugins/i_am_drinking_premium_malts/assets/stylesheets
      create  plugins/i_am_drinking_premium_malts/config/locales
      create  plugins/i_am_drinking_premium_malts/test
      create  plugins/i_am_drinking_premium_malts/test/fixtures
      create  plugins/i_am_drinking_premium_malts/test/unit
      create  plugins/i_am_drinking_premium_malts/test/functional
      create  plugins/i_am_drinking_premium_malts/test/integration
      create  plugins/i_am_drinking_premium_malts/README.rdoc
      create  plugins/i_am_drinking_premium_malts/init.rb
      create  plugins/i_am_drinking_premium_malts/config/routes.rb
      create  plugins/i_am_drinking_premium_malts/config/locales/en.yml
      create  plugins/i_am_drinking_premium_malts/test/test_helper.rb
```

うまくいきました!

```
% cat plugins/i_am_drinking_premium_malts/init.rb
Redmine::Plugin.register :i_am_drinking_premium_malts do
  name 'I Am Drinking Premium Malts plugin'
  author 'Seiei Miyagi'
  version '0.0.1'
  url 'https://github.com/hanachin'
  author_url 'https://www.hanach.in/'
end
```

### いらないディレクトリを消す

我々、RSpecを使っていてユニットテストを書くことはないのでそのあたりのディレクトリがあると困ります。消しましょう。


`lib/generators/redmine_plugin/redmine_plugin_generator.rb`を開いておもむろに数行コメントアウトします。

```lib/generators/redmine_plugin/redmine_plugin_generator.rb
class RedminePluginGenerator < Rails::Generators::NamedBase
  source_root File.expand_path("../templates", __FILE__)

  attr_reader :plugin_path, :plugin_name, :plugin_pretty_name

  def initialize(*args)
    super
    @plugin_name = file_name.underscore
    @plugin_pretty_name = plugin_name.titleize
    @plugin_path = File.join(Redmine::Plugin.directory, plugin_name)
  end

  def copy_templates
    empty_directory "#{plugin_path}/app"
    empty_directory "#{plugin_path}/app/controllers"
    empty_directory "#{plugin_path}/app/helpers"
    empty_directory "#{plugin_path}/app/models"
    empty_directory "#{plugin_path}/app/views"
    empty_directory "#{plugin_path}/db/migrate"
    empty_directory "#{plugin_path}/lib/tasks"
    empty_directory "#{plugin_path}/assets/images"
    empty_directory "#{plugin_path}/assets/javascripts"
    empty_directory "#{plugin_path}/assets/stylesheets"
    empty_directory "#{plugin_path}/config/locales"
    # empty_directory "#{plugin_path}/test"
    # empty_directory "#{plugin_path}/test/fixtures"
    # empty_directory "#{plugin_path}/test/unit"
    # empty_directory "#{plugin_path}/test/functional"
    # empty_directory "#{plugin_path}/test/integration"

    template 'README.rdoc',    "#{plugin_path}/README.rdoc"
    template 'init.rb.erb',   "#{plugin_path}/init.rb"
    template 'routes.rb',    "#{plugin_path}/config/routes.rb"
    template 'en_rails_i18n.yml',    "#{plugin_path}/config/locales/en.yml"
    # template 'test_helper.rb.erb',    "#{plugin_path}/test/test_helper.rb"
  end
end
```

試してみましょう。

```
% bin/rails g redmine_plugin i_am_drinking_2nd_premium_malts
      create  plugins/i_am_drinking_2nd_premium_malts/app
      create  plugins/i_am_drinking_2nd_premium_malts/app/controllers
      create  plugins/i_am_drinking_2nd_premium_malts/app/helpers
      create  plugins/i_am_drinking_2nd_premium_malts/app/models
      create  plugins/i_am_drinking_2nd_premium_malts/app/views
      create  plugins/i_am_drinking_2nd_premium_malts/db/migrate
      create  plugins/i_am_drinking_2nd_premium_malts/lib/tasks
      create  plugins/i_am_drinking_2nd_premium_malts/assets/images
      create  plugins/i_am_drinking_2nd_premium_malts/assets/javascripts
      create  plugins/i_am_drinking_2nd_premium_malts/assets/stylesheets
      create  plugins/i_am_drinking_2nd_premium_malts/config/locales
      create  plugins/i_am_drinking_2nd_premium_malts/README.rdoc
      create  plugins/i_am_drinking_2nd_premium_malts/init.rb
      create  plugins/i_am_drinking_2nd_premium_malts/config/routes.rb
      create  plugins/i_am_drinking_2nd_premium_malts/config/locales/en.yml
```

うまくいきました!

### なんでもできる

Generatorのメソッドは以下でいくつか紹介されています。
https://guides.rubyonrails.org/generators.html#generator-methods

Thor由来のメソッドもそれなりにあるので、ファイルの追加やディレクトリの作成など基本的な操作についてはThorのドキュメントを読むとよいでしょう。
https://www.rubydoc.info/github/erikhuda/thor/master/Thor/Actions.html

## まとめ

Redmineプラグイン開発をべんりにするテンプレート改造方法の紹介でした。

## 所感

内容が薄いとおもったあなた、編集リクエストを送ったりより内容が濃い記事をかいてくれ!! たのんだ!!!