Ruby on RailsのAPIサーバーのスタブを手動で作成する

# 背景
仕様検討中は [Swaggerを使ってAPI仕様を共有する - Qiita](https://qiita.com/ledsun/items/12e7e7c28dbd757e52a5) に見るように、SwaggerなどAPI仕様からソースコードを生成するコードジェネレーターを使って、柔軟にAPIのインタフェースを変更するのが便利です。

仕様が固まって開発に入る際に、Swaggerの生成したRailsのソースコードを流用しようとすると、次のような問題が出てきます。

- Railsのバージョンが5.0固定
- 含まれるモジュール構成（sprocketsなど）が固定
- DBがMySQL固定

実際に作りたいRailsアプリケーションは様々な構成になると思います。
そこで、API仕様に合わせたスタブサーバーを手動で作る方法をここにまとめます。

# アプリケーションの要件

- Rails 5.1.5
- DBにPostgreSQLを使う
- 画面のない純粋なAPIサーバー

# 作成

注意

1. モデルやDBを扱わない、APIのみのスタブサーバーを目指します
2. fish-shellを前提としています。bash等の場合は`(pwd)`を`$(pwd)`に置き換えてください

## 開発環境のファイルを揃える

### Gemfile

```rb
source 'https://rubygems.org'

gem 'rails', '~> 5.1.5'
```

ここで好みのRailsのバージョンを指定します。

### Dockerfile

```yaml
FROM ruby:2.5-alpine3.7

ENV BUILD_PACKAGES="curl-dev ruby-dev build-base bash" \
    DEV_PACKAGES="zlib-dev libxml2-dev libxslt-dev tzdata yaml-dev postgresql-dev" \
    RUBY_PACKAGES="ruby-json yaml"

# Update and install base packages and nokogiri gem that requires a
# native compilation
RUN apk update && \
    apk upgrade && \
    apk add --no-cache --update\
    $BUILD_PACKAGES \
    $DEV_PACKAGES \
    $RUBY_PACKAGES && \
    mkdir -p /myapp

# Copy the app into the working directory. This assumes your Gemfile
# is in the root directory and includes your version of Rails that you
# want to run.
WORKDIR /myapp
COPY Gemfile /myapp
#COPY Gemfile.lock /myapp

RUN gem install bundler
RUN bundle config build.nokogiri --use-system-libraries && \
    bundle install --jobs=4 --retry=10 --clean
```

使いたいデータベースに合わせて、`postgresql-dev`を`sqlite-dev`（SQLite3）や`mariadb-dev`（MySQL）に変更します。


## Dockerイメージをビルド
Dockerイメージをビルド

```sh
docker build -t temp .
```

## Rails new

Railsアプリケーションの雛形を作成

```sh
docker run --rm -it -v (pwd):/myapp temp rails new . -MCBSJT --skip-yarn --database=postgresql --api
```

|オプション|意味|
|---|---|
|--database=postgresql|Preconfigure for selected database|
|--skip-yarn|Don't use Yarn for managing JavaScript dependencies|
|M|Skip Action Mailer files|
|C|Skip Action Cable files|
|B|Don't run bundle install|
|S|Skip Sprockets files|
|J|Skip JavaScript files|
|T|Skip test files|
|-api|Preconfigure smaller stack for API only apps|

使いたい機能にあわせてオプションを調整します。

#### オプションを知りたい時
`rails new --help`で確認できます。
今回の環境では次のコマンドを使います。

```sh
docker run --rm -it -v (pwd):/myapp temp rails new --help
```


#### やり直したい時

オプション指定を変更して作り直したい時があります。

> Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.
Type 'rails' for help.

とエラーが出ます。
`bin`ディレクトリの有無を見ています。削除すれば、再実行できます。

## GemをDockerイメージにインストール
必要なGemをDockerイメージにインストールするために、Dockerイメージをビルドしなおします。

```sh
docker build -t temp .
```


## 起動確認

```sh
docker run --rm -it -v (pwd):/myapp -p 80:3000 temp rails s -b 0.0.0.0
```
http://localhost でRailsの初期画面が開きます。

![スクリーンショット 2018-03-16 15.04.53.png](https://qiita-image-store.s3.amazonaws.com/0/15964/8815c225-58f8-4191-de04-756c1bca28b2.png)

## コントローラーを実装
ユーザー一覧APIを作って見ましょう

### コントローラーを生成
`scaffold_controller`を使うと、モデルを作ってしまうので、`controller`を指定します。

```
docker run --rm -it -v (pwd):/myapp temp bin/rails generate controller api/v1/users index
```

`users`が複数形であることに気をつけてください。

### ルーティングの変更

`config/routes.rb`を更新します。

```rb
namespace :api do
  namespace :v1 do
    get 'users/index'
  end
end
```

と不思議なルーティングが生成されます。

```routes.rb
namespace :api do
  namespace :v1 do
    get 'users', to: 'users#index'
  end
end
```
に修正します。

メソッド名（get, post）やメソッド名(create, update)は適宜変更します。

### コントローラーの実装

生成した`app/controllers/api/v1/users_controller.rb`に仮データを返す実装をします。

```rb
def index
  render json: [{
    name: '太郎',
    age: 21
  }, {
    name: '花子',
    age: 29
  } ]
end
```

## 動作確認
curlコマンドを使って動作確認します。

```
curl 'http://localhost/api/v1/users'
```

## 続けてコントローラーを作る

あとは、事前に決めたAPI仕様にしたがって機械的にAPIを実装していきます。

続いて複数のコントローラーを作るときは

```sh
docker run --rm -it -v (pwd):/myapp -p 80:3000 temp bash
```

でdockerコンテナ上でbashを起動し

```
bin/rails generate controller Users show
```

railsコマンドを直接実行すると、毎回Dockerコンテナを起動し直さなくて良いので、効率が良いです。

# まとめ

Swagger CodegenでAPIサーバーのスタブを生成できます。
しかし、実際の開発環境とはギャップがあります。
ギャップを柔軟に埋めるには腕力を使いましょう。

## 課題
生成しているのは、APIごとに

1. *_controller.rb
2. routes.rb

の2ファイルです。Railsのジェネレーターを使わずに、swagger.ymlをパースしAPI定義を読み取って、スクリプトで生成するのもそれほど難しくなさそうです。