[Elixir] Phoenix+MySQL+gulpでアプリケーションを作る

# 動機

Phoenixはデフォルトのままで高い生産性のアプリケーション開発ができる。
が、人間どうしたって慣れ親しんだ道具が使いたいもの。
そこで、標準のPostgreSQLとBrunch（ビルドツール）でなく、MySQLとgulpを使って以下の様な環境を作ることにした。

|            | Version     |
|:-----------|:------------|
| Elixir     | 1.1.1       |
| Phoenix    | 1.0.3       |
| MySQL      | 5.6.26      |
| gulp       | 3.9.0       |

# やること

1. （省略）Elixir、Phoenix、MySQLのインストール
  - [Installing Elixir](http://elixir-lang.org/install.html)
  - [Installation · Phoenix](http://www.phoenixframework.org/docs/installation)
  - MySQLはbrewから入れた
1. デモアプリケーションを作る
1. MySQLの設定
1. gulpの設定
1. ビルド実行コマンドを指定する

## デモアプリケーションを作る

下記コマンドを実行して、brunchがなく、DBがMySQLになったPhoenixアプリケーションを作成する。

```zsh
mix phoenix.new demo --no-brunch --database mysql
```

アプリケーションが出来上がると、`mix ecto.create`と`mix phoenix.server`を実行してみろと言ってくるが、現時点では設定不足で動かないので無視。

## MySQLの設定

`--database mysql`を指定した時点でconfigや依存関係はMySQL用に差し替わっているので、接続先だけ設定してやる。`config/dev.exs`を開き、末尾にある以下の接続設定を自身の環境に合わせて変更する。

```config/dev.exs
# Configure your database
config :demo, Demo.Repo,
  adapter: Ecto.Adapters.MySQL,
  username: "root",
  password: "",
  database: "demo_dev",
  hostname: "localhost",
  pool_size: 10
```

設定が終わったら下記コマンドを実行。

```zsh
mix ecto.create
```

## gulpの設定

ここからが少し大変。まずはgulpをインストールする。Phoenixからもgulpを実行させる必要があるので、global/local両方にインストールしておく。

### gulp関連モジュールをインストール

今回は、babelify+browserifyでのES6コンパイルに加え、.scssのコンパイルや、browser-syncの導入など、Phoenixが**brunchを使ってやれることすべてをgulpで代替することを目標に、一通りの設定を行う。

```zsh
npm install -g gulp
npm install --save-dev babelify browserify gulp gulp-autoprefixer gulp-dir-sync gulp-minify-css gulp-rename gulp-sass gulp-sourcemaps gulp-uglify gulp-watch vinyl-buffer vinyl-source-stream
```

### 静的ファイルのディレクトリ構成

Phoenixに関係するstaticファイルは以下の通り。`web/static`にはビルド前のソースファイルを置き、`priv/static`にはビルド済みのファイルを置く。外部から参照出来るのは、`priv/staic`にあるファイルのみ。

```zsh
├── priv
│   └── static
│       ├── css
│       ├── fonts
│       ├── images
│       └── js
└── web
    └── static
        ├── assets
        │   ├── fonts
        │   └── images
        ├── css
        └── js
```

### gulpfileを書く

JavaScript、CSSはbrowserifyやbabelでコンパイルし、assets以下の画像やフォントなどは、そのまま`priv/static`へコピーする。

```gulpfile.js
var babelify = require('babelify')
  , browserify = require('browserify')
  , gulp = require('gulp')
  , autoprefixer = require('gulp-autoprefixer')
  , dirSync = require('gulp-dir-sync')
  , minifyCss = require('gulp-minify-css')
  , rename = require('gulp-rename')
  , sass = require('gulp-sass')
  , sourcemaps = require('gulp-sourcemaps')
  , uglify = require('gulp-uglify')
  , watch = require('gulp-watch')
  , buffer = require('vinyl-buffer')
  , source = require("vinyl-source-stream");

gulp.task('build-js', function() {
  browserify({ entries: 'web/static/js/app.js', extensions: ['.js'] })
    .transform(babelify)
    .bundle()
    .on('error', function (error) {
      console.error(error.stack);
    })
    .pipe(source('app.js'))
    .pipe(buffer())
    .pipe(sourcemaps.init({loadMaps: true}))
    .pipe(uglify())
    .pipe(sourcemaps.write('./'))
    .pipe(gulp.dest('priv/static/js'));
});

gulp.task('build-style', function () {
  gulp.src('web/static/css/app.scss')
    .pipe(sourcemaps.init())
    .pipe(sass().on('error', sass.logError))
    .pipe(autoprefixer())
    .pipe(minifyCss({ advanced:false }))
    .pipe(sourcemaps.write('./'))
    .pipe(rename("app.css"))
    .pipe(gulp.dest('priv/static/css'));
});

gulp.task('watch-js', function() {
  watch([
    'node_modules/**/*.*',
    'bower_components/**/*.*',
    'web/static/js/**/*.*'
  ], function () {
    gulp.start('build-js');
  });
});

gulp.task('watch-style', function () {
  watch([
    'bower_components/**/*.*',
    'web/static/css/**/*.*'
  ], function () {
    gulp.start('build-style');
  });
});

gulp.task('sync-assets', function() {
  dirSync('web/static/assets', 'priv/static');
});

gulp.task('default', [
  'build-js',
  'build-style',
  'watch-js',
  'watch-style',
  'sync-assets'
]);
```

### ビルド確認

下記コマンドを実行し、`priv/static`以下にファイルが置かれているか確認。

```zsh
mkdir -p web/static/js
mkdir -p web/static/css
cp priv/static/js/app.js web/static/js/app.js
cp priv/static/css/app.css web/static/css/app.scss
gulp
```

## ビルド実行コマンドを指定する

アセットのビルドコマンドは`config/dev.exs`に指定する

```config/dev.exs
# gulpを指定する
watchers: [node: ["node_modules/gulp/bin/gulp.js"]]
```

## サーバー起動

```zsh
iex -S mix phoenix.server
```

`http://localhost:4000/`を確認し、Welcome to Phoenix!などと出ていればOK。

# おわり

RDBMSの変更はとても簡単にできる一方で、タスクランナーの変更はちょっと面倒だった。

brunchはビルドが早くて良いのだが、情報が少なくて泣きそうになる時がある。その分gulpは情報収集におけるアドバンテージがある。また、gulpはJSで細かい部分まで記述できる点も魅力だ。

# 関連記事

[PhoenixでJSONを返すWeb APIを作る](http://qiita.com/FL4TLiN3/items/41ca80cdbfca1956ed78)

# 参考

- [Static Assets · Phoenix](http://www.phoenixframework.org/docs/static-assets)
- [Using MySQL · Phoenix](http://www.phoenixframework.org/docs/using-mysql)