Chapter 13

Mixタスク

koga1020
koga1020
2021.09.25に更新

Mixタスク

現在、新しく生成されたアプリケーション内で利用可能な、組み込みのPhoenix固有のタスクとEcto固有のMixタスクが多数存在します。また、独自のアプリケーション固有のタスクを作成することもできます。

注:mix について詳しく知りたい方は、Elixirの公式のMix入門を参照してください。

Phoenixタスク

➜ mix help --search "phx"
mix local.phx          # Updates the Phoenix project generator locally
mix phx                # Prints Phoenix help information
mix phx.digest         # Digests and compresses static files
mix phx.digest.clean   # Removes old versions of static assets.
mix phx.gen.cert       # Generates a self-signed certificate for HTTPS testing
mix phx.gen.channel    # Generates a Phoenix channel
mix phx.gen.context    # Generates a context with functions around an Ecto schema
mix phx.gen.embedded   # Generates an embedded Ecto schema file
mix phx.gen.html       # Generates controller, views, and context for an HTML resource
mix phx.gen.json       # Generates controller, views, and context for a JSON resource
mix phx.gen.presence   # Generates a Presence tracker
mix phx.gen.schema     # Generates an Ecto schema and migration file
mix phx.gen.secret     # Generates a secret
mix phx.new            # Creates a new Phoenix application
mix phx.new.ecto       # Creates a new Ecto project within an umbrella project
mix phx.new.web        # Creates a new Phoenix web project within an umbrella project
mix phx.routes         # Prints all routes
mix phx.server         # Starts applications and their servers

ガイドの中でも1度は目にしたことがありますが、それらの情報が1箇所に持っていることは良いことのように思えます。

ここでは、Phoenixインストーラーの一部である phx.new, phx.new.ecto, phx.new.web を除いたPhoenix Mixタスクを扱います。これらのタスクや他のタスクについては、mix help TASK を呼び出すことで詳しく知ることができます。

mix phx.gen.html

Phoenixは、完全なHTMLリソースを立ち上げるためのすべてのコードを生成する機能を提供します。生成されるのはectoマイグレーション、ectoコンテキスト、必要なすべてのアクション、ビュー、テンプレートを持つコントローラーです。これは、とてつもなく時間を節約できます。これを実現する方法を見てみましょう。

mix phx.gen.html タスクはいくつかの引数をとります。コンテキストのモジュール名、スキーマのモジュール名、リソース名、そしてcolumn_name:type属性のリストです。私たちが渡すモジュール名は適切な大文字から始まり、Elixirのモジュール名のルールに準拠していなければなりません。

$ mix phx.gen.html Blog Post posts body:string word_count:integer
* creating lib/hello_web/controllers/post_controller.ex
* creating lib/hello_web/templates/post/edit.html.eex
* creating lib/hello_web/templates/post/form.html.eex
* creating lib/hello_web/templates/post/index.html.eex
* creating lib/hello_web/templates/post/new.html.eex
* creating lib/hello_web/templates/post/show.html.eex
* creating lib/hello_web/views/post_view.ex
* creating test/hello_web/controllers/post_controller_test.exs
* creating lib/hello/blog/post.ex
* creating priv/repo/migrations/20170906150129_create_posts.exs
* creating lib/hello/blog.ex
* injecting lib/hello/blog.ex
* creating test/hello/blog/blog_test.exs
* injecting test/hello/blog/blog_test.exs

ファイルの作成が終わると、mix phx.gen.html は、ectoマイグレーションを実行するのと同様に、ルーターファイルに1行を追加する必要があることを教えてくれます。

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

    resources "/posts", PostController

Remember to update your repository by running migrations:

    $ mix ecto.migrate

重要: これを行わないと、ログに以下のような警告が表示され、関数を実行しようとするとアプリケーションがエラーになります。

$ mix phx.server
Compiling 17 files (.ex)

warning: function HelloWeb.Router.Helpers.post_path/3 is undefined or private
  lib/hello_web/controllers/post_controller.ex:22:

リソースのコンテキストやスキーマを作成したくない場合は、--no-context フラグを使うことができます。この場合でも、パラメーターとしてコンテキストモジュール名が必要になることに注意しましょう。

$ mix phx.gen.html Blog Post posts body:string word_count:integer --no-context
* creating lib/hello_web/controllers/post_controller.ex
* creating lib/hello_web/templates/post/edit.html.eex
* creating lib/hello_web/templates/post/form.html.eex
* creating lib/hello_web/templates/post/index.html.eex
* creating lib/hello_web/templates/post/new.html.eex
* creating lib/hello_web/templates/post/show.html.eex
* creating lib/hello_web/views/post_view.ex
* creating test/hello_web/controllers/post_controller_test.exs

ルーターファイルに行を追加する必要があることを教えてくれますが、コンテキストをスキップしたので、ecto.migrate については何も言及してくれません。

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

    resources "/posts", PostController

同様に、リソースのスキーマなしでコンテキストを作成したい場合は --no-schema フラグを使うことができます。

$ mix phx.gen.html Blog Post posts body:string word_count:integer --no-schema
* creating lib/hello_web/controllers/post_controller.ex
* creating lib/hello_web/templates/post/edit.html.eex
* creating lib/hello_web/templates/post/form.html.eex
* creating lib/hello_web/templates/post/index.html.eex
* creating lib/hello_web/templates/post/new.html.eex
* creating lib/hello_web/templates/post/show.html.eex
* creating lib/hello_web/views/post_view.ex
* creating test/hello_web/controllers/post_controller_test.exs
* creating lib/hello/blog.ex
* injecting lib/hello/blog.ex
* creating test/hello/blog/blog_test.exs
* injecting test/hello/blog/blog_test.exs

ルーターファイルに行を追加する必要があることを教えてくれますが、スキーマをスキップしているので、 ecto.migrate については何も言及していません。

mix phx.gen.json

また、Phoenixは、完全なJSONリソースを立ち上げるためのすべてのコードを生成する機能を提供しています。生成されるのはectoマイグレーション、ectoスキーマ、すべての必要なアクションとビューを持つコントローラーです。このコマンドは、アプリのテンプレートを作成しません。

mix phx.gen.json タスクはいくつかの引数をとります。コンテキストのモジュール名、スキーマのモジュール名、リソース名、そしてcolumn_name:type属性のリストです。私たちが渡すモジュール名は適切な大文字から始まり、Elixirのモジュール名のルールに準拠していなければなりません。

$ mix phx.gen.json Blog Post posts title:string content:string
* creating lib/hello_web/controllers/post_controller.ex
* creating lib/hello_web/views/post_view.ex
* creating test/hello_web/controllers/post_controller_test.exs
* creating lib/hello_web/views/changeset_view.ex
* creating lib/hello_web/controllers/fallback_controller.ex
* creating lib/hello/blog/post.ex
* creating priv/repo/migrations/20170906153323_create_posts.exs
* creating lib/hello/blog.ex
* injecting lib/hello/blog.ex
* creating test/hello/blog/blog_test.exs
* injecting test/hello/blog/blog_test.exs

mix phx.gen.json がファイルの作成を終えると、ectoマイグレーションを実行するのと同様に、ルーターファイルに1行を追加する必要があることを教えてくれます。

Add the resource to your :api scope in lib/hello_web/router.ex:

    resources "/posts", PostController, except: [:new, :edit]

Remember to update your repository by running migrations:

    $ mix ecto.migrate

重要: これを行わないと、ログに以下のような警告が表示され、ページを読み込もうとするとアプリケーションがエラーになります。

$ mix phx.server
Compiling 19 files (.ex)

warning: function HelloWeb.Router.Helpers.post_path/3 is undefined or private
  lib/hello_web/controllers/post_controller.ex:18

mix phx.gen.jsonmix phx.gen.html と同様に --no-context, --no-schema などもサポートしています。

mix phx.gen.context

完全なHTML/JSONリソースを必要とせず、代わりにコンテキストだけに興味がある場合、mix phx.gen.context タスクを使うことができます。これはコンテキスト、スキーマ、マイグレーション、テストケースを生成します。

mix phx.gen.context タスクはいくつかの引数を取ります。コンテキストのモジュール名、スキーマのモジュール名、リソース名、column_name:type属性のリストを受け取ります。

$ mix phx.gen.context Accounts User users name:string age:integer
* creating lib/hello/accounts/user.ex
* creating priv/repo/migrations/20170906161158_create_users.exs
* creating lib/hello/accounts.ex
* injecting lib/hello/accounts.ex
* creating test/hello/accounts/accounts_test.exs
* injecting test/hello/accounts/accounts_test.exs

注意: リソースを名前空間にする必要がある場合は、単にジェネレーターの第一引数を名前空間にできます。

* creating lib/hello/admin/accounts/user.ex
* creating priv/repo/migrations/20170906161246_create_users.exs
* creating lib/hello/admin/accounts.ex
* injecting lib/hello/admin/accounts.ex
* creating test/hello/admin/accounts/accounts_test.exs
* injecting test/hello/admin/accounts/accounts_test.exs

mix phx.gen.schema

完全なHTML/JSONリソースを必要とせず、コンテキストの生成や変更に興味がない場合、mix phx.gen.schema タスクを使うことができます。これはスキーマとマイグレーションを生成します。

mix phx.gen.schema タスクはいくつかの引数を取ります。スキーマのモジュール名(名前空間を持つこともあります)、リソース名、column_name:type属性のリストを受け取ります。

$ mix phx.gen.schema Accounts.Credential credentials email:string:unique user_id:references:users
* creating lib/hello/accounts/credential.ex
* creating priv/repo/migrations/20170906162013_create_credentials.exs

mix phx.gen.channel

このタスクは、基本的なPhoenixチャンネルとテストケースを生成します。このタスクはチャンネルのモジュール名を引数にとります。

$ mix phx.gen.channel Room
* creating lib/hello_web/channels/room_channel.ex
* creating test/hello_web/channels/room_channel_test.exs

mix phx.gen.channel が完了すると、ルーターファイルにチャンネルルートを追加する必要があることを教えてくれます。

Add the channel to your `lib/hello_web/channels/user_socket.ex` handler, for example:

    channel "rooms:lobby", HelloWeb.RoomChannel

mix phx.gen.presence

このタスクはプレゼンストラッカーを生成します。引数としてモジュール名を渡すことができ、モジュール名を渡さない場合は Presence が用いられます。

$ mix phx.gen.presence Presence
$ lib/hello_web/channels/presence.ex

mix phx.routes

このタスクの目的は1つで、あるルーターに対して定義されたすべてのルートを表示することです。ルーティングガイドで広く使われているのを見ました。

このタスクにルーターを指定しない場合、Phoenixが生成してくれたルーターがデフォルトになります。

$ mix phx.routes
page_path  GET  /  TaskTester.PageController.index/2

また、アプリケーションに複数のルーターがある場合は、個々のルーターを指定することもできます。

$ mix phx.routes TaskTesterWeb.Router
page_path  GET  /  TaskTesterWeb.PageController.index/2

mix phx.server

これはアプリケーションを起動するために使用するタスクです。これは引数を一切取りません。何か引数を渡しても、それは静かに無視されます。

$ mix phx.server
[info] Running TaskTesterWeb.Endpoint with Cowboy on port 4000 (http)

引数 DoesNotExist を黙って無視します。

$ mix phx.server DoesNotExist
[info] Running TaskTesterWeb.Endpoint with Cowboy on port 4000 (http)

アプリケーションを起動して iex セッションを開きたい場合は、iex 内でMixタスクを次のように実行できます。

$ iex -S mix phx.server
Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

[info] Running TaskTesterWeb.Endpoint with Cowboy on port 4000 (http)
Interactive Elixir (1.0.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>

mix phx.digest

このタスクは2つのことを行います。静的アセットのダイジェストを作成し、圧縮します。

ここでいう「ダイジェスト」とは、アセットの内容をMD5によってダイジェストしたもので、アセットのファイル名に追加されます。これにより、一種の「フィンガープリント」が作成されます。ダイジェストが変更されなければ、ブラウザやCDNはキャッシュされたバージョンを使用します。ダイジェストが変更された場合は、新しいバージョンを再取得します。

このタスクを実行する前に、helloアプリケーションの2つのディレクトリの内容を調べてみましょう。

最初の priv/static は次のようになっているはずです。

├── images
│   └── phoenix.png
├── robots.txt

そして、assets/ は次のようになります。

├── css
│   └── app.css
├── js
│   └── app.js
├── vendor
│   └── phoenix.js

これらのファイルはすべて静的アセットです。それでは mix phx.digest タスクを実行してみましょう。

$ mix phx.digest
Check your digested files at 'priv/static'.

タスクが示すように、priv/static ディレクトリの内容を検査できます。 assets/ のすべてのファイルが priv/static にコピーされ、各ファイルにはいくつかのバージョンがあることがわかります。それらのバージョンは次の通りです。

  • 元ファイル
  • gzip圧縮されたファイル
  • 元のファイル名とそのダイジェストを含むファイル
  • ファイル名とそのダイジェストを含む圧縮ファイル

オプションで、設定ファイルの :gzippable_exts オプションを使って、どのファイルを圧縮するかを決めることができます。

config :phoenix, :gzippable_exts, ~w(.js .css)

注意: mix phx.digest が処理されたファイルを置く別の出力フォルダーを指定できます。第一引数は静的ファイルが置かれているパスです。

$ mix phx.digest priv/static -o www/public
Check your digested files at 'www/public'.

Ectoタスク

新しく生成されたPhoenixアプリケーションは、デフォルトでectoとpostgrexを依存関係として含んでいます(mix phx.new--no-ecto フラグを付けて使わない限り)。これらの依存関係には、一般的なectoの操作を行うためのMixタスクが含まれています。どのようなタスクがあるのか見てみましょう。

$ mix help --search "ecto"
mix ecto               # Prints Ecto help information
mix ecto.create        # Creates the repository storage
mix ecto.drop          # Drops the repository storage
mix ecto.dump          # Dumps the repository database structure
mix ecto.gen.migration # Generates a new migration for the repo
mix ecto.gen.repo      # Generates a new repository
mix ecto.load          # Loads previously dumped database structure
mix ecto.migrate       # Runs the repository migrations
mix ecto.migrations    # Displays the repository migration status
mix ecto.rollback      # Rolls back the repository migrations

注意: アプリケーションを起動せずにタスクを実行するには、--no-start フラグを付けて上記のタスクを実行できます。

mix ecto.create

このタスクはレポで指定されたデータベースを作成します。デフォルトでは、アプリケーションの名前のついたレポ(ectoをオプトアウトしていない限り、アプリケーションで生成されたもの)を探しますが、必要に応じて別のレポを渡すこともできます。

実際の動作は次のようになります。

$ mix ecto.create
The database for Hello.Repo has been created.

ecto.create でうまくいかないことがいくつかあります。Postgresデータベースに "postgres" ロール(ユーザー)がない場合、このようなエラーが発生します。

$ mix ecto.create
** (Mix) The database for Hello.Repo couldn't be created, reason given: psql: FATAL:  role "postgres" does not exist

ログインしてデータベースを作成するために必要なパーミッションを持った "postgres" ロールを psql コンソールにて作成することで、これを修正できます。

=# CREATE ROLE postgres LOGIN CREATEDB;
CREATE ROLE

"postgres" ロールがアプリケーションにログインする権限を持っていない場合、このようなエラーが発生します。

$ mix ecto.create
** (Mix) The database for Hello.Repo couldn't be created, reason given: psql: FATAL:  role "postgres" is not permitted to log in

これを修正するには、"postgres" ユーザーのパーミッションを変更してログインを許可する必要があります。

=# ALTER ROLE postgres LOGIN;
ALTER ROLE

"postgres" ロールがデータベースを作成する権限を持っていない場合、このようなエラーが発生します。

$ mix ecto.create
** (Mix) The database for Hello.Repo couldn't be created, reason given: ERROR:  permission denied to create database

これを修正するには、psql コンソールで "postgres" ユーザーのパーミッションを変更して、データベースの作成を許可する必要があります。

=# ALTER ROLE postgres CREATEDB;
ALTER ROLE

"postgres" ロールがデフォルトの "postgres" とは異なるパスワードを使用している場合、このエラーが発生します。

$ mix ecto.create
** (Mix) The database for Hello.Repo couldn't be created, reason given: psql: FATAL:  password authentication failed for user "postgres"

これを修正するには、環境固有の設定ファイルでパスワードを変更します。開発環境で使用するパスワードは config/dev.exs ファイルの一番下にあります。

最後に、データベースを作成したい OurCustom.Repo という名前のレポがある場合は、次のように実行します。

$ mix ecto.create -r OurCustom.Repo
The database for OurCustom.Repo has been created.

mix ecto.drop

このタスクはレポで指定したデータベースを削除します。デフォルトでは、私たちのアプリケーションにちなんだレポを探します(ectoをオプトアウトしない限り、私たちのアプリケーションで生成されたもの)。dbを削除するかどうかを確認するためのプロンプトは表示されませんので、注意してください。

$ 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

多くのアプリケーションでは複数のデータストアを必要とします。それぞれのデータストアに対して新しいレポが必要で、ecto.gen.repo で自動的に生成できます。

レポの名前を OurCustom.Repo とすると、このタスクは lib/our_custom/repo.ex という名前でレポを作成します。

$ 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):

    children = [
      ...,
      OurCustom.Repo,
      ...
    ]

このタスクは config/config.exs を更新していることに注目してください。見てみると、新しいレポ用に追加された設定ブロックが見えます。

. . .
config :hello, OurCustom.Repo,
  database: "hello_repo",
  username: "user",
  password: "pass",
  hostname: "localhost"
. . .

もちろん、ログインクレデンシャルをデータベースが期待するものと一致するように変更する必要があります。また、他の環境用に設定を変更する必要があります。

指示にしたがって、新しいレポをスーパーバイザーツリーに追加する必要があります。私たちの Hello アプリケーションでは、lib/hello.ex を開き、私たちのレポをworkerとして children リストに追加します。

. . .
children = [
  # Start the Ecto repository
  Hello.Repo,
  # Our custom repo
  OurCustom.Repo,
  # Start the endpoint when the application starts
  HelloWeb.Endpoint,
]
. . .

mix ecto.gen.migration

マイグレーションは、データベーススキーマへの変更に影響を与えるためのプログラム的で繰り返し可能な方法です。マイグレーションは単なるモジュールであり、ecto.gen.migration タスクで作成できます。新しいコメントテーブルのためのマイグレーションを作成する手順を見てみましょう。

タスクを起動するには、必要なモジュール名のsnake_caseバージョンを指定するだけです。この名前にはマイグレーションで何をするかを記述することが望ましいです。

mix ecto.gen.migration add_comments_table
* creating priv/repo/migrations
* creating priv/repo/migrations/20150318001628_add_comments_table.exs

マイグレーションのファイル名は、ファイルが作成された日時を表す文字列で始まることに注目してください。

priv/repo/migrations/20150318001628_add_comments_table.exs にある ecto.gen.migration が生成したファイルを見てみましょう。

defmodule Hello.Repo.Migrations.AddCommentsTable do
  use Ecto.Migration

  def change do
  end
end

単一の関数 change/0 があり、これはフォワードとロールバックの両方を処理します。ectoの便利なDSLを使ってスキーマの変更を定義し、ロールフォワードかロールバックかによって何をすべきかを判断します。非常に良いですね。

ここでやりたいことは、body カラム、word_count カラム、そして inserted_atupdated_at のタイムスタンプカラムを持つ comments テーブルを作成することです。

. . .
def change do
  create table(:comments) do
    add :body,       :string
    add :word_count, :integer
    timestamps()
  end
end
. . .

繰り返しになりますが、このタスクは -r フラグと必要に応じて別のレポを使って実行できます。

$ mix ecto.gen.migration -r OurCustom.Repo add_users
* creating priv/repo/migrations
* creating priv/repo/migrations/20150318172927_add_users.exs

データベーススキーマを変更する方法の詳細についてはectoのマイグレーションDSL(ecto migrationドキュメント)を参照してください。
たとえば、既存のスキーマを変更するには、ectoのalter/2関数を使用しています。

これで完了です!マイグレーションを実行する準備ができました。

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

最初に ecto.migrate を実行すると、schema_migrations というテーブルが作成されます。これは、マイグレーションのファイル名のタイムスタンプ部分を保存することで、実行したすべてのマイグレーションを追跡します。

テーブル 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 はそのマイグレーションを表すレコードを schema_migrations から削除します。

デフォルトでは、ecto.migrate は保留中のすべてのマイグレーションを実行します。タスクの実行時にいくつかのオプションを指定することで、どのマイグレーションを実行するかを制御できます。

-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

--step オプションも同じように動作します。

mix ecto.migrate --step 2

to オプションは、指定されたバージョンまでのすべてのマイグレーションを実行します。

mix ecto.migrate --to 20150317170448

mix ecto.rollback

ecto.rollback タスクは最後に実行したマイグレーションを逆にして、スキーマの変更を元に戻します。ecto.migrateecto.rollback は互いにミラーイメージです。

$ mix ecto.rollback
[info] == Running Hello.Repo.Migrations.AddCommentsTable.change/0 backward
[info] drop table comments
[info] == Migrated in 0.0s

ecto.rollbackecto.migrate と同じオプションを扱うので、-n, --step, -v, --toecto.migrate と同じように動作します。

独自のMixタスクを作成する

このガイドで見てきたように、mix自体も、アプリケーションに持ち込む依存関係も、本当に便利なタスクを無料で提供してくれます。これらのいずれも、個々のアプリケーションのすべてのニーズを予測することはできませんが、mixでは独自のカスタムタスクを作成できます。これがまさにこれからやろうとしていることです。

最初にすべきことは、lib の中に mix/tasks ディレクトリを作ることです。ここにアプリケーション固有のMixタスクを作成します。

$ 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

ここでは、作成中のMixタスクに含まれる動的な部分を簡単に見てみましょう。

最初にすべきことは、モジュールの名前を付けることです。すべてのタスクは Mix.Tasks 名前空間で定義する必要があります。これを mix hello.greeting として呼び出したいので、モジュール名を Hello.Greeting と命名します。

use Mix.Task 行は、このモジュールをMixタスクとして動作させるMixの機能をもたらします。

モジュール属性 @shortdoc は、ユーザーが mix help を起動したときのタスクを説明する文字列を保持します。

moduledoc は他のモジュールと同じ機能を果たします。長文のドキュメントやdoctestがあれば、ここに置くことができます。

関数 run/1 はMixタスクの重要な心臓部です。これは、ユーザーがタスクを起動したときにすべての作業を行う関数です。我々のタスクでは、アプリから挨拶を送るだけですが、run/1 関数を実装することで、必要なことを何でも行うことができます。Mix.shell().info/1 は、ユーザーへテキストを出力するのに好ましい方法であることに注目してください。

もちろん、このタスクは単なるモジュールなので、run/1 関数をサポートするために必要に応じて他のプライベート関数を定義できます。

タスクモジュールを定義したので、次のステップはアプリケーションをコンパイルすることです。

$ mix compile
Compiled lib/tasks/hello.greeting.ex
Generated hello.app

これで新しいタスクが mix help に表示されるようになりました。

$ mix help --search hello
mix hello.greeting # Sends a greeting to us from Hello Phoenix

mix help@shortdoc に入力したテキストとタスク名を表示していることに注目してください。

ここまでは順調ですが、うまくいくでしょうか?

$ mix hello.greeting
Greetings from the Hello Phoenix Application!

確かに動作しました。

新しいMixタスクでアプリケーションのインフラストラクチャを使用するようにしたい場合、Mixタスクが実行されているときにアプリケーションが起動しているようにする必要があります。これは、Mixタスク内からデータベースへアクセスする必要がある場合にとくに便利です。ありがたいことに、mixはそれを本当に簡単にしてくれます。

  . . .
  def run(_args) do
    Mix.Task.run("app.start")
    Mix.shell().info("Now I have access to Repo and other goodies!")
  end
  . . .