Sinatraで最小限にActiveRecordでDBを扱う

# はじめに
Sinatraでデータベースを扱うためにネットで色々見ていたのですが，情報が古かったり断片的であったりして，思い通りに動かないことが多々あったので，最小限にデータベースを扱う方法を自分なりにまとめました．データベースではsqlite3を使用します．
とりあえず簡単にSinatraでデータベースを扱いたい人向けです．

### 環境
```console
$ ruby -v
ruby 2.6.0p0 (2018-12-25 revision 66547)

$ gem -v
3.0.1

$ bundler -v
Bundler version 2.0.1

$ sqlite3 -version
3.27.2 2019-02-25 16:06:06
```

# SQLファイルからDBを作成
今回使用したファイルはGitHubにあげています．
[Sinatra Sample 1](https://github.com/EisKern/sinatra-db-min/tree/master/sample1)

ファイル構成はこんな感じです．

```console:.
$ tree

├── Gemfile
├── Gemfile.lock
├── app.rb
├── config.ru
├── db
│   ├── messages.db
│   └── seeds.sql
├── vendor
│   └── bundle
└── views
    ├── layout.erb
    └── show.erb
```


## gemのインストール
`$ bundle init` -> `Gemfile`の編集 -> `$ bundle install --path=vendor/bundle`で必要なgemをインストールします．

```rb:Gemfile
# gem "rails"
gem 'sinatra'
gem 'sinatra-contrib'
gem 'activerecord'
gem 'sinatra-activerecord'
gem 'sqlite3'
```

最小限にはこれぐらいかと．

## DBの作成
この章では，SQLファイルからデータベースを作成します．

```sql:./db/seeds.sql
drop table if exists messages;

create table messages(
    name text,
    comment text
);

insert into messages values('aaa', 'Hello'); -- (1)
insert into messages(name, comment) values('bbb', 'Hello World'); -- (2)
```

messagesテーブルを作成，名前（name）とコメント（comment）を管理します．
(1)と(2)は書き方は違いますが，同じくレコードの挿入をしています．ただし，(1)を用いる場合，カラム数と引数の数が合わないとエラーとなります．
以下のコマンドで実際にデータベースを作成します．

```console:.
$ cd db
$ sqlite3 messages.db < seed.sql
```

これで新たに`messages.db`ファイルが作成されます．
（ちなみに，拡張子は`db`でなくても良いみたいです．）
色々確認してみましょう．

```console:./db
$ sqlite3 messages.db
SQLite version 3.27.2 2019-02-25 16:06:06
Enter ".help" for usage hints.

sqlite> .database
main: ~/db/messages.db

sqlite> .schema
CREATE TABLE messages(
    name text,
    comment text
);

sqlite> select * from messages;
aaa|Hello
bbb|Hello World

sqlite> .quit
```

作成およびレコードの挿入ができていることが確認できました．

## ActiveRecordでレコードの取り出し
今回は，データを取り出すために以下のファイルを作成しました．
Sinatraの書き方等については扱いません．

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

# 接続部分
ActiveRecord::Base.establish_connection(
    adapter: 'sqlite3',
    database: './db/messages.db'
)

class Message < ActiveRecord::Base
end

get '/' do
    "Number of Messages: #{Message.count}"
end

get '/show' do
    @messages = Message.all
    erb :show
end
```

接続部分は，他にも書き方はありますが，とりあえずこれで．sqlite3で`./db/messages/db`に接続します．
また，`show.erb`は以下のようになっています．共通部分`layout.erb`も作成していますが，中身は特にないので今回は省略．

```erb:./views/show.erb
<ul>
    <% @messages.each do |message| %>
        <li><%= message.name %>: <%= message.comment %></li>
    <% end %>
</ul>
```

これで完成です．
`bundle exec ruby app.rb`を実行して，ブラウザで`localhost:4567`にアクセスしてみましょう．
（`config.ru`を用いる場合は，`bundle exec rackup`で`localhost:9292`．）
`/`でデータベースに格納されているレコード数を確認でき，`/show/`でデータベースに格納されているレコードを確認できるようになっています．
レコードの追加や削除は，パラメータ等紹介しないといけないことが多いので，今回は省略します．なので，このプログラムでは，`sqlite3`コマンドでsql文で追加等をするしか方法はありません．

# マイグレーションファイルからDBを作成
今回使用したファイルはGitHubにあげています．
[Sinatra Sample 2](https://github.com/EisKern/sinatra-db-min/tree/master/sample2)

ファイル構成はこんな感じです．

```console:.
$ tree

├── Gemfile
├── Gemfile.lock
├── Rakefile
├── app.rb
├── config.ru
├── db
│   ├── messages.db
│   ├── migrate
│   │   └── 20190331060225_create_messages.rb
│   └── schema.rb
├── vendor
└── views
    ├── layout.erb
    └── show.erb
```

## gemのインストール

```rb:Gemfile
# gem "rails"
gem 'sinatra'
gem 'sinatra-contrib'
gem 'activerecord'
gem 'sinatra-activerecord'
gem 'rake'
gem 'sqlite3'
```

一つ目とほぼ同じですが，`rake`を追加でインストールします．

## DBの作成
### `rake`コマンドの準備
インストールしただけでは，`rake`コマンドは使えないので，`Rakefile`を作成します．

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

以下のコマンドで使えるようになったか確認します．

```sh:.
$ bundle exec rake -T
...
rake db:create_migration    # Create a migration (parameters: NAME, VERSION)
rake db:migrate             # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)
...
```

上記のコマンドで，使えるコマンド一覧を出力します．上記のように色々出てこれば使えるようになっています．

### マイグレーションファイルの作成
データベースの設計図となるマイグレーションファイルを作成します．

```console:.
$ bundle exec rake db:create_migration NAME=create_messages
db/migrate/20190331052506_create_messages.rb
```

`db/migrate/`下にマイグレーションファイルが作成されます．
今回は，一つ目の例と同じようにメッセージという形で名前とコメントを管理したいので，名前をcreate_messagesとしました．
また，ファイルの先頭の数字は，作成した日時を表しており，被らないようになっています．
このファイルを編集します．

```rb:./db/migrate/20190331052506_create_messages.rb
class CreateMessages < ActiveRecord::Migration[5.2]
    def change
        create_table :messages do |t|
            t.string :name
            t.string :comment
        end
    end
end
```

### マイグレーションファイルからDBを作成
以下のコマンドでデータベースを作成します．

```console:.
$ bundle exec rake db:migrate
== 20190331052506 CreateMessages: migrating ===================================
-- create_table(:messages)
   -> 0.0010s
== 20190331052506 CreateMessages: migrated (0.0011s) ==========================
```

新たに`./db/messages.db`というファイルが作成されます．確認してみます．

```sh:.
$ sqlite3 db/messages.db
SQLite version 3.27.2 2019-02-25 16:06:06
Enter ".help" for usage hints.

sqlite> .database
main: ~/db/messages.db

sqlite> .schema
CREATE TABLE IF NOT EXISTS "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY);
CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime NOT NULL,
"updated_at" datetime NOT NULL);
CREATE TABLE IF NOT EXISTS "messages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar, "comment" varchar);
CREATE TABLE sqlite_sequence(name,seq);

sqlite> insert into messages(name, comment) values('aaa', 'hello');

sqlite> select * from messages;
1|aaa|hello

sqlite> .quit
```

`.schema`と打った時の出力に関しては要勉強．
データベースには，作った時点では，何も入っていないので，sql文でサンプルを一つ入れておきました．

## ActiveRecordでレコードの取り出し
最後に1つ目の例と同じ`app.rb`を実行するとブラウザで確認できます．
`bundle exec ruby app.rb` -> `localhost:4567`
`bundle exec rackup` -> `localhost:9292`
こちらも今回の実装の場合，レコードの追加等はsql文で行うしか方法はありません．

# 最後に
今回は，とりあえずSinatraでデータベースを扱うプログラムを最小限に作成しました．とりあえず動かしたいということで，効率やセキュリティ等は全然考えていません．
データベースを触るのは初めてで，今回の実装では問題があったり，非効率であるかもしれません．
もし何か気になることや参考になるものがあれば，コメントお願いします！

# 参考サイト
- [Sinatra＋ActiveRecord＋SQLite3で，軽量なWeb-DB連携例 &#8211; tamo&#039;s blog](https://tamosblog.wordpress.com/2012/10/26/sinatra/)
- [Sinatra + ActiveRecord + sqlite3覚書 - ataru_kodaka’s diary](http://ataru-kodaka.hatenablog.com/entry/2014/07/22/212526)
- [Sinatra-ActiverecordでDBをマイグレーションする - Qiita](https://qiita.com/myokkie/items/b6b68b247ec7a110a1c4)