開発環境の設定を使い回すためにプライベートなgemを作ってみる

この記事は、[Livesense Advent Calendar 2016 その３](http://qiita.com/advent-calendar/2016/livesense_3) の12日目です。

# はじめに
いきなりですが、heroku便利ですよね。
私自身も、個人でちょっとしたものを作ったり、仕事でプロトタイプサイトを作ったりする時などによくお世話になっています。
ただ最近、アプリケーションが増えてくるにつれて、毎回同じようなGemをGemfileを記載したり、管理画面などでよく使うCSSフレームワークに合わせたscaffold用のテンプレートを用意したり、それらをアプリケーション単位で管理することが結構面倒になってきましたので、今回はそのあたりを全てGemにする手順をまとめてみました。

## 今回やること
今回は、開発用に利用する汎用的なこのあたりをGemで管理してみます

 * アプリケーションに依存しないようなデバッグ用のGem（開発環境で利用するようなbetter_errorsとかpry-*みたいなもの）
 * デバッグ用の設定ファイル（とりあえずrubocopあたり）
 * 管理画面で利用するscaffoldのテンプレート(app:templates:copyで生成されたもの)

# どうやって管理するか？

デバッグ用のgemや設定ファイルだけを管理するのであれば、githubあたりのパブリックなリポジトリでgemを作成して管理しても良いと思うのですが、今回はCSSフレームワークを（個人的には）いい感じにカスタマイズした恥ずかしいテンプレートも一緒に管理したかったので、プライベートなパッケージリポジトリとして利用することができる[Gemfury](https://gemfury.com/)[^1]を利用しました。

[^1]: Gemfuryはクラウドパッケージサービスの一種で、RubyGemsだけでなく、npm、PHPComposerなどなど各種言語のパッケージモジュールを管理できます

# Gemを作る
まずは管理用のgemを作ります。

``` 
$ bundle gem developer -t 
```

1.アプリケーションに依存しないようなデバッグ用のGemを追加
開発デバッグ時に利用するGemを追加します。

```Ruby:developer.gemspec
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'developer/version'

Gem::Specification.new do |spec|
  ...
  spec.add_dependency "better_errors"
  spec.add_dependency "pry-rails"
  spec.add_dependency "pry-byebug"
  spec.add_dependency "pry-doc"
  spec.add_dependency "pry-coolline"
  spec.add_dependency "rubocop"
end
```

2.デバッグ用の設定ファイルを設置
独自ルールが含まれた.rubocop.ymlなどを追加します。

```YAML:.rubocop.yml 
# こんな感じのやつ
Metrics/MethodLength:
  Max: 15 # default: 10
```

3.管理画面で利用するscaffoldのテンプレートを移動
railsでscaffoldを利用した時に生成されるテンプレートも今回のgemに入れておきます。
テンプレートを用意する場合には、[Rails ジェネレータとテンプレート入門 | Rails ガイド](http://railsguides.jp/generators.html)のように独自のgenerateタスクを用意するのが一般的なのかなぁと思うのですが、今回は標準のscaffoldジェネレーターのテンプレートを変更したかったので、別のrailsアプリケーションでscaffoldのテンプレートを生成して、こちらに移動します。

```
# このコマンドで生成されたlib/templates以下のファイルをgemに移動
$ bundle exec rake app:templates:copy
```

gemの作成はこれで完了です。本来であれば、この後にgemのパッケージを　``` rake build ``` などで作成してそれをアップロードしたりもできますが、今回はリポジトリごとGemfuryに登録しようと思いますのでパッケージの作成は手元では行いませんでした。

# 作成したGemをGemfuryに登録する

[Gemfury](https://gemfury.com/)の「Sing in with github」から登録して、ログイン後に左ナビの「Upload」にアクセスすると、各言語のパッケージ別のアップロードの仕方が載っているので、そこからRubygemsの以下を使って今回作成したGemをGemfuryにアップロードします。

```
git remote add fury https://<username>@git.fury.io/<username>/<package-name>.git
git push fury master
```

成功するとこんな感じにパッケージに追加されます。びっくりするほど簡単ですね。

![image](https://qiita-image-store.s3.amazonaws.com/0/100018/e29010d4-b62a-bdf6-830a-5a89dbf9bf36.png)


# 実際にGemを使ってみる

1.作成したGemのインストール
先ほど作成したGemをGemfileに追加して使ってみます。追加方法は[ここ](https://gemfury.com/help/install-gems#gemfile)でも記載されています

```Ruby:Gemfile
.
.
.

group :development do
  gem 'developer', '0.1.0', source: 'https://<secret-token>@repo.fury.io/<username>/'
end
.
.
.
```

一応疎通確認します

```
$ bundle exec pry
[1] pry(main)>
```

プライベートなgemで追加したgemが利用できることが確認できました

2.デバッグ用の設定ファイルを利用する
今回はrubocopを追加していたので、そこで用意した設定を利用先のアプリケーションでも有効にします

```Ruby:.rubocop.yml
inherit_gem:
  developer: .rubocop.yml
```

試しに新しく追加したルールが適用されているか確認してみます

```
$ bundle exec rubocop lib/sample.rb
Inspecting 1 file
C

Offenses:

lib/sample.rb:3:3: C: Method has too many lines. [18/15]
  def hoge ...
  ^^^^^^^^
```

適用されてました

3.scaffoldのテンプレートを適用する

最後に ``` app:templates:copy ``` で生成したscaffoldのテンプレートを有効にします（少し無理矢理ですが

```Ruby:config/application.rb
module SampleProduct
  class Application < Rails::Application
  ...
  config.generators do |g|
    g.templates = %W(#{Gem::Specification.find_all_by_name('developer').first.full_gem_path}/lib/templates)
  end
  ...
  end
end
```

# おわりに
なんかherokuのくだりが全く生かされていない感じの話でしたが、これで開発環境のデバッグで利用する設定をGemで管理することができるようになりました。今回は個人で利用することを想定していましたが、チーム内でも、コーディング規約やみんながデバッグ時によく使うgemなどをアプリケーションに依存せずにまとめておけると後から入った人などにも情報共有などがしやすそうですね。

ちなみに、今回scaffoldのテンプレートをGem側に移動しましたが、これはRails5対応でapplication.rbに記載していたlibのautoload→eager_loadと置き換えた場合に、production環境でlib/templates以下がエラーになる(lib/templates/rails/controller/controller.rbの中身がerb形式で書かれているから)という現象も回避できるのでscaffoldのテンプレートの設置場所に困っている方などは一度試してみてください :)

明日は13日目です！ @omega999 さん宜しくお願いします！