技術メモ

プログラミングとか電子工作とか

Elixir入門(Mix and OTP編 第1章 Introduction to Mix)

f:id:ysmn_deus:20190122112104p:plain

どうも、靖宗です。
ようやくGETTING STARTEDが終わりましたが氷山の一角すら体感できてない感じです。
ささっとウェブのチュートリアルを終わらせてfukuoka.exの強い人の記事なんかを追いたいところです。
浮気心を押さえてMix and OTP編を進めて行きます。

Elixirのアプリケーションのビルドとかの話だと思うのですが、「アプリケーションはキーバリューストアとして動作する」とあります。
('ω')。o(????????????)
イマイチ全容がつかめません。この章で作って行くサンプルのことを指している?
おそらくこの章、もしくは章をまたいでシンプルなクライアントを作って行きます。想定としては下記のようなリクエストが送受信できる感じ?

CREATE shopping
OK

PUT shopping milk 1
OK

PUT shopping eggs 3
OK

GET shopping milk
1
OK

DELETE shopping eggs
OK

典型的なCRUD?とりあえず読み進めます。
このキーバリューのアプリケーションを作って行くに当たって3つのツールを使います。

  • OTP (Open Telecom Platform) Erlangのライブラリ。Erlangの開発者はコレを使って堅牢なシステムを作ってるみたい。たぶん進めれば分かる。
  • Mix ビルドツール。テストとかもコレを使う?
  • ExUnit Elixirのユニットテストフレームワーク。たぶん。

たぶんばっかりですが、知らないものは仕方ない。

Our first project

って言っても前回なにもしないプロジェクトを作成してるゾ!
という突っ込みは無しにして、このMix and OTP編で使うプロジェクトを作成していきます。

PS > mix new kv --module KV
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/kv.ex
* creating test
* creating test/test_helper.exs
* creating test/kv_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd kv
    mix test

mix new プロジェクト名という感じでしょうか。
今回は--module KVとしてますが、デフォルトではKvとなるそうです。hello_projectならHello_projectになるのかな。

defmodule HelloProject do
  @moduledoc """
  Documentation for HelloProject.
  """

  @doc """
  Hello world.

  ## Examples

      iex> HelloProject.hello()
      :world

  """
  def hello do
    :world
  end
end

HelloProjectになってました。意外と賢い。
兎に角、明示的にモジュール名を指定するときは--module 名前とするそうです。

Project compilation

生成されたファイルで、プロジェクト名のディレクトリ直下にあるmix.exsを見ていきます。
どうやらこれはプロジェクトの設定ファイルのようです。

defmodule KV.MixProject do
  use Mix.Project

  def project do
    [
      app: :kv,
      version: "0.1.0",
      elixir: "~> 1.6-dev",
      start_permanent: Mix.env == :prod,
      deps: deps()
    ]
  end

  # Run "mix help compile.app" to learn about applications
  def application do
    [
      extra_applications: [:logger]
    ]
  end

  # Run "mix help deps" to learn about dependencies
  defp deps do
    [
      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
    ]
  end
end

KV.MixProjectモジュール内にパブリックな関数2つとプライベートな関数1つが定義されてます。
projectはプロジェクト自体のバージョンやElixirのバージョン依存などでしょうか。内部でプライベート関数depsの呼び出しもあります。
aplicationはよく分かりませんがなんか要るんでしょう。そういえば二十二章The crypto moduleで書き足せって言われた気がします。これはまぁ追々。
あとはプライベート関数のdeps。見るからに依存関係を表してそうな感じ。ライブラリや外部のライブラリなどの依存関係?gitのアドレスも例として書かれているのでウェブからも参照してコンパイルできるのかな。(Goっぽい?)

次にlib/kv.exファイル。これはプロジェクト名にも該当するし実質的なメインファイル?

defmodule KV do
  @moduledoc """
  Documentation for KV.
  """

  @doc """
  Hello world.

  ## Examples

      iex> KV.hello()
      :world

  """
  def hello do
    :world
  end
end

Hello Worldが書かれている。
とりあえずコンパイルするにはこんだけで良いらしい。
コンパイルもmixで行う。

PS kvのプロジェクトフォルダ> mix compile
Compiling 1 file (.ex)
Generated kv app

プロジェクトのディレクトリに_buildというフォルダが追加される。この中にコンパイルされたkv.appがある。
他にもconsolidatedフォルダの中にゴチャゴチャとあるが、これらはおそらくkv.appを動かす時に必要なモジュール群であり、全てが独立したプロトコルとしてVM上を走るっぽい。
十六章ではイマイチピンときませんでしたが、なるほどなっとく。
iexでは色々動いてる環境下でありがたくStringライブラリなど使えてましたが、おそらくコンパイルした後はそうも行かないのでそういった有り難いライブラリはプロトコルとして動かしてる感じでしょうか。

iex -S mixでiex上で起動できる。ただしWindowsPowerShell上なのでiex.bat

PS > iex.bat -S mix
Interactive Elixir (1.8.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> KV.hello()
:world

Running tests

mixツールでテストまで実行できるようです。
基本的にテストのスクリプトtestディレクトリ内部に<filename>_test.exsとして用意するようです。
今回生成したkv.exのテストファイルkv_test.exsは最初っから生成されてます。

defmodule KVTest do
  use ExUnit.Case
  doctest KV

  test "greets the world" do
    assert KV.hello() == :world
  end
end

なんか注意書きが。

  1. テストファイルはElixirのスクリプトファイル.exsであること。(いちいちコンパイルしなくていいように)
  2. テストのモジュールはモジュール名Testとし、ExUnit.Caseuseしてtest/2マクロを使う(ルー大柴みたいになってますが許して)

最初に生成されるテストファイルを参照しつつドキュメント読めばよさそうではあります。

あとtestディレクトリにtest_helper.exsというファイルが生成されていますが、中には関数一個が実行されるように書かれてるだけです。

ExUnit.start()

テストをカスタムしたいときとかに使うんでしょうか。

テストの実行もmixを使用します。

PS > mix test
Compiling 1 file (.ex)
Generated kv app
..

Finished in 0.04 seconds
1 doctest, 1 test, 0 failures

Randomized with seed 994000

テストするときに再コンパイルしてるのが気になりますが後々言及がある様なのでひとまず置いときます。
doctestはドキュメントとして成り立ってればそれでええのかな?

テストが問題無く終わってるので、敢えてミスるように書き換える。
kv_test.exsの比較してるところを変更する。

assert KV.hello() == :oops

mix testを再実行

PS > mix test


  1) test greets the world (KVTest)
     test/kv_test.exs:5
     Assertion with == failed
     code:  assert KV.hello() == :oops
     left:  :world
     right: :oops
     stacktrace:
       test/kv_test.exs:6: (test)

.

Finished in 0.03 seconds
1 doctest, 1 test, 1 failure

Randomized with seed 927000

今回はコンパイルが起こってない。
また、書き換えた箇所が「こう違うで!」と表示されている。

テストが失敗した箇所(test/kv_test.exs:5)が表示されてるが、ここだけをテストすることも可能だそうで。
試しに変更した箇所を元に戻してやってみる。

PS > mix test test/kv_test.exs:5
Excluding tags: [:test]
Including tags: [line: "5"]

.

Finished in 0.03 seconds
1 doctest, 1 test, 0 failures, 1 excluded

Randomized with seed 50000

開発していくときに結構使えそう。

Automatic code formatting

mix newでプロジェクトを生成した際に出てくる.formatter.exsの説明が書かれていますがイマイチぴんときません。
たぶんプロジェクト内部で、ファイル名をどうするか、ディレクトリ名をどうするかなどの決まり事をチェックできる機能であるmix formatのオプションファイルでしょうか。
チーム内で開発してる時にファイル名をどうするだとか決まり事を書くのに使うのかもしれません。(使い方次第ではキャメルケースだの指定可能?)
よく分からないので必要になるまで放置!

Environments

Mixでコンパイルする際に環境毎に機能を切り替える機能も備わっているようです。
つまり開発時には自社内のプライベート空間にあるサーバー(192.168.200.xなど)を参照するようにして、リリースする際にはグローバルなサーバー(101.142.xxx.yyyなど)を参照する様にする機能だと思います。
主に3種類あり

  • :dev 開発用
  • :test テスト用
  • :prod リリース用(in production?)

となっております。

この環境で切り替わる機能はMix.envという関数で取得できるそうで、例としてプロジェクト直下のmix.exsが挙げられています。

      start_permanent: Mix.env() == :prod,

たしかに書かれてる。
このstart_permanent:というオプションはおそらくこのアプリケーションがなんらかのトラブルでクラッシュしても永遠に再起動するようにするオプションだと思います。
まさにリリース用のオプション・・・
この例から分かるように、環境毎に機能を切り分けたいときはMix.env()で場合分けすれば良さそうです。

また、mixで環境を切り替えてコンパイルするには以下の通り

MIX_ENV=prod mix compile
# Windowsはこっち
> set "MIX_ENV=prod" && mix compile

Exploring

mixで困ったらmix helpをしてみると良い。コマンド一覧とちょっとした説明はこれで出てきます。
より詳しい説明はドキュメントかmixのソースを見るのがよさそう。

hexdocs.pm