Google ColabをRailsのコーディングテスト環境にしてみた

# 背景
エンジニアの採用について、コーディングテストがよく使われています。しかしRailsのコーディングテストはRailsプロジェクト及びデータがなければ実施できない問題がありますので、多くのところはRubyのコーディングテストのみ実施しているのは実情でした。

# はじめに
最近私はGoogle Colab環境で機械学習の練習を行っており、Colab(Jupyter Notebook)の便利さを改めて認識しました。
それで、Colabの実行環境を生かして、Railsのコーディングテストを行えるかを試みました。

# 環境構築
まず、RubyとRailsが実行できる環境をColab上で構築しなければいけません。
本記事では、構築の手順をゼロから案内いたします。
また、私は[こちらのノートブック](https://github.com/jerrywdlee/coding-test-rails/blob/master/vendor/notebooks/Rails_for_Colab_[ja].ipynb)を公開しましたので、ダウンロードして、Google Colaboratoryで実行しても良いと思います。

## Ruby On Rails環境構築

```sh
# Ruby環境構築
!apt update -y
!apt install -y -qq ruby

# 以降はRails用
!apt install -y -qq ruby-dev make sqlite3 libsqlite3-dev
# !apt install libtool libffi-dev libzmq3-dev libczmq-dev
!gem install --no-ri --no-rdoc bundler
```

こうすれば `%%ruby` の記述でRubyコードを走れますが、Railsのコードはまた別の方法を取らなければいけません。

## SQLのコーディングテスト環境
どうせRailsの環境を構築するので、Sqlite3を用いたSQLのテスト環境も構築しました。

```sh
# SQL文のテストもスムーズに行えるためのSQlite環境設定
%%bash

cat << SQLITERC > ~/.sqliterc
.header on
.mode column
SQLITERC

# ここは確認用
cat ~/.sqliterc
```

## ベースとなるRailsプロジェクトのクローン
該当リポジトリについては、２点注意しなければいけない点があります。
1. ColabにインストールされるRubyのバージョンはv2.5.1なので、Gemfileが `ruby '2.5.1'` になっているかを確認する必要があります。
2. `config/webpacker.yml` の `check_yarn_integrity` が `false` になっていることを確認する必要があります。

```sh
# RailsサンプルのClone
!git clone https://github.com/jerrywdlee/coding-test-rails.git
```

## デフォルトDirの移動
colabのデフォルトディレクトリは `/content` ですが、先ほどクローンしたレポジトリ配下に移動すべきです。
他のセルと違い、ここには先頭の `!` が必要ありません。
また、このコマンドは他のセルに入れて実行することができません。

```sh
# デフォルトのDirの移動(「!」がいらない)
cd coding-test-rails
```

## bundle及びデータベースをセットアップ
ビルドが遅いです。

```sh
!bundle install
!bundle exec rails db:create
!bundle exec rake db:migrate
```

# 環境確認
上記手順でRailsの実行環境が構築されました。これから環境を確認します。

## Rails環境確認
下記コマンドの原理は、`tmp/script.rb` にRailsのソースコードを一旦保存して、 `bundle exec rails runner tmp/script.rb`　を使って実行することです。
この手法を生かすと、Railsだけでなく、SQL、Node.jsなどもColabで実行できるようになります。

```sh
# Rails環境確認
%%bash

cat << RUBY > tmp/script.rb
puts "Rails v#{Rails.version}"
puts "Env: #{Rails.env}"
RUBY

bundle exec rails runner tmp/script.rb
```

結果：

```sh
Rails v6.0.0
Env: development
Running via Spring preloader in process 15095
```

## SQL環境確認
同じく `tmp/`配下に一時ファイルを保存して実行します。

```sh
# SQL環境確認
%%bash

cat << SQL > tmp/sql_code.sql
SELECT 1+1;
SQL

# cat tmp/sql_code.sql
cat tmp/sql_code.sql | bundle exec rails db
```

結果：

```sh
1+1       
----------
2     
```

# データの導入
ここまではRails環境を構築して確認しました、これからはRailsにサンプルデータを保存します。

## CSVファイルの作成
Colabは機械学習に特化した実行環境であるため、numpy、pandas、scikit-learn、seabornなど便利なライブラリはすでに導入済みでした。
ここでその力を借りて、[seabornサンプルデータ](https://github.com/mwaskom/seaborn-data)の一つ、タイタニック号の生死者データ(の一部)をRailsに導入します。

```python
# データCSVの作成(Python)
import seaborn as sns

# タイタニック号の生死者データ
titanic = sns.load_dataset("titanic")
display (titanic.head(5))

titanic.to_csv("tmp/titanic.csv", index=False)
```

書き出したCSVデータの確認：

```sh
!ls -la tmp/*.csv
!wc -l tmp/*.csv
```

```sh
-rw-r--r-- 1 root root 57018 Sep 13 04:59 tmp/titanic.csv
892 tmp/titanic.csv
```

## CSVデータの導入
内部ロジックは[ここ](https://github.com/jerrywdlee/coding-test-rails/blob/master/app/util/data_import.rb)で確認できます。
詳しい説明は割愛します。

```sh
# サンプルCSVをRailsにインポート
!bundle exec rails runner 'DataImport.load_titanic_csv "tmp/titanic.csv"'
```

結果: 

```sh
Running via Spring preloader in process 15368
891 passengers inseted!
```

注意: `bundle exec rails runner` で前述した [Rails環境確認](#Rails環境確認) のソースコードを実行してから、インポートをするのがおすすめです。いきなりCSVのインポートを実行すると、環境が止まってしまうケースがあります。

# サンプル質問
いよいよ環境構築が全て終えました。参考として、サンプルの質問をここで出します。

### タイタニック号乗客の生還率を求めなさい
*生還率: 生存した乗客数 / 乗客の総数*  

*ソースコードは下記のリンクで確認できます*  
*https://github.com/jerrywdlee/coding-test-rails*

#### Rails版
```sh
# Rails Ver.
%%bash

cat << RUBY > tmp/script.rb

# ここからはRailsコードです

alive_cnt = Passenger.alive.size
ttl_cnt =  Passenger.all.size
puts "Survival Rate: #{(alive_cnt.to_f * 100 / ttl_cnt).round(1)}%"

# ここまではRailsコードです

RUBY

bundle exec rails runner tmp/script.rb
```

結果: 

```sh
Survival Rate: 38.4%
Running via Spring preloader in process 15418
```

#### SQL版

```sh
# SQL Ver.
%%bash

cat << SQL > tmp/sql_code.sql

-- ここからはSQL文です

SELECT
  (alive_cnt * 1.0 / ttl_cnt) AS survival_rate
FROM
(
  SELECT COUNT(*) AS alive_cnt FROM passengers WHERE survived = 1
) AS alive,
(
  SELECT COUNT(*) AS ttl_cnt FROM passengers
) AS ttl;

-- ここまではSQL文です

SQL

cat tmp/sql_code.sql | bundle exec rails db
```

結果: 

```sh
survival_rate    
-----------------
0.383838383838384
```


# 参考
- https://qiita.com/kojix2/items/64f14e3487e3d957d4e5
- https://shobon.hatenablog.com/entry/2014/03/25/224131