最近、なにかと話題のElixir。
達人プログラマやプログラミングRubyの著者で有名なDave Thomasさんが書いた
「Programming Elixir」で一気に知名度が上がりました。
Erlangの仮想環境上で動作するとか、並行処理が得意だとか、Ruby風味でとっつきやすいとか etc...
言語の特徴はすでにいろいろなサイト上で紹介されているので、あえてここでは触れません。
じっくり触ってみて、いずれ自分なりの見解を示したいと思っています。
では、アウトプットとしてElixir製のWebFrameworkであるPhoenixを利用して、
RESTFulなWebAPIを実装してみたいと思います。
バージョン
- MacOSX El Capitan(10.11.6)
- VirtualBox(5.1.8)
- Vagrant(1.8.7)
- Ubuntu(16.04 Xenial Xerus)
- PostgreSQL(9.6)
- Erlang/OTP(19.2.3)
- Elixir(1.4.1)
- Phoenix(1.2.1)
1. 仮想環境構築
こちらを参考に環境を作っておいてください。
2. PostgreSQLをインストール
PhoenixはデフォルトのデータストアをPostgreSQLに指定しているので、
こちらを参考にインストールしておいてください。
3. Erlang/OTPをインストール
冒頭でも書いた通り、ElixirはErlangの仮想環境上で動作するので、まずはErlang本体をインストールします。
ちなみにOTPは Open Telecom Platform の略で、Erlangの標準ツール類とライブラリ群の集合体です。
Erlang自体は言語仕様そのもので、OTPが無いと何もできないとか。
ErlangのコンパイラとインタプリタはこのOTPに含まれています。
OTPについてはこちらに詳しく書かれているので一読してみてください。
日本語翻訳はここ
Erlangのdebパッケージをダウンロード
適当なディレクトリにdebパッケージをダウンロードしてください。
$ cd /var/tmp
$ wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
パッケージの取り込み
$ sudo dpkg -i erlang-solutions_1.0_all.deb
パッケージのアップデート
$ sudo apt-get update
Erlangのインストール
$ sudo apt-get install esl-erlang
確認
$ which erl
/usr/bin/erl
4. Elixirをインストール
Elixirのdebパッケージもありますが、今後頻繁にバージョンアップされる事を考慮してexenvでインストールします。
Rubyにはrbenvというバージョン管理ツールがありますが、そのElixir版がexenvになります。
exenvのダウンロード
$ sudo -s
# git clone https://github.com/mururu/exenv.git /usr/local/exenv
# git clone https://github.com/mururu/elixir-build.git /usr/local/exenv/plugins/elixir-build
exenvのパスを設定
どのユーザーでもexenvコマンドが使えるようにします。
$ vim /etc/profile.d/exenv.sh
export EXENV_ROOT="/usr/local/exenv"
export PATH="$EXENV_ROOT/bin:$PATH"
eval "$(exenv init -)"
設定を有効にする
# exec $SHELL -l
インストール可能なElixirのバージョンを表示
# exenv install --list
Elixirのインストール
# exenv install 1.4.1
システム全体で使用するElixirのバージョンを指定する
# exenv global 1.4.1
コマンド群を配置
# exenv rehash
確認
# elixir -v
Erlang/OTP 19 [erts-8.2] [source-fbd2db2] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
Elixir 1.4.1
5. Elixir&Erlang向けパッケージ管理ライブラリとビルドツールのインストール
一旦rootから抜ける
# exit
一般ユーザーでのexenvを有効化
$ exec $SHELL -l
$ which exenv
hexのインストール
$ mix local.hex --force
--force で最新を持ってきます
rebarのインストール
$ mix local.rebar --force
バージョン確認
$ mix hex.info
Hex: 0.15.0
Elixir: 1.4.1
OTP: 19.2.3
Built with: Elixir 1.3.4 and OTP 18.3.4.4
6. Phoenixのインストール
インストール実行
$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez
バージョン確認
$ mix phoenix.new -v
Phoenix v1.2.1
7. プロジェクトの作成
ここまできたらようやくPhoenixフレームワークでAPIを作成する準備が整います。
それではやってみましょう!
適当なプロジェクトディレクトリを作成し移動
$ sudo mkdir -p /var/webapp
$ sudo chown vagrant:vagrant /var/webapp
$ cd /var/webapp
プロジェクトの作成
今回はTODOを管理するAPIを作成したいと思います。
APIなのでレイアウトやフロント周りのビルドツールは必要ありません。
--no-brunchと--no-htmlオプションで除外しておきます。
$ mix phoenix.new todo_api --no-brunch --no-html
作成されたtodo_api配下に移動し中身を確認すると、
下記のような構成になっているはずです。
$ cd todo_api
$ ls -l
drwxrwxr-x 9 vagrant vagrant 4096 Feb 9 23:04 ./
drwxr-xr-x 4 vagrant vagrant 4096 Feb 13 22:52 ../
drwxrwxr-x 4 vagrant vagrant 4096 Feb 9 23:06 _build/
drwxrwxr-x 2 vagrant vagrant 4096 Feb 9 23:06 config/
drwxrwxr-x 19 vagrant vagrant 4096 Feb 9 23:04 deps/
-rw-rw-r-- 1 vagrant vagrant 378 Feb 9 23:04 .gitignore
drwxrwxr-x 3 vagrant vagrant 4096 Feb 9 23:04 lib/
-rw-rw-r-- 1 vagrant vagrant 1549 Feb 9 23:04 mix.exs
-rw-rw-r-- 1 vagrant vagrant 3513 Feb 9 23:04 mix.lock
drwxrwxr-x 5 vagrant vagrant 4096 Feb 9 23:04 priv/
-rw-rw-r-- 1 vagrant vagrant 693 Feb 9 23:04 README.md
drwxrwxr-x 7 vagrant vagrant 4096 Feb 9 23:04 test/
drwxrwxr-x 7 vagrant vagrant 4096 Feb 9 23:21 web/
8. DBのセットアップ
PhoenixはデフォルトのデータストアにPostgreSQLが指定されているので、
事前に作成したPostgresのDB情報をPhoenixのDB設定に反映します。
開発用
$ vim config/dev.exs
# Configure your database
config :todo_api, TodoApi.Repo,
adapter: Ecto.Adapters.Postgres,
username: "作成したユーザー名",
password: "作成したユーザーのパスワード",
database: "todo_api_dev",
hostname: "localhost",
pool_size: 10
テスト用
$ vim config/test.exs
# Configure your database
config :todo_api, TodoApi.Repo,
adapter: Ecto.Adapters.Postgres,
username: "作成したユーザー名",
password: "作成したユーザーのパスワード",
database: "todo_api_test",
hostname: "localhost",
pool_size: 10
下記のコマンドでDBを作成します。
$ mix ecto.create
テストを実行してみます。
$ mix test
問題なくテストが通りました。
Compiling 6 files (.ex)
.............
Finished in 0.9 seconds
13 tests, 0 failures
Randomized with seed 903290
9. APIの作成
Railsにはscaffoldというアプリケーションの基盤を作成するジェネレータがありますが、
Elixirにも同様の機能が存在します。
今回はそれを利用してAPIの基盤を作成してみます。
ちなみにTODOの管理対象は
- TODOの内容
- 完了フラグ
の2つだけを持つとてもシンプルなものにします。
また、レスポンスはJSONで返したいのでphoenix.gen.jsonを指定します。
(その他のジェネレータについては、mix --help | grep phoenix.gen で調べてみてください。)
$ mix phoenix.gen.json Todo todos description:string complete:boolean
ジェネレータを実行すると priv/repo/migrations/ 配下に後述するマイグレーションファイルが作成されます。
その他にもいろいろ作成されていますが説明は省きます。
* creating web/controllers/todo_controller.ex
* creating web/views/todo_view.ex
* creating test/controllers/todo_controller_test.exs
* creating web/views/changeset_view.ex
* creating web/models/todo.ex
* creating test/models/todo_test.exs
* creating priv/repo/migrations/20170214011354_create_todo.exs ← マイグレーションファイル
Add the resource to your api scope in web/router.ex:
resources "/todos", TodoController, except: [:new, :edit]
Remember to update your repository by running migrations:
$ mix ecto.migrate
メッセージの中に「resources "/todos", TodoController, except: [:new, :edit]」の1行を、
ルーターのscopeブロックに追記してくださいと書かれているので追記します。
10. ルーターの編集
$ vim web/router.ex
defmodule TodoApi.Router do
use TodoApi.Web, :router
pipeline :api do
plug :accepts, ["json"]
end
scope "/api", TodoApi do
pipe_through :api
### ここに追記 ###
resources "/todos", TodoController, except: [:new, :edit]
end
end
11. マイグレーションの実行
マイグレーションとは、マイグレーションファイルというテーブル定義書のようなものから、
SQLを書く事なくテーブルを作成したりフィールドやインデックスを追加したり、
作成したものをロールバックさせたりする機能です。
Railsではお決まりの機能ですね。
$ mix ecto.migrate
マイグレーションを実行すると priv/repo/migrations/ 配下にマイグレーションファイルが作成されます。
$ ls -l priv/repo/migrations
-rw-rw-r-- 1 vagrant vagrant 244 Feb 14 10:13 20170214011354_create_todo.exs
12. APIの動作確認
ここまでできたらサーバーを起動し、アクセスしてみましょう。
サーバーの起動
下記のコマンドを実行するとErlang製のWebサーバー(Cowboy)が起動します。
$ mix phoenix.server
[info] Running TodoApi.Endpoint with Cowboy using http://localhost:4000
アクセスして確認
他のマシーンからアクセスしてレスポンスが返ってくれば成功です。
$ curl -X GET "http://192.168.xxx.xxx:4000/api/todos"
{"data":[]}
まだ、データが何も登録されていなので空のレスポンスが返ってきます。
Cowboyのログ
[info] GET /api/todos
[debug] Processing by TodoApi.TodoController.index/2
Parameters: %{}
Pipelines: [:api]
[debug] QUERY OK source="todos" db=1.1ms
SELECT t0."id", t0."description", t0."complete", t0."inserted_at", t0."updated_at" FROM "todos" AS t0 []
[info] Sent 200 in 99ms
ためしに何かTODOをPOSTしてみます。
$ curl "http://192.168.xxx.xxx:4000/api/todos" -X POST -d "todo[description]=掃除をする" -d "todo[complete]=false"
{"data":{"id":1,"description":"掃除をする","complete":false}}
$ curl "http://192.168.xxx.xxx:4000/api/todos" -X POST -d "todo[description]=買い物に行く" -d "todo[complete]=true"
{"data":{"id":2,"description":"買い物に行く","complete":true}}
$ curl "http://192.168.xxx.xxx:4000/api/todos" -X POST -d "todo[description]=勉強する" -d "todo[complete]=false"
{"data":{"id":2,"description":"勉強する","complete":false}}
再度GETしてみます
curl -X GET "http://localhost:4000/api/todos"
{"data":[{"id":1,"description":"掃除をする","complete":false},{"id":2,"description":"買い物に行く","complete":true},{"id":3,"description":"勉強する","complete":false}]}
問題なく投稿したTODOが取得できました!
PATCHやDELETEもできそうです。
詳しくは web/controllers/todo_controller.ex を覗いてみてください。
13. おわりに
環境のセットアップが少々煩雑な感じを受けますが、
普段からRailsを触っている人にとってはほぼ迷いなく導入できると思います。
また、今回はscaffoldで手っ取り早く作成したので、
Phoenix(Elixir)のコードには一切触れていません。
なので、今後は自動で生成されたコード類を読みながら、
より理解を深めていきたいと思っています。