Ecto 2.0.0-betaを試す (1)
はい。
Ectoはしばらく触っていなかったのですが、v2.0がもうすぐということで触ってみました第1弾。
まずはプロジェクトをセットアップして単純なinsert
のテストを通すところまで。
TL;DR
こちらに今日時点のコードがあるので、細かいとこは飛ばしていきます。
プロジェクト作成
mix new play_ecto --sup
依存性はこんな。2.0.0-beta
を試すと書きましたが、mix ecto.create
やらmix ecto.drop
が動きませんでした。すると最近のコミットで直っていそうだったので、じゃあHEAD使おうということにしました。
defp deps do [ {:ecto, github: "elixir-lang/ecto", ref: "dc2d2455ded89683e383736358727b0d89e2c9e6"}, {:postgrex, ">= 0.0.0"} ] end
migration作成
mix ecto.gen.migration add_users_table
defmodule PlayEcto.Repo.Migrations.AddUsersTable do use Ecto.Migration def change do create table(:users) do add :name, :string add :password_hash, :string timestamps end create index(:users, [:name]) end end
mix ecto.migrate
この辺は以前と変わってないかな。
User
モジュール
defmodule PlayEcto.User do use Ecto.Schema import Ecto.Changeset schema("users") do field :name, :string field :password, :string, virtual: true field :password_hash, :string timestamps end @allowed ~w[name password] def changeset(model, params) do model |> cast(params, @allowed) |> hash_password |> validate_required(:name) |> validate_required(:password_hash) end defp hash_password(changeset) do case get_change(changeset, :password) do nil -> changeset password -> put_change(changeset, :password_hash, hashing(password)) end end defp hashing(_password), do: "hogehogefugafuga" end
私がちょっと触ってたときとは結構変わっているようです。 以前はcast/4で、第3、第4引数にそれぞれ必須フィールドと任意フィールドの名前をリストで与えていましたが、deprecatedになったようです。代わりにcast/3を使い、第3引数には許可するパラメータ名をリストで与えます。
テスト
以下のテストをGreenにしましょう。
defmodule PlayEctoTest do use ExUnit.Case alias PlayEcto.{User, Repo} test "User operation" do changeset = User.changeset(%User{}, %{name: "タナカ", password: "tanaka1234"}) Repo.insert!(changeset) user = Repo.get_by(User, name: "タナカ") assert user.name == "タナカ" assert user.password |> is_nil assert user.password_hash |> is_binary end end
まずDBが無いと怒られるので、test_helper.exs
にこんな2行を追加します。これはphoenixのプロジェクトから持ってきました。初回のmix test
はコケて、2回目からはテストが走るようになりました。なんででしょ。
Mix.Task.run "ecto.create", ~w(-r PlayEcto.Repo --quiet) Mix.Task.run "ecto.migrate", ~w(-r PlayEcto.Repo --quiet)
Ecto.Adapters.SQL.begin_test_transaction(PlayEcto.Repo)
という1行も入れてみたのですが、そんな関数は無いと言われたので消しました。
このままでは、テスト中にinsertした行がテスト後も残ってしまうのでは、と思ったらやっぱり残りました。
さてどうしたものかと思い、本家はどうやってテストしてるのかな〜と見てみると怪しいコードが。
ecto/test_helper.exs at dc2d2455ded89683e383736358727b0d89e2c9e6 · elixir-lang/ecto · GitHub
最初から読めという感じですが、Sandbox
のドキュメントに色々と書いてありました。test_helper.exs
の最後には以下の行を書くべきだそうです。
Ecto.Adapters.SQL.Sandbox.mode(PlayEcto.Repo, :manual)
ドキュメントによると、オーナーシップの考え方が導入されたそうです。checkout
するとプールされたコネクションが貰えてトランザクションが走ります。このコネクションはcheckout
したプロセスしか明示的に許可しない限り使えません。で、なにが変わったのかというと、これまではDBの読み書きを含むようなテストをするときはasync: false
を付けてシーケンシャルに行わなければならなかったのですが、それらが並行にテストできるようになったのでした。すごい。
次はリレーション辺りを攻めようかな。