どうも、靖宗です。
引き続きMix Tasksの項目を見ていきます。
今回はEcto関連のコマンドから。
Ecto Specific Mix Tasks
このEcto周辺のコマンドですが、当たり前ですが--no-ecto
とかしてない場合が対象です。
mix ecto.create
データベースの作成を行うコマンドです。デフォルトならconfigやlib/hello/repo.ex
に記載したデータベースに作成するはずです。
デフォルトでいいなら引数は要らないのでシンプルです。
$ mix ecto.create The database for Hello.Repo has been created.
別の名前で定義してたりするなら、-r
オプションでモジュール名を指定します。
$ mix ecto.create -r OurCustom.Repo The database for OurCustom.Repo has been created.
PostgreSQLのユーザー周りの説明は割愛します。
(要望があれば書きますが)
ecto.drop
純粋にecto.create
の逆だと思えば良さそうで、指定したデータベースからデータを消去します。
使い方もほぼcreate
と同じで、デフォルトなら引数は不要。
$ mix ecto.drop The database for Hello.Repo has been dropped.
-r
でモジュール名を指定することも可能。
$ mix ecto.drop -r OurCustom.Repo The database for OurCustom.Repo has been dropped.
mix ecto.gen.repo
データストアが1個じゃないケースもあると思います。そういうときにこのecto.gen.repo
で新しいリポジトリを作成できるそうです。
たとえば、デフォルトのリポジトリがHello.Repo
で、別のリポジトリがOurCustom.Repo
であるばあい、下記のようにして作成するようです。
$ mix ecto.gen.repo -r OurCustom.Repo * creating lib/our_custom * creating lib/our_custom/repo.ex * updating config/config.exs Don't forget to add your new repo to your supervision tree (typically in lib/hello.ex): worker(OurCustom.Repo, [])
* updating config/config.exs
とあるので、configも修正されています。
... config :hello, OurCustom.Repo, database: "hello_repo", username: "user", password: "pass", hostname: "localhost" ...
これは各環境で合わせて下さい。
場合によってはdev.exs
やprod.exs
に記載してもいいかもしれません。
ファイルの生成などは自動でやってくれましたが、リポジトリのワーカーをSupervisorで管理する必要があります。
ドキュメントではlib/hello.ex
を編集、と書いてありますがたぶんlib/hello/application.ex
の間違いです。
... children = [ # Start the Ecto repository Hello.Repo, # Start the endpoint when the application starts HelloWeb.Endpoint, # Starts a worker by calling: Hello.Worker.start_link(arg) # {Hello.Worker, arg}, # Here you could define other workers and supervisors as children OurCustom.Repo ] ...
これで大丈夫そうです。
mix ecto.gen.migration
マイグレーションする準備をするのがecto.gen.migration
です。こういうフレームワークにはよくある奴だと思います。
Contextの章でもいじったファイルを作成するやつで、マイグレーションも実際の所はElixirのスクリプトを実行して行っていますのでそのスクリプトを生成するコマンドということです。
基本的に何かのコマンドで自動生成されるのでそこまで出番があるように思えませんが、スキームの変更などをした場合には手動で作成する必要がありそうです。
コマンドにマイグレーションファイル名を引数として渡します。
mix ecto.gen.migration add_comments_table * creating priv/repo/migrations * creating priv/repo/migrations/20150318001628_add_comments_table.exs
マイグレーション用のファイルがpriv/repo/migrations
に作成されます。おそらくタイムスタンプ_指定した名前.exs
というファイルが作成されます。
ファイルの中身は下記のようになっているそうです。
defmodule Hello.Repo.Migrations.AddCommentsTable do use Ecto.Migration def change do end end
基本的にコマンドで生成した場合は特に何も書かれていないはずです。
このchange/0
に記載された仕様をもとに、データベースを編集したりロールバックしたりします。
たとえば、comments
というテーブルを作成し、body
、word_count
というフィールド+タイムスタンプがあるスキームを追加したとします。
このとき、マイグレーションファイルは下記のように編集します。
... def change do create table(:comments) do add :body, :string add :word_count, :integer timestamps() end end ...
ちなみに、今はcreate
を使いましたが、スキームを追加するのではなく変更する場合などはalter
を利用します。
基本的な使い方は上記の通りですが、デフォルトではないリポジトリを対象としたマイグレーションのファイルは、ecto.create
などと同様-r
オプションでリポジトリ名を指定してやる必要があります。
$ mix ecto.gen.migration -r OurCustom.Repo add_users * creating priv/repo/migrations * creating priv/repo/migrations/20150318172927_add_users.exs
mix ecto.migrate
マイグレーションファイルを作成したらすることは決まっています。マイグレーションです。
何も考えずにmix ecto.migrate
することが多いのではないでしょうか。
$ mix ecto.migrate [info] == Running Hello.Repo.Migrations.AddCommentsTable.change/0 forward [info] create table comments [info] == Migrated in 0.1s
そこまで開発にかかわってこない可能性がありますが、mix ecto.migrate
をするとデータベース上のschema_migrations
というテーブルにマイグレーション日時のタイムスタンプが作成されるようです。
hello_dev=# select * from schema_migrations; version | inserted_at ----------------+--------------------- 20150317170448 | 2015-03-17 21:07:26 20150318001628 | 2015-03-18 01:45:00 (2 rows)
あとで見てみますが、ecto.rollback
などでロールバックするときはこの辺の情報を利用してロールバックするようです。
基本的に存在している全てのマイグレーションファイルを実行してマイグレートするコマンドですが、一応実行する個数を指定出来るようです(順番はタイムスタンプ依存?)
-n
か--step
のオプションを使用して実行するマイグレーションの個数を指定します。
$ mix ecto.migrate -n 2 [info] == Running Hello.Repo.Migrations.CreatePost.change/0 forward [info] create table posts [info] == Migrated in 0.0s [info] == Running Hello.Repo.Migrations.AddCommentsTable.change/0 forward [info] create table comments [info] == Migrated in 0.0s
一応下記も同様
$ mix ecto.migrate --step 2
-v
のオプションを使用すると、指定したタイムスタンプのマイグレーションファイルのみを実行できるようです。
$ mix ecto.migrate -v 20150317170448
--to
も同じ働き。
$ mix ecto.migrate --to 20150317170448
mix ecto.rollback
ロールバック、詰まり先祖返りできます。スキーム変更したけどやり直したい!とかいう場合に活用できそうです。
それもコレもマイグレーションファイルにきっちり変更の仕様が記載されているおかげでしょう。
$ mix ecto.rollback [info] == Running Hello.Repo.Migrations.AddCommentsTable.change/0 backward [info] drop table comments [info] == Migrated in 0.0s
何も指定しなければ全部戻ってしまいそうです。
rollback
はmigration
と同様のオプションが取れます。例えば-n
でn個マイグレーションファイルを遡る、などでしょうか。
こちらはオプションを大いに活用しそうです。
Creating Our Own Mix Tasks
おおよそ見ていったmixコマンドで事足りそうですが、「もうちょっとやってくれよ」「ここ毎回同じ事してる」という場合はかゆいところに手が届くmixコマンドが欲しくなると思います。
そういった需要に応えるがごとく、mixコマンドを作成できるそうです。
まずはlib/
フォルダにmix/tasks
ディレクトリを作成していきます。
$ mkdir -p lib/mix/tasks
試しにhello.greeting.ex
というファイルを作成するとします。
defmodule Mix.Tasks.Hello.Greeting do use Mix.Task @shortdoc "Sends a greeting to us from Hello Phoenix" @moduledoc """ This is where we would put any long form documentation or doctests. """ def run(_args) do Mix.shell.info("Greetings from the Hello Phoenix Application!") end # We can define other functions as needed here. end
慣習なのか知りませんが、基本的にElixirはディレクトリ名をモジュール名に適応するようです。
(そもそもコンパイル時にチェックされる?すみません、この辺は勉強不足です。)
なので、lib/
ディレクトリ以下のmix/tasks
ディレクトリ下にあるhello.greeting.ex
なので、モジュール名はMix.Tasks.Hello.Greeting
になります。
お次にuse Mix.Task
が宣言されています。たぶんこれでmixのコマンドとして機能するのでしょう。
@shortdoc
のモジュール属性ですが、これはmix help
字に表示される説明文です。
@moduledoc
はモジュールのドキュメントです。コレに関しては割愛します。
run/1
はmix
で呼ばれたときに実行される関数です。ここではmix
で実行されたときにシェルに文字列がプリントされるだけのようです。
以上の仕様で、利用する為にはまずコンパイルします。
$ mix compile Compiled lib/tasks/hello.greeting.ex Generated hello.app
コンパイルすると、mix help
にも表示されるようです。
$ mix help | grep hello mix hello.greeting # Sends a greeting to us from Hello Phoenix
実際に利用する場合は、ファイル名で実行するようです。
$ mix hello.greeting Greetings from the Hello Phoenix Application!
run/1
では今のところ文字列を出力するMix.shell.info/1
しか利用していませんが、アプリケーションを実行するにはMix.Task.run/1
を利用すればいいようです。
... def run(_args) do Mix.Task.run("app.start") Mix.shell.info("Now I have access to Repo and other goodies!") end ...
もうちょっといろんな機能を実装するにはMixの仕様をより深掘りする必要がありそうです・・・