PhoenixでYamlで定義したシードデータを投入する

# Phoenixでのシードデータ投入
phoenixでは、

``` priv/repo/seeds.exs
%App.SomeModel{}
|> App.Repo.insert!
```

的なelixirコードを記述して `mix run priv/repos/seed.exs` と実行してやれば、シードデータ投入が出来るみたいです。

`mix ecto.reset`を実行すると、drop→create→migrate→seed投入、の順番で実行します。開発の初期段階だと便利ですね。

しかし、まぁ投入データをEctoの記法でいちいち記述するのも面倒臭いので、YAMLで書けた方が便利（？）と思ったのでまー試しにやって見ます。

# 必要なモジュール
``` mix.exs
{:yaml_elixir, "~> 1.1"},
```

``` sh
$ mix deps.get
```

# データベース設定
MySQL 使ってます。

``` config/dev.exs
config :app, App.Repo,
  adapter: Ecto.Adapters.MySQL,
  username: "root",
  password: "",
  database: "app_dev",
  hostname: "mysql",
  pool_size: 10
```

# モデル
AreaモデルとPrefectureモデルを想定します。
Area : Prefecture = 1 : n の関係にあります。
Prefectureが、Areaのcodeをarea_codeとして持っている形です。

``` web/models/area.ex
defmodule App.Area do
  use Ecto.Schema
  alias App.Prefecture

  @primary_key {:code, :string, autogenerate: false}
  schema "areas" do
    field :name, :string

    has_many  :prefectures, Prefecture, 
              references: :code,
              foreign_key: :area_code
  end
end
```

``` web/models/prefecture.ex
defmodule App.Prefecture do
  use Ecto.Schema
  alias App.Area

  @primary_key {:code, :string, autogenerate: false}
  schema "prefectures" do
    field :name, :string

    belongs_to :area, Area, 
              references: :code,
              foreign_key: :area_code,
              type: :string
  end
end
```

# Fixtureモジュール定義
yamlを読み込んでデータ投入するためのモジュールを作成します。
別のプロジェクトでも使えるようにする為に、App.Repoの関数呼んでるところをもうちょっと抽象化したい感じがします。

``` web/models/concerns/fixture.ex
defmodule App.Model.Fixture do
  alias App.Model.Fixture

  def create(model) do
    struct(model)
  end

  def create(model, params) do
    struct(model, params)
  end

  def primary_key(model, params) do
    create(model, params)
    |> Ecto.Changeset.cast(params, Map.keys( params ))
    |> Ecto.Changeset.apply_changes
    |> Ecto.primary_key
  end

  def find(model, primary_key) do
    App.Repo.get_by model, primary_key
  end
  
  def insert(file_name, model) do
    Fixture.get_params_list_from(file_name)
    |> Fixture.insert_list(model)
  end

  def insert_or_update(file_name, model) do
    Fixture.get_params_list_from(file_name)
    |> Fixture.insert_or_update_list(model)
  end

  def get_params_list_from(file_path) do
    File.cwd!
    |> Path.join(file_path)
    |> YamlElixir.read_from_file
  end

  def insert_list(params_list, model) do
    for params <- params_list do
      model.change(params)
      |> App.Repo.insert!  
    end
  end

  def insert_or_update_list(params_list, model) do
    for params <- params_list do
      object = find(model, primary_key(model, params) ) || create(model)
      object
      |> Ecto.Changeset.cast(params, Map.keys( params ))
      |> App.Repo.insert_or_update!
    end
  end
end
```

# 定義ファイル
``` priv/repo/fixtures/areas.yml
- code: '01'
  name: 北海道・東北
- code: '02'
  name: 関東
- code: '03'
  name: 甲信越・北陸
- code: '04'
  name: 東海
- code: '05'
  name: 関西
- code: '06'
  name: 中国
- code: '07'
  name: 四国
- code: '08'
  name: 九州・沖縄
```

``` priv/repo/fixtures/prefectures.yml
- code: '01'
  name: '北海道'
  area_code: '01'
- code: '02'
  name: '青森県'
  area_code: '01'
- code: '03'
  name: '岩手県'
  area_code: '01'
- code: '04'
  name: '宮城県'
  area_code: '01'
- code: '05'
  name: '秋田県'
  area_code: '01'
- code: '06'
  name: '山形県'
  area_code: '01'
- code: '07'
  name: '福島県'
  area_code: '01'
- code: '08'
  name: '茨城県'
  area_code: '02'
- code: '09'
  name: '栃木県'
  area_code: '02'
- code: '10'
  name: '群馬県'
  area_code: '02'
- code: '11'
  name: '埼玉県'
  area_code: '02'
- code: '12'
  name: '千葉県'
  area_code: '02'
- code: '13'
  name: '東京都'
  area_code: '02'
- code: '14'
  name: '神奈川県'
  area_code: '02'
- ...
- ...
-

```

# 実行ファイル
``` priv/repo/seeds.exs
alias App.Model.Fixture

Fixture.insert_or_update("priv/repo/fixtures/areas.yml", App.Area)
Fixture.insert_or_update("priv/repo/fixtures/prefectures.yml", App.Prefecture)
```

いちいち指定するのが面倒であれば、priv/repo/fixtures以下のymlを全部見るようにしても良さそう。
Fixtureの関数として追加してもいい気がする。

# 実行
``` sh
$ mix run priv/repo/seeds.exs
```

Ruby/Railsは4〜5年使っていますが、Elixir/Phoenixは始めて10日程度の素人です。
もうちょっといい方法や書き方とかがあったら教えていただけると幸いです。