どういうときに使うと便利か
外部ライブラリが絡むようなバグの最小再現コード,あるいはすぐに試せるサンプルを置くようなとき.
そういったコードは
- 今後コミットを重ねていくつもりがあまりない
- 外部ライブラリに依存を持つ
という特徴を持つ.
1 を考えると,Github のリポジトリとして登録するほどでもない.
2 を考えると,単なるコード片だけだとライブラリのインストールや依存関係を解決できない.
そういったものを
% git clone https://example.com/foo.git
% cd foo
foo% mix deps.get
foo% mix run foo.exs
といったコマンドを順番に打つだけで内容を再現できると,伝えたい相手が試しやすい.
Gistの制限
ディレクトリ構造を作れない.
以前は作れたようなのだが,現在はファイルのみを置ける.
GistへアップロードするElixirのプロジェクトを準備する
例として,外部ライブラリ HTTPoison を利用した,すぐに実行できるようなサンドボックスを Gist に置く
まずは通常と同じように mix new foo
し,そこから不要なファイルを削除する.
% mix new httpoison_sandbox
% cd httpoison_sandbox/
httpoison_sandbox% ls
README.md config lib mix.exs test
httpoison_sandbox% rm -rf README.md config lib test
httpoison_sandbox% ls
mix.exs
ここで mix.exs の 2 箇所を編集する.
一つは,deps へと依存関係を追加する.これは普通の Elixir プロジェクトでも行っているものだ.
もう一つは,ルートディレクトリにあるソースコードをプロジェクトのソースコードとして利用するため,コンパイルパス :elixirc_paths を追加する.この設定をしないと,mix は lib
以下からソースコードを探してしまうため,ルートディレクトリにあるソースコードを見つけられず,コンパイルエラーになる.
defmodule HttpoisonSandbox.Mixfile do
use Mix.Project
def project do
[app: :httpoison_sandbox,
version: "0.1.0",
elixir: "~> 1.4",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
elixirc_paths: ["."], # ソースコードを読み込むパスを変更するため,追加
deps: deps()]
end
def application do
[extra_applications: [:logger]]
end
defp deps do
[
{:httpoison, "~> 0.10.0"} # 依存関係を示すため,追加
]
end
end
次に実行したいソースコードを新規追加する.
HTTPoison.start
result = HTTPoison.get! "http://example.com"
IO.inspect result
これで動作させられる最小の構成が完成した.
httpoison_sandbox% ls
httpoison_sandbox.exs mix.exs
httpoison_sandbox% mix deps.get
httpoison_sandbox% mix run
%HTTPoison.Response{body: "<!doctype html>\n<html>\n<head>\n <title>Example Domain</title>\n\n <meta charset=\"utf-8\" />\n <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <style type=\"text/css\">\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 50px;\n background-color: #fff;\n border-radius: 1em;\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (max-width: 700px) {\n body {\n background-color: #fff;\n }\n div {\n width: auto;\n margin: 0 auto;\n border-radius: 0;\n padding: 1em;\n }\n }\n </style> \n</head>\n\n<body>\n<div>\n <h1>Example Domain</h1>\n <p>This domain is established to be used for illustrative examples in documents. You may use this\n domain in examples without prior coordination or asking for permission.</p>\n <p><a href=\"http://www.iana.org/domains/example\">More information...</a></p>\n</div>\n</body>\n</html>\n",
headers: [{"Cache-Control", "max-age=604800"}, {"Content-Type", "text/html"},
{"Date", "Wed, 18 Jan 2017 13:46:17 GMT"},
{"Etag", "\"359670651+gzip+ident\""},
{"Expires", "Wed, 25 Jan 2017 13:46:17 GMT"},
{"Last-Modified", "Fri, 09 Aug 2013 23:54:35 GMT"},
{"Server", "ECS (rhv/818F)"}, {"Vary", "Accept-Encoding"}, {"X-Cache", "HIT"},
{"x-ec-custom-error", "1"}, {"Content-Length", "1270"}], status_code: 200}
この2つのファイルをGistへアップロードして完成だ.
に用意した.
GistへアップロードしたElixirプロジェクトを利用する
GistへアップロードしたElixirプロジェクトが意図通り利用できるか検証する.
% git clone https://gist.github.com/niku/1fa19416421214ab78987a6c1e8ae93d
Cloning into '1fa19416421214ab78987a6c1e8ae93d'...
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (4/4), done.
% cd 1fa19416421214ab78987a6c1e8ae93d
1fa19416421214ab78987a6c1e8ae93d% ls
httpoison_sandbox.exs mix.exs
% mix do deps.get, run httpoison_sandbox.exs
(...snip...)
%HTTPoison.Response{body: "<!doctype html>\n<html>\n<head>\n <title>Example Domain</title>\n\n <meta charset=\"utf-8\" />\n <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <style type=\"text/css\">\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 50px;\n background-color: #fff;\n border-radius: 1em;\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (max-width: 700px) {\n body {\n background-color: #fff;\n }\n div {\n width: auto;\n margin: 0 auto;\n border-radius: 0;\n padding: 1em;\n }\n }\n </style> \n</head>\n\n<body>\n<div>\n <h1>Example Domain</h1>\n <p>This domain is established to be used for illustrative examples in documents. You may use this\n domain in examples without prior coordination or asking for permission.</p>\n <p><a href=\"http://www.iana.org/domains/example\">More information...</a></p>\n</div>\n</body>\n</html>\n",
headers: [{"Cache-Control", "max-age=604800"}, {"Content-Type", "text/html"},
{"Date", "Wed, 18 Jan 2017 13:59:28 GMT"},
{"Etag", "\"359670651+gzip+ident\""},
{"Expires", "Wed, 25 Jan 2017 13:59:28 GMT"},
{"Last-Modified", "Fri, 09 Aug 2013 23:54:35 GMT"},
{"Server", "ECS (rhv/818F)"}, {"Vary", "Accept-Encoding"}, {"X-Cache", "HIT"},
{"x-ec-custom-error", "1"}, {"Content-Length", "1270"}], status_code: 200}
準備したときの実行結果とほぼ同じ(時間などを除くと同じ)内容が出力できている.
これで Gist 上に Elixir プロジェクトを置くことができているといえる.
再現性の向上(optional)
もしバグの再現コードとして利用する場合は,mix deps.get
したときに追加される,依存関係を固定するファイル mix.lock
を含めて,3 ファイルを Gist へ置いておけば,依存ライブラリのバージョンも固定され,より再現しやすい.