Elixir+Phoenixに触ってみる（デモ作成編1）

# 目的
簡単なデモアプリケーションを作成して、phoenixの簡単な使い方を確認する。
なお、この記事は前回作成した環境( http://qiita.com/ak-ymst/items/12153c854965ad28fc20 )を元に作成しています。

# 作成するアプリケーションの概要
Phoenix用の程よいチュートリアルが見つからなかったので、ベタですが、記事の投稿と
記事に対するコメントを行えるだけの簡単なBlogアプリを作ってみたいと思います。
必要な機能は
- 記事一覧表示機能
- 記事詳細及びコメント一覧表示機能
- 記事投稿機能
- 記事修正機能
- コメント投稿機能

# DBの準備
今回作成するアプリ用のDBを作る。
config/dev.exsに保存された開発環境用DBの接続情報を見ると

    # Configure your database
    config :phoenix_sample, PhoenixSample.Repo,
      adapter: Ecto.Adapters.MySQL,
      username: "root",
      password: "",
      database: "phoenix_sample_dev",
      hostname: "localhost",
      pool_size: 10

となっている。
config.secret.exsを見ると、本番環境用のDB名はphoenix_sample_prodとなっていたので、
どうやら、デフォルトのDB名は
プロジェクト作成時に指定したプロジェクト名+'_'+環境
という形で設定される模様。
DBはそのままにするとして、ユーザとしてパスワード無しのRootを使うのも気が引けるので
先にユーザだけ作成しておく。
SQLファイルの置き場所はよくわからないが、Migration関係のファイルはpriv/repo/migrations
に配置することになっているようなので、適当にpriv以下においておく。

priv/sql/create_user.sql

    CREATE USER phoenix@localhost IDENTIFIED BY 'phoenix';
    GRANT ALL PRIVILEGES ON `phoenix_sample%`.* TO 'phoenix'@'localhost' WITH GRANT OPTION;

config/dev.exsの接続ユーザを新たに作成したユーザに変更後、DB作成を実行。

    $ mix ecto.create
    ...
    ...
    $ mysql -u phoenix -p
    Enter password:

    MariaDB [(none)]> show databases;
    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | phoenix_sample_dev |
    | test               |
    +--------------------+
    3 rows in set (0.00 sec)

Dev環境のDBしか作成されずProd環境のDBは作成されない理由は不明ですが
おそらくはコマンドのオプションとして環境を指定してやる必要があるのでしょう。

# 記事（Post）の閲覧/投稿機能
## モデル等の生成
記事用のテーブル、モデル、ビューなどを作成する。
テーブルはこんな感じのものを作りたい。

    posts   
        id: int,
        title: string(255),
        content: text,
        inserted_at: datetime,
        updated_at: datetime

phoenix.gen.htmlコマンドを使って色々と一括で作成する。

    $ mix phoenix.gen.html Post posts title:string content:string
    * creating web/controllers/post_controller.ex
    * creating web/templates/post/edit.html.eex
    * creating web/templates/post/form.html.eex
    * creating web/templates/post/index.html.eex
    * creating web/templates/post/new.html.eex
    * creating web/templates/post/show.html.eex
    * creating web/views/post_view.ex
    * creating test/controllers/post_controller_test.exs
    * creating priv/repo/migrations/20160208022404_create_post.exs
    * creating web/models/post.ex
    * creating test/models/post_test.exs

    Add the resource to your browser scope in web/router.ex:

        resources "/posts", PostController

    Remember to update your repository by running migrations:

テーブルのID及び作成日時、更新日時は自動で生成されるので、オプションで指定するのはそれ以外の要素のみ。
↓のようなmigrationファイルが生成されるので、

priv/repo/migrations/20160208022404_create_post.exs

    defmodule PhoenixSample.Repo.Migrations.CreatePost do
      use Ecto.Migration

      def change do
        create table(:posts) do
          add :title, :string
          add :content, :string

          timestamps
        end

      end
    end

そのままmigration実行する。
すると、こんなエラーが。

    $ mix phoenix.migrate
    Compiled web/models/post.ex

    == Compilation error on file web/controllers/post_controller.ex ==
    ** (CompileError) web/controllers/post_controller.ex:25: undefined function post_path/2
        (stdlib) lists.erl:1337: :lists.foreach/2
        (stdlib) erl_eval.erl:669: :erl_eval.do_apply/6
        (elixir) lib/kernel/parallel_compiler.ex:100: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/8

調べたところ、この未定義の関数だと怒られているpost_pathというのは、自動生成されるPostモデル関係のURLを特定するための関数らしく、有効にするには先にルーティングにリソースを追加してやる必要がある、とのこと。
さっそく, web/routes.exsの16行目付近を以下のように変更

    scope "/", PhoenixSample do
      pipe_through :browser # Use the default browser stack

      get "/", PageController, :index
      resources "/posts", PostController # add post routes
    end

再度migrationを実行すると。（ちなみに、上の方のphoenix.migrateというコマンドはまちがいでした）

    $ mix ecto.migrate
    Compiled web/router.ex
    Compiled web/views/post_view.ex
    Compiled web/views/page_view.ex
    Compiled lib/phoenix_sample/endpoint.ex
    Compiled web/views/layout_view.ex
    Compiled web/views/error_view.ex
    Compiled web/controllers/post_controller.ex
    Compiled web/controllers/page_controller.ex

    11:48:02.638 [info]  == Running  PhoenixSample.Repo.Migrations.CreatePost.change/0 forward

    11:48:02.638 [info]  create table posts

    11:48:02.648 [info]  == Migrated in 0.0s

DBの内容を見てみると。

    $ mysql -u phoenix -p phoenix_sample_dev
    Enter password:

    MariaDB [phoenix_sample_dev]> show tables;
    +------------------------------+
    | Tables_in_phoenix_sample_dev |
    +------------------------------+
    | posts                        |
    | schema_migrations            |
    +------------------------------+
    2 rows in set (0.00 sec)

    MariaDB [phoenix_sample_dev]> show columns from posts; 
    +-------------+---------------------+------+-----+---------+----------------+
    | Field       | Type                | Null | Key | Default | Extra          |
    +-------------+---------------------+------+-----+---------+----------------+
    | id          | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
    | title       | varchar(255)        | YES  |     | NULL    |                |
    | content     | varchar(255)        | YES  |     | NULL    |                |
    | inserted_at | datetime            | NO   |     | NULL    |                |
    | updated_at  | datetime            | NO   |     | NULL    |                |
    +-------------+---------------------+------+-----+---------+----------------+
    5 rows in set (0.00 sec)

postsテーブルと、マイグレーション記録用のテーブルが作成されていることが確認されます。

## 画面の確認
上の方でルーティングルールに記事リソースを追加したので、すでに記事のCRUDに対するルートが割り当てられています。
各ルートの確認は、やはりmixコマンドを使っておこなえます。

    $ mix phoenix.routes
    page_path  GET     /                PhoenixSample.PageController :index
    post_path  GET     /posts           PhoenixSample.PostController :index
    post_path  GET     /posts/:id/edit  PhoenixSample.PostController :edit
    post_path  GET     /posts/new       PhoenixSample.PostController :new
    post_path  GET     /posts/:id       PhoenixSample.PostController :show
    post_path  POST    /posts           PhoenixSample.PostController :create
    post_path  PATCH   /posts/:id       PhoenixSample.PostController :update
               PUT     /posts/:id       PhoenixSample.PostController :update
    post_path  DELETE  /posts/:id       PhoenixSample.PostController :delete

ここで目を引くのは、記事更新に対するルート。
記事更新フォーム画面のルートが

    post_path  GET     /posts/:id/edit  PhoenixSample.PostController :edit

となっているのに対して、更新実行のルートは

    post_path  PATCH   /posts/:id       PhoenixSample.PostController :update

となっている点。
この辺りに、RESTに対する強い意識が感じられます。

それはさておき、サーバを立ち上げて
http://192.168.33.100:4000/posts
にブラウザでアクセスしてみると![スクリーンショット 2016-02-08 12.23.10.png](https://qiita-image-store.s3.amazonaws.com/0/111295/80ad79bc-8d5f-d61d-35b2-a0da03b41329.png)

こんな記事一覧画面が表示されることが確認できると思います。
あとは、記事リスト（空ですが）の下にある「NewPost」から記事の投稿をしたり、表示したり、修正したりすることができることが確認できると思います。

## Viewを修正する
このままでも別に構わないのですが、Viewを変更してみます。
記事リソースの各Viewに対応したテンプレートは
web/templates/post
以下にあるので、web/templates/post/index.eex.htmlをエディタで開き、
title -> 題名
contents -> 本文
と日本語表記に変えます。

![スクリーンショット 2016-02-08 12.34.12.png](https://qiita-image-store.s3.amazonaws.com/0/111295/e8075022-23b7-f046-e808-1bc3061e86f6.png)

このように、表示が変更されていることが確認できると思います。
ファイルのジェネレート時にコンパイルという文字列が飛び交っていたので
テンプレート変更後もコンパイルやサーバのリロード等が必要かと思ったのですが、特にそういった作業は必要ありませんでした。

Phoenixでは、eexというRailsのerb風の文法を持ったテンプレートエンジンを使っているようですが、詳しい使い方はまたおいおい調べて行きます。


ここまでで、記事投稿関係の機能は終了。
少し長くなってしまったので、コメント機能は次回に持ち越します。