Posted at

[翻訳]Elixirでコマンドラインアプリケーションを書く

More than 3 years have passed since last update.

この文章はIlija Eftimovさんの2015年11月27日付のブログ記事Writing command line apps with Elixirの翻訳です。

Elixirを日常的に使用する簡易なツールを作るための言語としても利用できたらなと思っていたところこの記事を見つけました。

誤訳や関連記事などがあればコメント欄にお願いいたします。


Elixirはとてもクールな言語だ。まだ十分な経験があるわけではないが、私はいつもこれで興味深いものを作り、ビルトインツールを学んでいる。この記事ではescriptを使ってコマンドラインアプリケーションの作り方をお見せしようと思う。


Escript

ErlangとElixirはescriptというクールなツールを持っている。これは基本的にはElixirのアプリケーションをコマンドラインアプリケーションにコンパイルするものだ。

Elixirのescriptのドキュメントからの引用


escriptはコマンドラインから実行可能である。Erlangがインストールされていればどのような環境でも実行可能で、Elixirはコマンドラインアプリケーションに埋め込まれるためデフォルトではElixirのインストールは要求されない。


興味深いことはescriptはElixirアプリケーションをビルドしコマンドラインアプリケーションを作成するということだ。実行可能なアプリケーションはErlangさえインストールされていればどの環境でも動作する。


eight_ballescriptでくるむ

例示のための小さなアプリケーションを作る代わりに、以前にWrite and publish your first Elixir libraryという記事を書く際に作ったeight_ballというアプリケーションを流用しようと思う。もしこの記事を読んでいないようであれば一読して戻ってくることをお勧めする。

このアプリケーションはすごくシンプルで、EightBall.ask/1という関数だけを持つ。引数として質問を取り、ランダムに回答を返す。ではescriptを使ってこれのコマンドラインアプリケーションを作ってみよう。


アプリケーションにescriptを追加する

もしこの話題についていきたいのであれば、このリポジトリにあるソースを見ておくといいだろう。

mix.exsに追加する。


mix.exs

defmodule EightBall.Mixfile do

use Mix.Project

def project do
[app: :eight_ball,
version: "0.0.1",
elixir: "~> 1.0",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
escript: [main_module: EightBall.CLI], # <- this line
deps: deps,
package: package ]
end
# ...
end


追加した行はescriptEightBall.CLImain/1関数があることを知らせるものだ。main/1関数はコマンドラインアプリケーションのエントリーポイントとなる。


EightBall.CLI

EightBall.CLIモジュールがまだないので、まずはそれを作るところから始めよう。

defmodule EightBall.CLI do

def main(args) do
end
end

見ての通りこのモジュールはコマンドライン引数を取るmain/1関数を持っている。コマンドライン引数を扱いやすくするためにOptionParserを使う必要がある。

アプリケーションを実行する際のシンタックスはこんな感じか

eight_ball --question "Is Elixir great?"

こんな感じにしたい。

eight_ball -q "Is Elixir great?"

ではOptionParserを使って-q/--question引数をオプションとして定義してみよう。

defmodule EightBall.CLI do

def main(argv) do
{options, _, _} = OptionParser.parse(argv,
switches: [question: :string],
)

IO.inspect options
end
end

main/1関数は引数をパースし意味あるタグ付けされたリストを生成する。この段階ではmain/1関数はパースされた引数を表示するだけとなっている。後ほど意味のある処理を追加していこうと思う。

まずは実行可能はescriptを作ってみよう。作り方はプロジェクトのルートディレクトリに移動して以下のコマンドを実行する。

mix escript.build

これはElixirアプリケーションをコンパイルし実行可能なescriptを生成する。

➜  eight_ball git:(master) ✗ mix escript.build

Compiled lib/eight_ball/cli.ex
Generated eight_ball app
Generated escript eight_ball with MIX_ENV=dev

eight_ballプロジェクトのルートディレクトリを見ると、eight_ballという実行可能なファイルが出来上がっていることがわかるだろう。これを使うには以下のように入力する。

./eight_ball --question "Is Elixir great?"

そうすると以下のように表示されると思う。

➜  eight_ball git:(master) ✗ ./eight_ball -q "Is Elixir great?"

[question: "Is Elixir great?"]

ほらみて!パースされたコマンドライン引数が見えるでしょ!


アプリケーションを統合する

ではEightBall.ask/1をコマンドラインアプリケーションで使ってみよう。EightBall::CLI.main/1に以下のコードを追加してみよう。

defmodule EightBall.CLI do

def main(opts) do
{options, _, _} = OptionParser.parse(opts,
switches: [question: :string],
aliases: [q: :question] # makes '-q' an alias of '--question'
)

try do
IO.puts EightBall.ask(options[:question])
rescue
e in RuntimeError -> e
IO.puts e.message
end
end
end

try/rescueブロックの中で質問文をask/1関数に送信し、RuntimeErrorをキャッチする。このエラーはEightBall::QuestionValidatorという入力値チェック機能によって引き起こされる。もし質問文が疑問形でない場合にこのようなエラーを投げる。

"Question must be a string, ending with a question mark."

もしコマンドラインアプリケーションがエラーをキャッチした場合はエラーメッセージを表示する。


コマンドラインアプリケーションをビルドする

最後のステップはescriptを実行することだ。Elixirではmixを使うことでescriptを実行することはとても簡単である。

mix escript.build

もし手順に誤りがなければ以下のように表示されるだろう。

➜  eight_ball git:(master) ✗ mix escript.build

Compiled lib/eight_ball.ex
Generated eight_ball app
Generated escript eight_ball with MIX_ENV=dev

これはメインモジュールと同名のコマンドラインアプリケーションを生成する。この場合はeight_ballのようになる。もし実行可能ファイルをテキストエディタなどで開くと、読むことが困難なたくさんのコードを目にするだろう。これはErlangVMのバイトコードに変換されたという証拠である。

素晴らしいことはこれをErlangのインストールされたマシンに送り使用することができる。Elixirはアプリケーションに埋め込まれているので、唯一の依存性はErlangだけである。クールでしょ?

アプリケーションは以下のようにして実行する。

➜  eight_ball git:(master) ✗ ./eight_ball --question "Is Elixir awesome?"

Outlook good

もしくはこのように。

➜  eight_ball git:(master) ✗ ./eight_ball --question "Is Elixir awesome"

Question must be a string, ending with a question mark.