はてなブログでElixirのコードにシンタックスハイライトを適用する方法

2015/11/18 時点で、ここを見る限りElixir のシンタックスハイライトは未対応。

でも、大丈夫 Elixir と Ruby は見た目が似ていると言われている。ruby を指定しておけばそれっぽくハイライトされる。

defmodule, defmacro などなど Elixir にしかないキーワードも多いけれど、ないよりかはマシな気がする。

HTTPリクエストからChannelへメッセージを送る

GETやPOSTなどのHTTPリクエストを受け取り、その処理の中でChannelにデータを送信したい場合、以下のように書くことでメッセージを送信できる。

HelloPhoenix.Endpoint.broadcast_from! self(), "rooms:lobby", "new_msg", %{uid: uid, body: body}

http://hexdocs.pm/phoenix/Phoenix.Channel.html の "Broadcasting to an external topic" の箇所に書いてあったのを ここ に合わせて少し変更した。そこの例では channel 側のコードとして書かれているけど、controller 側のコードの中でも使えた(まあ、*.Endpoint モジュールの関数なので当たり前?)。

ちなみに、これで何をしたかったのかというと、fluentd でログを転送して、それをブラウザでリアルタイムで見れるようにしたかった。

こんな感じ。

       +---------+     +---------+
Log -> | fluentd |     | Browser |
       +---------+     +---------+
           |                A
           |                | 
           +-- HTTP(POST)   +-- Websocket
           |                |
           V                |
       +-----------------------+
       |         Phoenix       |
       +-----------------------+

コマンドラインの引数を得る方法

Elixirでコマンドラインツールを書く方法が(自分の観測範囲内では)二通りある。

一つはスクリプトとして実行する方法。もう一つは、escript を使う方法(こっちの方法も名前からするとスクリプトとして実行されてるっぽいけど)。

スクリプトとして実行した場合、System.argv/0 を使えばいつでもコマンドに渡された引数を得ることができるけれど、escript を使った場合は System.argv は nil を返してくる。

ということのメモ。

スクリプトとして実行した場合

ソース

defmodule RunScript do
    def go(argv) do
        IO.inspect argv
        IO.inspect System.argv
    end
end

RunScript.go(System.argv)

実行

$ elixir run_script.exs one two three
["one", "two", "three"]
["one", "two", "three"]

escriptを使った場合

ソース

defmodule RunEscript do
    def main(argv) do
        IO.inspect argv
        IO.inspect System.argv
    end
end

実行

$ ./run_escript one two three
["one", "two", "three"]
nil

参考

escript を使った書き方は以下のページを参考にした。

Elixirで正規表現

Elixir の正規表現Regexモジュール を使う。

仕事ではPHPを書くことが多い。PHP正規表現PCRE関数 が標準的だろう。普段使うPCRE関数をElixirのRegex で置き換えるとしたらどうなるのか?

比較の対象は preg-matchpreg_replace の二つにする。理由は、その二つをよく使うので (その二つくらいしか使わないとも言う)。

preg_match と比べて
単純なマッチ

単純にマッチするかどうかを判定する場合, Regex.match?/2 を使う。

<?php
echo preg_match('/foo/', 'foo');
// => 1
iex> Regex.match?(~r/foo/, "foo")
true
名前なしキャプチャ

名前なしキャプチャを利用する場合, Regex.run/3 を使う。preg_match はキャプチャした結果は変数の参照を介して受け取る。Regex の場合 run/3 の返り値として受けとる。

一度マッチしたらそこで処理は打ち切られる。打ち切られたくない場合は Regex.scan/3 を使う (PHP なら preg_match_all)。

<?php
preg_match('/c(d)/', 'abcd', $m);
print_r($m);
// => Array
//    (
//        [0] => cd
//        [1] => d
//    )
iex> Regex.run(~r/c(d)/, "abcd")
["cd", "d"]
名前つきキャプチャ

名前つきキャプチャは Regex.named_captures/3。Regex.named_captures/3 はキャプチャされた部分のみの結果になる。

<?php
preg_match('/c(?<foo>d)/', "abcd", $m);
print_r($m);
// => Array
//    (
//        [0] => cd
//        [foo] => d
//        [1] => d
//    )
iex> Regex.named_captures(~r/c(?<foo>d)/, "abcd")
%{"foo" => "d"}
preg_replace と比べて

Regex.replace で置換を行う。引数の順序が違うので慣れで書くと間違う。

<?php
echo preg_replace('/b/', 'd', 'abc').PHP_EOL;
// => adc

echo preg_replace('/\.(\d)$/', '.${1}0', '500.5');
// => 500.50
iex> Regex.replace(~r/b/, "abc", "d")
"adc"

iex> Regex.replace(~r/\.(\d)$/, "500.5", ".\\g{1}0")
"500.50"

感想

PHP でできていたことは Elixir でもできる(まあ、PCRE互換だし)。

PHPの場合はパラメータによって一つの関数の中で機能をon/offしたりする感じだけれど、Elixir は機能ごとに関数を用意する感じ。

Elixirのドキュメント生成方法

mix.ex の deps に ex_docearmark を追加する。バージョンは最新版で。

  defp deps do
    [{:ex_doc, ">= 0.0.0", only: :docs},
     {:earmark, ">= 0.0.0", only: :docs}]
  end

パッケージのダウンロードとコンパイル

$ mix deps.get
$ mix deps.compile

環境変数に docs を設定して、mix docs コマンドを実行する。

$ MIX_ENV=docs mix docs

出来上がり。

ドキュメントを hex に上げる。

$ MIX_ENV=docs mix hex.docs

prefecture_jp というライブラリを作成した

prefecture_jpというライブラリを作成した。Hexにも上げた。

機能

都道府県コードから都道府県名への変換、またはその逆。都道府県コードは JIS X 0401 に対応(0埋め2桁の数字)。

使い方

単体で

都道府県コードから都道府県名を検索

iex> PrefectureJp.find("01").name
"北海道"

iex> PrefectureJp.find(code: "13").name
"東京都"

都道府名から都道府県コードを検索

iex> PrefectureJp.find(name: "東京都").code
"13"

都道府県情報の一覧

iex> PrefectureJp.all
[%PrefectureJp.Prefecture{area: "北海道", code: "01", name: "北海道",
  name_e: "hokkaido", name_h: "ほっかいどう",
  name_k: "ホッカイドウ"},
  .
  .
  .
 %PrefectureJp.Prefecture{area: "九州", code: "47", name: "沖縄県",
  name_e: "okinawa", ...}]
Ectoと一緒に

use PrefectureJp, 都道府県コードを持つカラム名 と書くことで、以下のように都道府県コードから都道府県名を求めることができる。

defmodule Place do
    use Ecto.Model
    use PrefectureJp, :prefecture_code

    schema "places" do
        field :prefecture_code, :string
    end
end
iex> place = %Place{prefecture_code: "23"}
iex> place |> Place.prefecture(:name)
"愛知県"

その他

このライブラリは jp_prefectureに大きな影響を受けている。

Elixir v1.1.0

v1.1.0 がリリースされていた (下書きしているときは、v1.1.0 だったけれど、現在の最新版は v1.1.1)。

機能追加で特記すべきことは、Enum, Dict, Task への関数の追加と、MapSet 型の追加、と書かれている。HashSet よりも速く、今後はSetの実装はMapSetがメインになっていくっぽい。英語がよく理解できていない。ついでに言うと HashSetMapSet の違いもよくわかっていない。

今後は MapSet を使うようにすればいいのかな。