ローカルおよびHeroku上のデータベースにcsvファイルのデータを追加（Sinatra）

# はじめに
Heroku上のデータベースにデータを格納し，それを参照するだけの簡単なSinatraアプリを作成したかったのですが，少し詰まったので整理も兼ねて記事にします．
ActiveRecordを用いずにcsvファイルからHeroku上のデータベースに格納します．
Heroku上にデータベースを設置し，そのデータベースから読み取りのみを行うWebサービスを作りたい方や初期データを格納したいといった方向けの記事です．

# 準備: Sinatraアプリの作成
準備として，Sinatraを含めた本記事（作成したいアプリ）で必要なパッケージのインストールを行います．

```console:~/
$ mkdir sinatra-db
$ cd sinatra-db
$ bundle init
$ vim Gemfile
$ bundle install --path vendor/bundle --without production
```

```ruby:Gemfile
# gem "rails"
gem "sinatra"
gem "sinatra-contrib"
gem "sinatra-activerecord", :require => 'sinatra/activerecord'
gem "activerecord", "~> 5.2.3"
gem "rake"

group :development do
    gem 'sqlite3'
end

group :production do
    gem 'pg'
    gem "activerecord-postgresql-adapter"
end
```

>
2019年9月29日，ActiveRecordの最新バージョンは6.0.0ですが，データベース作成で不具合が発生するので，動作確認の取れている5.2.3をインストールします．

これ以降，`~/sinatra-db/`を`~`と表記します．
また，以下に本記事対象外のファイルを列挙します．簡単に説明すると，やっていることは単純にデータベース内のデータを表示しているだけです．
後々書くべきこともコード内には書いています．必要に応じてここに戻ってきて，見返してみてください．

```ruby:~/app.rb
require 'bundler'
Bundler.require

class App < Sinatra::Base
    configure :development do
        ActiveRecord::Base.establish_connection(
            adapter: 'sqlite3',
            database: 'db/development.db'
        )
    end

    configure :production do
        ActiveRecord::Base.establish_connection(ENV['DATABASE_URL'])
    end

    get '/' do
        @comments = Comment.all
        erb :index
    end
end

class Comment < ActiveRecord::Base
end
```

```erb:~/views/index.erb
<% @comments.each do |comment| %>
    <div style="padding: 10px; margin-bottom: 10px; border: 1px dotted #333333;">
        <h3><%= comment.title %></h3>
        <h5><%= comment.detail %></h5>
        by <%= comment.name %>
    </div>
<% end %>
```

```ruby:~/config.ru
require './app'
run App
```

```:~/Procfile
web: bundle exec rackup config.ru -p $PORT
```

# 準備: Herokuにアプリを作成
更に準備として，先にHeroku上にアプリのデプロイ先を作成しておきます．
ここはぱぱーっと行きます．詳しく知りたい方は[ブログ](https://www.tech-ek.com/sinatra-deploy-to-heroku/)等を参考にしてください．

```sh:~/
# Herolu CLIはインストール済を想定
$ heroku login
...

# リポジトリの作成
$ git init
$ git add .
$ git commit -m "first commit"

# デプロイ先の作成（アプリの名前は一意じゃないとダメみたいです．お好きな名前で．）
$ heroku create (app name)

# デプロイ（後々）
$ git push heroku master
```

# データベースの作成とレコードの追加
本記事では，以下のようなテーブルをローカルとHeroku上に作成します．
ローカルではSQLite3，HerokuではPostgreSQLを使用します．

| id | name | title | detail |
|-----:|:-----|:-----|:-----|
| 1 | hoge | hello | Hello Sinatra |
| 2 | foo | foodbye | Goodbye Sinatra |
| 3 | bar | emergency | Reply Required |

以下のようなcsvファイルを用意します．

```txt:~/comments.csv
1,hoge,hello,Hello Sinatra
2,foo,goodbye,Goodbye Sinatra
3,bar,emergency,Reply Required
```

（1行目に`id,name,title,detail`のようなカラムの名前を入れたかったのですが，後々Heroku上のテーブルに追加する際にエラーが起きてしまうので入れていません．）

## ローカル
### データベースの作成
[別の記事](https://qiita.com/EisKern/items/4df5c9d7cb61f352b0d4#%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%8B%E3%82%89db%E3%82%92%E4%BD%9C%E6%88%90)を参考にマイグレーションファイルからデータベース（テーブル）を作成します．こちらも詳しくは先の記事を参考にしてください．

```sh:~/
$ vim Rakefile

# 使えるコマンドの確認
$ bundle exec rake -T
...

#　マイグレーションファイルの作成と編集
$ bundle exec rake db:create_migration NAME=create_comments
# => db/migrate/20190928154422_create_comments.rb
$ vim ./db/migrate/20190928154422_create_comments.rb

# データベースの作成
$ bundle exec rake db:migrate
```

```ruby:~/Rakefile
require 'sinatra'
require 'sinatra/activerecord'
require 'sinatra/activerecord/rake'
require './app'
```

```ruby:~/db/migrate/20190928154422_create_comments.rb
class CreateComments < ActiveRecord::Migration[5.2]
    def change
        create_table :comments do |t|
            t.string :name
            t.string :title
            t.string :detail
        end
    end
end
```

マイグレーションファイルにて，idをカラムとして追加していませんが，マイグレーションする際に自動でidカラムが作成されるみたいなので，ここでは追加していません．

`~/db`に`development.db`と`schema.rb`ができたと思います．
この`development.db`がデータベースの本体です．

### csvファイルからレコードの追加
データベースにSQLite3で直接接続し，コマンドでcsvファイルからレコードを追加します．
と言っても，コマンドを数ステップ打つだけでできてしまいます．

```:~/
$ sqlite3 ./db/development.db
...

sqlite> .schema comments
CREATE TABLE IF NOT EXISTS "comments" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar, "title" varchar, "detail" varchar);

sqlite3> .separator ,
sqlite3> .import comments.csv comments

sqlite3> select * from comments;
1|hoge|hello|Hello Sinatra
2|foo|goodbye|Goodbye Sinatra
3|bar|emergency|Reply Required

sqlite3> .quit
```

無事レコードを追加することができました．
Sinatraアプリの方でも呼び出せるか確認してみましょう．
`bundle exec rackup config.ru`と打ち，`localhost:9292`にブラウザでアクセスしてみます．
<img width="220" alt="スクリーンショット 2019-09-29 12.47.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168653/73322028-31d7-8bfc-e5dc-f4bb5cb57257.png">

Sinatra(ActiveRecord)の方でもレコードを呼び出すことができています．
これで，ローカルでのデータベースの構築は完了です．

## Heroku上
次にHeroku上でデータベースの作成からcsvファイルのレコードの追加を行ってみます．

### データベースの作成
Herokuでは，AddonとしてHeroku Postgresが提供されています．
これの無料プラン（hobby-dev）をアプリに追加して使用します．

```console:~/
$ heroku addons:create heroku-postgresql:hobby-dev
```

これだけで追加できました．
`heroku pg:info`で情報を見たり，`heroku pg:psql`で作成したデータベースにアクセスできます．
詳しくは，[公式リファレンス](https://devcenter.heroku.com/articles/heroku-postgresql)や[こちらのサイト](http://neos21.hatenablog.com/entry/2018/12/06/080000)も参考にしてください．

次にテーブルの作成です．と言っても，これ以降はローカルで行ったことと同じようなことを行うだけです．
はじめに，`git push`でherokuにデプロイします．`comments.csv`やマイグレーションファイルを含めてデプロイしている点がポイント(?)です．
（`.vendor`や`db/development.db`等のローカルでしか必要のないファイルやディレクトリは適宜`.gitignore`に追加してください．）

```console:~/
$ git add .
$ git commit -m "deploy"
$ git push heroku master
```

マイグレーションファイルからテーブルを作成します．

```console:~/
$ heroku run bundle exec rake db:migrate
```

`heroku run`の後にコマンドを続けることで，Heroku上でそのコマンドを実行するということですね．

### csvファイルからレコードの追加
最後に作成したテーブルにcsvファイルからレコードを追加しましょう．
ローカルではSQLite3でしたが，HerokuではPostgreSQLなので，少し書き方を変える必要がありますが，やっていることは同じです．

```:~/
$ heroku pg:psql
...
(app name):DATABASE=> \copy comments (id, name, title, detail) from 'comments.csv' with csv
COPY 3

(app name):DATABASE=> select * from comments;
 id | name |   title   |     detail      
----+------+-----------+-----------------
  1 | hoge | hello     | Hello Sinatra
  2 | foo  | goodbye   | Goodbye Sinatra
  3 | bar  | emergency | Reply Required

(app name):DATABASE=> \q
```

レコードが追加されていますね．
最後に確認として，`https://(app name).herokuapp.com/`にアクセスしてみてください．
ローカルで確認した時と同じように表示されていれば成功です！

# 終わりに
ボリュームの割にタイトルと中身のミスマッチ感がありますが，Sinatraアプリの作成からローカルおよびHerokuにデータベースを構築し，csvファイルからレコードを追加するところまでを書きました．
今回，「はじめに」で述べたように読み取りだけを行うアプリをHeroku上にて動かしたく，試行錯誤の結果，本記事のように進めると解決できたということで，色々ナンセンスな部分はあると思います．（個人的に，ファイルのほとんどをデプロイするのは如何なものかと思っています．）
もっと良いやり方あるよ〜って方はコメントお願いします．
また，書いているうちにデータベースとテーブルがごっちゃになってきたので，気が向いたらまた直します．

# 参考サイト
[Heroku Postgres | Heroku Dev Center](https://devcenter.heroku.com/articles/heroku-postgresql)
[Heroku アプリに PostgreSQL を導入する - Corredor](http://neos21.hatenablog.com/entry/2018/12/06/080000)
[SQLite3 のテーブルに CSV でデータを読み込む - CUBE SUGAR CONTAINER](https://blog.amedama.jp/entry/sqlite3-import-csv)
[CSVファイルをデータベースにインポートする - Qiita](https://qiita.com/eggc/items/cec72bda3759f875285f)