Sinatra ＋ ActiveRecord ＝ APIサーバー

# Sinatra + ActiveRecord = APIサーバー

SinatraでActiveRecordを使って簡単なAPIサーバーを作る。
今ではありきたりな組み合わせだが、やってみると案外ハマってしまう箇所もあったのでメモとして残しておく。

## 環境構築

Rubyのinstallは環境ごとに違うので省略

Rubyは入った前提で進める。Rubyのインストールは、rbenv経由で入れると良い。
なお、Rubyのバージョンは2.2.2以上にすること（ActiveRecordのバージョンの都合上、2.2.1以下では上手く動作しなかった。）

ターミナルにて以下のコマンドを入力

```
$ mkdir <project-name>
$ cd <project-name>
$ gem install bundler # gemインストール用
$ rbenv rehash # 上のインストール内容を適用する
```

以下の手順では、上で作成した`<project-name>`のディレクトリ内で作業していく

## 最終的なディレクトリ構造

```
$ tree
.
├── Gemfile
├── Gemfile.lock
├── Rakefile
├── app.rb
├── db
│   ├── migrate
│   │   └── 20160808141848_create_posts.rb
│   └── schema.rb
└── models
    └── post.rb

```

## 手順１）必要なgemをインストール

Gemfileを作成

```Gemfile
source "https://rubygems.org"

gem "rake"
gem "sinatra"
gem "activerecord"
gem "sinatra-activerecord"
gem "sqlite3"
```

ターミナルにて以下のコマンドを入力して必要なgemをインストール

```
$ bundle install
```

## 手順２）ActiveRecordのモデルを作成する

### Rakefileの作成〜DBのマイグレーション
Rakefileを作成する

```Rakefile
require 'sinatra/activerecord'
require 'sinatra/activerecord/rake'
```

これで、rakeコマンドが使用可能になる。

ターミナルにて以下のコマンドを入力
NAME=~~~~の部分には、作成したいテーブル名（複数形）を記入する。

```
$ bundle exec rake db:create_migration NAME=create_posts
db/migrate/20160808141848_create_posts.rb
```

実行すると、`db/migrate/20160808141848_create_posts.rb`みたいなファイルが出来ている。
作成されたファイルを開いて以下のように書き込む

```db/migrate/20160808141848_create_posts.rb
class CreatePosts < ActiveRecord::Migration
  def change
    create_table :posts do |t|
      t.string :title
      t.string :content
      t.timestamps
    end
  end
end
```

`create_table :table_name`と書くことで新しいDBのテーブルを作成する。table_nameには、複数形の単語で書くこと。
また、`t.string :title`のように書くことで、上記のテーブル内にカラムを追加することが出来る。この場合、string型でtitleという名前のカラムを追加することになる。

`models/post.rb`というファイルを作って、以下のように書く。
さっき作ったテーブルが`posts`なので、モデル名は`Post`と単数形になる。

```models/post.rb
ActiveRecord::Base.establish_connection('sqlite3:///model.db')
class Post < ActiveRecord::Base 
end
```

Rakefileも編集しておく。 `models/post.rb` を読み込んで、rakeコマンドでDBと接続できるようにする。

```Rakefile
require 'sinatra/activerecord'
require 'sinatra/activerecord/rake'
require './models/post.rb'
```

これでDBとの接続は完了。
ここまで来たら、ターミナルに戻って以下のコマンドを入力

```
$ bundle exec rake db:migrate
  class CreateUsers < ActiveRecord::Migration[4.2] (called from load at ~/.rbenv/versions/2.3.0/bin/rake:23)
== 20160808141848 CreatePosts: migrating ======================================
-- create_table(:posts, {})
   -> 0.0014s
== 20160808141848 CreatePosts: migrated (0.0015s) =============================
```

なんかうにゃうにゃ出てきたら成功

### Sinatraでアプリを作っていく

`app.rb`というファイルを作って、以下のように書く。
Sinatraの書き方に関しては説明を割愛する。
ポイントは、それぞれの返り値を `.to_json` としてjson形式で終わらせること。

```app.rb
require "sinatra"
require "sinatra/activerecord"
require "./models/post.rb" # さっき作ったモデルを読み込み

get "/posts" do
  Post.all.to_json # 全てのPostを返す
end

get "/posts/:id" do
  Post.find(params[:id]).to_json # :idのPostを返す
end

post "/posts" do
  # sinatraはPOSTメソッドの際デフォルトでjsonを受け取れないので変換
  # 参考: http://qiita.com/izumin5210/items/caf66ece1f67a0fd6a4c
  json = JSON.parse(request.body.read)
  post = Post.new(json)
  if post.save
    { result: "success", code: 200 }.to_json
  else
    { result: "failure" }.to_json
  end
end
```

ここまで書けたら、ターミナルでSinatraサーバーを起動する

```
$ ruby app.rb
```

### 作ったアプリを試してみる

Sinatraのデフォルトポートは4567なので、localhost:4567に対してcurlでアクセスしてみる。

```
$ curl http://localhost:4567/posts
[]

$ curl -XPOST -d '{"title": "dummy title", "content": "dummy content"}' http://localhost:4567/posts
{"result":"success","code":200}

$ curl http://localhost:4567/posts
[{"id":1,"title":"dummy title","content":"dummy content","created_at":"2016-08-08T14:52:07.086Z","updated_at":"2016-08-08T14:52:07.086Z"}]

$ curl http://localhost:4567/posts/1
{"id":1,"title":"dummy title","content":"dummy content","created_at":"2016-08-08T14:52:07.086Z","updated_at":"2016-08-08T14:52:07.086Z"}
```

正常に動作していることを確認。お疲れさまでした。

# 終わりに

今回作成したプロジェクトはこちら。
https://github.com/litmon/sinatra-api

rubyのバージョンが2.2.1以下だと、ActiveRecordのバージョン5.0.0が導入できない。
そのせいで、モデルのデータをDBから引っ張ってくるときにエラーになって何十分かハマってしまった。
ActiveRecordのバージョンをもう少し下げれば上手くいくかもしれないな……要調査。