[Ruby]Dockerで外部依存のあるgemを試す

# Dockerで外部依存のあるgemを試す

## 概要

開発中、使っている`gem`が原因であろう事象が発生したときに、`github`でホスティングされている`gem`の場合その`gem`のソースを調べますよね。

で、調べた結果どうもおかしいからもっと詳細に確認してみたいということでcloneしてきてtest実行なども行おうとすると、ライブラリによってはとても大変な時があります。

代表的なのは外部依存があるパターンです。

`ruby`のライブラリレベルであれば、`bundler`で依存関係含めてインストールできるので問題ないですが、外部のミドルウェアに依存しているようなライブラリになると結構大変です。

ライブラリの挙動を調査、確認したいだけなのにその都度ミドルウェアをローカルPCにインストールして・・・なんてやってられないですよね。

最近たまたまそういうケースがあって、`docker`を使って非常にうまく対処できたのでそのメモです。
また、OSSのライブラリにプルリク送ってみたのは始めただったのでその部分も書いてみます。

## 背景

今関わっているプロジェクトでは、既存のシステムを`rails`に乗せ換える必要があったので、DBは新規構築でなく既存のスキーマをそのまま使う要件となっていました。なので、ActiveRecord wayな`id`カラムが主キーになるテーブル以外に複合主キーのテーブルがたくさんあります。
それをサロゲートキーを追加してActive Recordに合う形にする方式もあると思いますがsqlの変更や既存データの移行含めて大変そうなので、今のスキーマのまま移行したいというのが要件です。

そこで`rails`で複合主キーを扱うために有名な[composit-primary-key](https://github.com/composite-primary-keys/composite_primary_keys)を導入しています。

このライブラリはとても良く出来ていて`oracle`, `mysql`, `postgresql`, `sqlite3`などメジャーな`DBMS`に対応していて、業務系のシステムをrailsで扱う場合とても重宝します。

## 今回発生した問題

つい先日, `rails`の [rc1](https://github.com/rails/rails/releases/tag/v5.0.0.rc1) が出ましたね！

上記のプロジェクトは`rails5`で構築しようとしているので早速導入しました。するとエッジな`rails`の常だと思いますが、`composit-primary-key`がまだ`rc1`に対応しておらず、[v5.0.0.beta3](https://github.com/rails/rails/releases/tag/v5.0.0.beta3)まで正常に動いていたコードがエラーになりました。

`composit-primary-key`のサイトを見てみると導入していたバージョンは`rails5`対応版である [v9.0.0.0.beta1](https://github.com/composite-primary-keys/composite_primary_keys/releases/tag/v9.0.0.0.beta1) なので、これでエラーが起こっているということは`rails5`の`beta3`から`rc1`の間にはいった変更のせいでエラーになっているのであろうと推測できます。

### 具体的な問題
`rails`を`rc1`に変更すると、下記のエラーで起動すらできませんでした。

```
ArgumentError: wrong number of arguments (given 2, expected 1)
  /var/data/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.0.rc1/lib/active_record/connection_adapters/abstract/connection_pool.rb:842:in `establish_connection'
  /var/data/vendor/bundle/ruby/2.3.0/bundler/gems/composite_primary_keys-9ff65e55df48/lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb:25:in `establish_connection'
```

`ArgumentError`ですので、`composit-primary-key`が`rails`の`API`の変更においついていないんだろうなと推測できます。

## composit-primary-keyの調査

さて、アタリはついたので、実際に`composit-primary-key`を確認してみます。
`git clone`して該当のブランチをテストしてみましょう。

```sh
% git clone git@github.com:composite-primary-keys/composite_primary_keys.git
```

で、該当のタグからブランチを切ります。

```sh
% git checkout -b v9.0.0.0.beta1 refs/tags/v9.0.0.0.beta1
```

多くの`github`プロジェクトでは、TOPレベルの`README.md`にテストの仕方を書いているので確認してみると

![スクリーンショット 2016-05-13 21.46.42.png](https://qiita-image-store.s3.amazonaws.com/0/34692/dbc48bc8-28ad-64b3-1c8c-7f41b71d488f.png)

と書いてあったので、`test/README`を見てみます。

![スクリーンショット 2016-05-13 21.53.19.png](https://qiita-image-store.s3.amazonaws.com/0/34692/0f378e76-00b6-db9c-03f1-f4207b4882f3.png)

すると、上記のようになっていたので、実際にテストするためには`bundle install`での依存ライブラリだけでなく、`dbms`本体が必要なようです。

## Dockerで環境構築

以下`%`で始まっているのはホスト側、`root@`で始まっているのはコンテナ内のシェルです。


`bundle install`で済むなら簡単なんですが、テストするために`mysql`だの`postgresql`だのをローカルにインストールなんてやってられません。

そこで`Docker`です。　`ruby`のライブラリなので、公式の`ruby`のコンテナ上で`mysql`や`postgresql`をインストールして確認して見ます。

### rubyのdockerコンテナ

`composit-primary-key`のトップレベルのファイルに`.ruby-version`があったので確認して見ると

```
% cat .ruby-version
2.2.2
```

とのことなので、`2.2.2`の環境を使います。
その際、`clone`してきたホスト側の内容はそのまま使いたい（ファイルの編集はホストでしたい）ので`-v`でマウントします。

#### コンテナ実行

```
% docker run -it -v $(pwd):/var/ruby ruby:2.2.2 bash
Unable to find image 'ruby:2.2.2' locally
2.2.2: Pulling from library/ruby
4d2e9ae40c41: Already exists
a3ed95caeb02: Already exists
7c8152785df5: Already exists
8b04aafd7cd8: Already exists
412dfc97bdb3: Already exists
4d2a6e278593: Already exists
1fce3d6a0fe1: Already exists
79aaa3ae3986: Already exists
Digest: sha256:76059222ec07aa8b9dc6fe2ff097a359ded336c98c664911bb3bd6545604ad5f
Status: Downloaded newer image for ruby:2.2.2
root@0f4fa7cb0443:/# 
```

マウントした`/var/ruby`に移動してホスト側で`clone`した内容を確認します。

```
root@581d9cc2d677:/# cd /var/ruby; ls
Gemfile  History.rdoc  README.rdoc  README_DB2.rdoc  Rakefile  composite_primary_keys.gemspec  lib  log  scripts  tasks  test  website
```

きちんと見えていますね。

では、とりあえず`mysql`でテストしてみましょう。docker公式の`ruby`は`debian`ベースなので、`apt`でインストールします。

```sh
root@581d9cc2d677:/var/ruby# apt-get update; apt-get install mysql-server
```

途中`mysql`の`root`のパスワードを入力させられました。運用するわけでは無いので何でもいいです。今回は`root`としました

インストールした`mysql`を起動します。

```sh
root@581d9cc2d677:/var/ruby# /etc/init.d/mysql start
[ ok ] Starting MySQL database server: mysqld ..
[info] Checking for tables which need an upgrade, are corrupt or were
not closed cleanly..
```

次に、`test/README`に従ってホスト側でテンプレートを元に`database.yml`を編集します。

```
% cp test/connections/databases.example.yml test/connections/databases.yml
% vi test/connections/databases.yml
```

`mysql`の設定のところに、インストール時に設定したパスワード, `root`を設定して保存します

```yml:test/connections/databases.yml
mysql:
  adapter: mysql2
  username: root
  password: root # <= ここを追加
  database: composite_primary_keys_unittest
```

これで設定は整ったので、コンテナ内で`bundle install`します。(ここではプロジェクトローカルに`gem`をインストールするために`--path`を指定してます)

```
root@581d9cc2d677:/var/ruby# bundle install --path=vendor/bundle
〜中略〜
Installing activesupport 5.0.0.rc1
```

問題になっている`rails rc1`がインストールされたことは確認しときます。

さて、これで実行出来るようになりました。引き続き`test/README`に従ってテストを実行します。

```
root@581d9cc2d677:/var/ruby# bundle exec rake mysql:build_database
rake aborted!
ArgumentError: wrong number of arguments (2 for 1)
/var/ruby/vendor/bundle/ruby/2.2.0/gems/activerecord-5.0.0.rc1/lib/active_record/connection_adapters/abstract/connection_pool.rb:842:in `establish_connection'
/var/ruby/lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb:25:in `establish_connection'
```

バッチリ同じエラーが再現しました！
さて、ココからはひたすら直すだけです。特に言語、ライブラリ依存でないので省略します（プルリク出したので内容は後述の内容参照）。エラー箇所と`rails rc1`になって変わったところをいろいろ確認して直します。

### 修正後の確認

さて、修正前はそもそもテストの実行すら出来なかったので、テストの実行箇所までログを残しておきます。


修正後のソースツリーで`test/README`に従ってテストしていきます。

```
# DB構築
root@581d9cc2d677:/var/ruby# bundle exec rake mysql:build_database
Using native MySQL
# テスト実行
root@581d9cc2d677:/var/ruby# bundle exec rake mysql:test
# 〜中略〜
176 runs, 437 assertions, 0 failures, 0 errors, 0 skips
```

すべてのテストが通りました。

同じ要領で、`postgresql`と`sqlite3`も`apt`でインストールして確認しました。詳細は各`dbms`依存になるので省略します。

## プルリク

さて、ここまで来たら自分のプロジェクトだけに適用(`Gemfile`で自分のリポジトリを指定する)してもいいですが、せっかくなのでプルリクして本家に取り込んでもらいましょう。

いざプルリクしようとすると、やっぱり生粋の日本人だと英語がネックですよね。私もかなりネックです。
まぁ、無視されて特に被害をうけるわけでもないので、中学生時代の記憶やらgoogle翻訳とかを駆使してカタコトの英語で`pull request`や`issue`を出してみましょう。

僕が出した`issue`と`pull request`はこちら。

https://github.com/composite-primary-keys/composite_primary_keys/issues/356
https://github.com/composite-primary-keys/composite_primary_keys/pull/357

##　まとめ
というわけで、なんとか今のプロジェクトで発生していたバグが直りました。「2016-05-13現在の`rails rc1`で`composit-primary-key`が動かない！」という方は、`Gemfile`で

```Gemfile
gem 'composite_primary_keys', github: 'composite-primary-keys/composite_primary_keys', branch: 'master'
```

にすると動くと思います。