どうも、靖宗です。
この章も引き続きMix and OTP編なので、通読が必要かもしれません。
Elixir入門は一度再編集したほうが分かりやすいし自分の為にもなるかもしれないなぁ。
- External dependencies
- Internal dependencies
- Umbrella projects
- Dependencies within an umbrella project
- Don’t drink the kool aid(盲信するな)
今回は依存関係という感じでしょうか?
そういえばmix.exs
に依存関係を記述するような箇所があったのであの辺でしょう。
TCP周りは一から作って行くのは車輪の再発明なのでTCPの理解をしたいときでなければ辞めておきましょう。
ということでおそらくエコシステム(PythonでいうpipとかRubyでいうgem?)の使い方とかです。
External dependencies
Elixirには外部依存と内部依存という考え方があるそうです。とりあえず外部から。
ErlangとElixirの共通のエコシステムとしてHex Package Managerなるものがあるそうです。なんかどっかで見たことあるなと思ったら、Elixirのドキュメントとかこのページの一部な気がします。
今回はHTTPのAPIが欲しいので、Plug
というプロジェクトをとってきます。予想通りmix.exs
に依存関係を記述していきます。
def deps do [{:plug, "~> 1.0"}] end
mix.exs
に記載するということはこのエコシステムからの取得もmixがやってくれるんでしょうか。デキる奴・・・
~> 1.0
という書き方はVer1.0系列の最新版を取得してくる書き方のようです。
基本的にHexに登録されてるのはstable版なので、開発版を使いたい場合はgithubから直接取得することもできるそうです。
def deps do [{:plug, git: "git://github.com/elixir-lang/plug.git"}] end
mixで依存関係の取得などをするにはmix deps.get
を利用するようです。
PS > mix deps.get Resolving Hex dependencies... Dependency resolution completed: New: [32m mime 1.3.1[0m [32m plug 1.7.2[0m [32m plug_crypto 1.0.0[0m * Getting plug (Hex package) * Getting mime (Hex package) * Getting plug_crypto (Hex package)
とりあえず問題無さそう。
なんにも機能使ってないけどコンパイルできるかやってみる。
PS > mix compile ==> mime Compiling 2 files (.ex) Generated mime app ==> plug_crypto Compiling 4 files (.ex) Generated plug_crypto app ==> plug Compiling 1 file (.erl) Compiling 38 files (.ex) warning: System.stacktrace/0 outside of rescue/catch clauses is deprecated. If you want to support only Elixir v1.7+, you must access __STACKTRACE__ inside a rescue/catch. If you want to support earlier Elixir versions, move System.stacktrace/0 inside a rescue/catch lib/plug/conn/wrapper_error.ex:23 Generated plug app ==> kv Compiling 4 files (.ex) Generated kv app
なんか怒られたけどとりあえず通ってる。
依存関係を取り扱うmixのコマンドはmix help
のなかで参照できるので困ったらそれかドキュメントをみよう。
たぶん大体mix deps.get
とmix deps.update
ぐらいかな?
また、mix.lock
というファイルが作成されるが、これは現在のバージョンなどの依存関係を記録するファイルらしく、開発環境の再現性の為にはわりと重要になってくるかも。
たぶんそこまで意識しなくてもmixがよしなにしてくれる。
Internal dependencies
内部依存に関しては、イメージ的に外部に公開したくない様なライブラリ?モジュール?プロジェクト的なものを指しているそうです。
External/InternalというよりはPublic/Privateというイメージの方がしっくりくるかも。
mixでの対応は主に2種類で、githubのポジトリを使用するか、umbrella projectsを使用するかだそうです。umbrella projectsに関しては次の項目で。
def deps do [{:kv, git: "https://github.com/YOUR_ACCOUNT/kv.git"}] end
たぶん参照先さえ間違ってなければgitlabでもいけそう。
もしリポジトリがプライベートであれば、アドレスはgit@github.com:YOUR_ACCOUNT/kv.git
と書く必要があるそうです。勿論暗号鍵通信などの認証関係の設定はお忘れ無く。
ただ、なんでもかんでもgithubなどに置くというのはメンテナンス性にしろあまり嬉しくないのも事実。そこでプロジェクトがネストしたような構造?のumbrella projectsがあるそうです。
たぶんkv
のプロジェクトディレクトリに細かいモジュール群的なモノを入れていく感じなんでしょう。
イメージするディレクトリ構造はこのようになってるみたいです。
+ kv_umbrella + apps + kv + kv_server
いままで作ってきたKVモジュールがappsディレクトリ以下に来てるので、新しくプロジェクトを始めるイメージです。
とりあえず進みましょう。
Umbrella projects
umbrella projectsとしてプロジェクトを追加していくのにもmixを使うようです。
KV
を追加したときと同じ様にmix new
を使うそうですが、--umbrella
オプションが必須だそうです。
PS > pwd Path ---- D:\hogehoge\kv\ PS D:\hogehoge\kv> cd .. PS D:\hogehoge> mix new kv_umbrella --umbrella * creating README.md * creating .formatter.exs * creating .gitignore * creating mix.exs * creating apps * creating config * creating config/config.exs Your umbrella project was created successfully. Inside your project, you will find an apps/ directory where you can create and host many apps: cd kv_umbrella cd apps mix new my_app Commands like "mix compile" and "mix test" when executed in the umbrella project root will automatically run for each application in the apps/ directory.
なんかできた。
生成されたディレクトリにもmix.exs
があるが、プロジェクトディレクトリにあるmix.exs
とは少々異なる。
defmodule KvUmbrella.MixProject do use Mix.Project def project do [ apps_path: "apps", start_permanent: Mix.env() == :prod, deps: deps() ] end defp deps do [] end end
project/0
関数のところで既に違う。apps_path: "apps"
なるオプションがありますが、これがumbrella
として動作するようです。
とりあえずサンプルを進めて行きます。
まずはkv_umbrella/apps/
にモジュールを追加していきます。
PS > cd .\kv_umbrella\apps PS > mix new kv_server --module KVServer --sup * creating README.md * creating .formatter.exs * creating .gitignore * creating mix.exs * creating config * creating config/config.exs * creating lib * creating lib/kv_server.ex * creating lib/kv_server/application.ex * creating test * creating test/test_helper.exs * creating test/kv_server_test.exs Your Mix project was created successfully. You can use "mix" to compile it, test it, and more: cd kv_server mix test Run "mix help" for more commands.
ここでmix new kv_server --module KVServer --sup
としていますが、--sup
はsupervision treeを自動的に組み込むオプションだそうです。
mix.exs
のapplication/0
にmod: {KVServer.Application, []}
が追加されており、その参照してるモジュールのKVServer.Application
もKV
でsupervisorを起動するコードを書いた箇所などを全て自動で用意してくれます。
defmodule KVServer.Application do # See https://hexdocs.pm/elixir/Application.html # for more information on OTP Applications @moduledoc false use Application def start(_type, _args) do # List all child processes to be supervised children = [ # Starts a worker by calling: KVServer.Worker.start_link(arg) # {KVServer.Worker, arg}, ] # See https://hexdocs.pm/elixir/Supervisor.html # for other strategies and supported options opts = [strategy: :one_for_one, name: KVServer.Supervisor] Supervisor.start_link(children, opts) end end
childrenにモジュール名と名前を記載するのではなくoptsに直接名前を定義しにいってます。
さて、基本的にmix new
コマンドは--sup
を除いて最初のKVを作成したときと同じですが、mix.exs
の中身が違います。
defmodule KVServer.MixProject do use Mix.Project def project do [ app: :kv_server, version: "0.1.0", build_path: "../../_build", config_path: "../../config/config.exs", deps_path: "../../deps", lockfile: "../../mix.lock", elixir: "~> 1.7-dev", start_permanent: Mix.env() == :prod, deps: deps() ] end # Run "mix help compile.app" to learn about applications def application do [ extra_applications: [:logger], mod: {KVServer.Application, []} ] 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"}, # {:sibling_app_in_umbrella, in_umbrella: true}, ] end end
kv_umbrella/apps
下で実行したことにより、mixがよしなに下記の項目を追加してくれました。
build_path: "../../_build", config_path: "../../config/config.exs", deps_path: "../../deps", lockfile: "../../mix.lock",
ビルドパスなどが全てkv_umbrella
のディレクトリを参照しています。
ここに作成するモジュールは全てkv_umbrella
へコンパイルされるようです。依存関係もkv_umbrella
のディレクトリを参照するようです。
一応追加したKVServer
モジュールが問題無いかテスとしてみます。とはいってもテストはまだ編集してないのでmixなどが正常に動作するかのチェック程度です。
PS kv\kv_umbrella\apps\kv_server> mix test Compiling 2 files (.ex) Generated kv_server app .. Finished in 0.03 seconds 1 doctest, 1 test, 0 failures Randomized with seed 932000
おけまる!
ここまで問題無いのでKVServerにKVモジュールの依存関係を記載していきます。
Dependencies within an umbrella project
apps/kv_server/mix.exs
に依存関係を追記します。
defp deps do [{:kv, in_umbrella: true}] end
これはKVServerがKVモジュールに依存している、つまりKVServerが起動する前にKVモジュールが起動してる必要性があることを意味しています。
依存関係が書けたので、KVモジュールをコピーしてきます。
以下のディレクトリ構造になるようにkv
フォルダごとapps
の中に放り込みます。
+ kv_umbrella + apps + kv # これ + kv_server
ビルドパスなどをkv_server同様に変更します。
apps/kv/mix.exs
をapps/kv_server/mix.exs
と同様に修正。
build_path: "../../_build", config_path: "../../config/config.exs", deps_path: "../../deps", lockfile: "../../mix.lock",
一応mix test
すると、問題無く通ります。
Don’t drink the kool aid(盲信するな)
umbrella projectはモジュール?プロジェクト?の分離管理に訳に立ちそうですが、依存関係などは完全に切り離せている訳ではありません。
完全に切り離したいならapps
ディレクトリから別のプロジェクトフォルダに移してしまってbuild_path
などを削除してしまうのが良さそうです。
そうした後に依存関係の箇所で書いた{:kv, git: "https://github.com/YOUR_ACCOUNT/kv.git"}
の:git
を:path
に変更してパスを指定すれば使えそうな記述があります。
需要が出てきたらやってみたいとは思いますが基本はumbrella projectかそもそもプロジェクト一個で書いてしまうのが良さそうです。
この辺も色々経験してみてから再考した方が良さそうです(´ε`;)