Goal
ファイルの自動生成を行うカスタムタスクを作成する。
Dev-Environment
OS: Windows8.1
Erlang: Eshell V6.4, OTP-Version 17.5
Elixir: v1.0.4
Erlang: Eshell V6.4, OTP-Version 17.5
Elixir: v1.0.4
Wait a minute
mixで使える自分用のコマンド(カスタムタスク)を作成します。
おまけにて、大体どのドキュメントを見ればいいか簡単にまとめてます。
Index
My mix command
|> Preparation
|> Automatic generation of file
|> Extra
|> Preparation
|> Automatic generation of file
|> Extra
Preparation
準備は以下の記事を見て下さい。
参考: Mixのカスタムタスクを作成
参考: Mixのカスタムタスクを作成
Automatic generation of file
mixのコマンドからファイルの自動生成を行うカスタムタスクを作成します。
ファイル: lib/mix/tasks/sample.ex
defmodule Mix.Tasks.Sample do
use Mix.Task
import Mix.Generator
@shortdoc "My custom task sample"
def run(_args) do
app_module = Mix.Project.config[:app] |> Atom.to_string |> Mix.Utils.camelize
assigns = [app_module: app_module]
# template
create_file(
Path.join(["lib", "test_template.ex"]) |> Path.relative_to(Mix.Project.app_path),
test_template(assigns))
# text
create_file(
Path.join(["lib", "test.ex"]) |> Path.relative_to(Mix.Project.app_path),
test_text)
end
# template
embed_template :test, """
defmodule <%= @app_module %>.Test do
end
"""
# text
embed_text :test, """
defmodule MyMixCmd.Test do
end
"""
end
Result:
>mix sample
* creating lib/test_template.ex
* creating lib/test.ex
Description:
・embed_template/2
渡している引数の@app_module部分は、
アプリケーション名に変換されて出力されています。
アプリケーション名に変換されて出力されています。
Phoenix-Frameworkでよく使う、
.html.eexの埋め込みコードのようなことをやっていますね。
.html.eexの埋め込みコードのようなことをやっていますね。
正しいです。
実際にこれEExの機能です。
実際にこれEExの機能です。
・embed_text/2
渡している引数の通り出力されていますね。
Extra
mixのモジュールと関数を簡単に把握します。
・Mix
皆さんご存知のmixです。
mixは、Elixirプロジェクトの作成、コンパイル、テストをするためのタスク、
それとハンドルの依存関係の機能を提供する総合的なビルドツールです。
それとハンドルの依存関係の機能を提供する総合的なビルドツールです。
素晴らしいですね~開発が捗ります><
とりあえず、raise/1の例だけ記述しておきます。
Example:
defmodule Mix.Tasks.Sample do
use Mix.Task
@shortdoc "My custom task sample"
def run(_args) do
Mix.raise "raise"
end
end
Result:
>mix sample
** (Mix) raise
・Mix.Task
作成、ロード及びタスクを操作するための便利な機能を提供する単純なモジュールです。
useして使っているインターフェース(behaviour)ですね。
前回の記事で、このインターフェースのrun/2を実装しています。
後は・・・shortdoc/1もこのインターフェースにある関数ですね。
後は・・・shortdoc/1もこのインターフェースにある関数ですね。
Example:
defmodule Mix.Tasks.Sample do
use Mix.Task
@shortdoc "My custom task sample"
def run(args) do
IO.puts (inspect args)
end
end
・Mix.Shell.IO
mixのデフォルトのシェルです。
単純にstdioとstderrにメッセージを出力します。
単純にstdioとstderrにメッセージを出力します。
ソースコードを見てみると、
Mix.Shellインターフェース(behaviour)を実装しているようです。
Mix.Shellインターフェース(behaviour)を実装しているようです。
おそらく・・・cmd/2、error/1、info/1あたりは割と利用頻度が高いのではないかと予想。
例としてcmd/2も使いたかったのですが・・・使い方が分かりませんでした。
Example:
defmodule Mix.Tasks.Sample do
use Mix.Task
@shortdoc "My custom task sample"
def run(_args) do
Mix.Shell.IO.info("info")
Mix.Shell.IO.error("error")
end
end
Result:
>mix sample
info
error
・Mix.Project
Mixプロジェクトの定義と操作をします。
プロジェクトの情報を取得する際に役に立ちそうですね。
プロジェクトの情報を取得する際に役に立ちそうですね。
使い方についてはドキュメントを見れば大体分かります。
ファイル: mix.exs
モジュールでuseしてますね。
defmodule MyMixCmd.Mixfile do
use Mix.Project
def project do
[app: :my_mix_cmd,
version: "0.0.1",
elixir: "~> 1.0",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps]
end
...
end
Example:
defmodule Mix.Tasks.Sample do
use Mix.Task
@shortdoc "My custom task sample"
def run(_args) do
IO.inspect Mix.Project.app_path
IO.inspect Mix.Project.build_path
IO.inspect Mix.Project.config
end
end
Result:
>mix sample
"c:/MyWorkSpaces/darui_works_local/elixir_space/my_mix_cmd/_build/dev/lib/my_mix_cmd"
"c:/MyWorkSpaces/darui_works_local/elixir_space/my_mix_cmd/_build/dev"
[app: :my_mix_cmd, version: "0.0.1", elixir: "~> 1.0", build_embedded: false,
start_permanent: false, deps: [], aliases: [], build_per_environment: true,
default_task: "run", deps_path: "deps", elixirc_paths: ["lib"],
erlc_paths: ["src"], erlc_include_path: "include", erlc_options: [:debug_info],
lockfile: "mix.lock", preferred_cli_env: []]
・Mix.Config
定義するためのモジュールへ、アプリケーションの設定の読み込みとマージをします。
最も一般的には、このモジュールは、独自の構成を定義するために使用されます。
最も一般的には、このモジュールは、独自の構成を定義するために使用されます。
これの使い方としては、config/config.exsを見るのが手っ取り早いですね。
丁寧に使い方のサンプルが書いてありますので・・・
丁寧に使い方のサンプルが書いてありますので・・・
・Mix.Utils
そのまんまですね。mixとタスク全体で使用するユーティリティです。
便利そうな関数があります。
幾つか使ってみます。
幾つか使ってみます。
Example:
defmodule Mix.Tasks.Sample do
use Mix.Task
@shortdoc "My custom task sample"
def run(_args) do
IO.inspect Mix.Utils.camelize("foo_bar")
IO.inspect Mix.Utils.command_to_module_name("sample")
IO.inspect Mix.Utils.module_name_to_command(Mix.Tasks.Sample, 2)
IO.inspect Mix.Utils.mix_home()
IO.inspect Mix.Utils.mix_paths()
end
end
Result:
>mix sample
"FooBar"
"Sample"
"sample"
"c:/Users/user_name/.mix"
[]
・Mix.Generator
コンテンツのパスと生成の作業に便利。
これらの機能の全ては、ある意味で、彼らはMix.shellを介して実行するアクションを記録し、冗長です。
(説明が上手く翻訳できない!!)
これらの機能の全ては、ある意味で、彼らはMix.shellを介して実行するアクションを記録し、冗長です。
(説明が上手く翻訳できない!!)
ファイルやディレクトリの作成ができるのと、
生成するファイルに対してテキストやテンプレートの埋め込みができるようです。
生成するファイルに対してテキストやテンプレートの埋め込みができるようです。
テキストの例。
Example:
defmodule Mix.Tasks.Sample do
use Mix.Task
import Mix.Generator
@shortdoc "My custom task sample"
def run(_args) do
create_file(
Path.join(["lib", "test.ex"]) |> Path.relative_to(Mix.Project.app_path),
test_text)
end
embed_text :test, """
defmodule MyMixCmd.Test do
end
"""
end
Description:
さて、どうやって動作しているのかさっぱりです・・・
ですが、分かったことが一つだけあります。
さて、どうやって動作しているのかさっぱりです・・・
ですが、分かったことが一つだけあります。
embed_text/2の内部で:testの部分は、:test_textに変換されているようです。
そして、create_file/3の引数でtest_textを渡しています。
そして、create_file/3の引数でtest_textを渡しています。
test_textは、別にどこかで定義したわけでもありません。
実際に、test_textのアンダースコア以降を変更するとコンパイルエラーで動かなくなります。
実際に、test_textのアンダースコア以降を変更するとコンパイルエラーで動かなくなります。
ソースコードを見る限りは、
Elixirのメタプログラミングを習得しないと分からないと思います。
Elixirのメタプログラミングを習得しないと分からないと思います。
動作はさせられるので、とりあえずはいいでしょう。
Result:
>mix sample
* creating lib/test.ex
>mix sample
* creating lib/test.ex
lib/test.ex already exists, overwrite? [Yn] y
Description:
既に同名ファイルが存在する場合、
上書きしていいか聞いてくれます。
既に同名ファイルが存在する場合、
上書きしていいか聞いてくれます。
ファイルの内容を見てみるとembed_text/2で渡している引数の内容が出力されていますね。
さてさて、もう一つ例を書いておきます。
テンプレートの例。
テンプレートの例。
Example:
defmodule Mix.Tasks.Sample do
use Mix.Task
import Mix.Generator
@shortdoc "My custom task sample"
def run(_args) do
app_module = Mix.Project.config[:app] |> Atom.to_string |> Mix.Utils.camelize
assigns = [app_module: app_module]
# template
create_file(
Path.join(["lib", "test_template.ex"]) |> Path.relative_to(Mix.Project.app_path),
test_template(assigns))
end
# template
embed_template :test, """
defmodule <%= @app_module %>.Test do
end
"""
end
Result:
>mix sample
* creating lib/test_template.ex
Speaking to oneself
embed_template/2を使う場合、
Mix.Generator.create_file/3の第二引数に渡す値がさっぱり分からなかった。
Mix.Generator.create_file/3の第二引数に渡す値がさっぱり分からなかった。
お陰様で時間を大分消費してしまった。
Bibliography
Github - ma2gedev/breadcrumble_ex implement generator for the Phoenix application
A Custom Mix Task for Phoenix App
hexdocs - v1.0.5 Elixir Mix
hexdocs - v1.0.5 Elixir Mix.Task
hexdocs - v1.0.5 Elixir Mix.Shell.IO
hexdocs - v1.0.5 Elixir Mix.Generator
hexdocs - v1.0.5 Elixir Mix.Utils
hexdocs - v1.0.5 Elixir Mix.Project
hexdocs - v1.0.5 Elixir Mix.Config
A Custom Mix Task for Phoenix App
hexdocs - v1.0.5 Elixir Mix
hexdocs - v1.0.5 Elixir Mix.Task
hexdocs - v1.0.5 Elixir Mix.Shell.IO
hexdocs - v1.0.5 Elixir Mix.Generator
hexdocs - v1.0.5 Elixir Mix.Utils
hexdocs - v1.0.5 Elixir Mix.Project
hexdocs - v1.0.5 Elixir Mix.Config