スポンサーリンク

2016年7月8日

[Elixir]夏コミ用の内容を一部公開

Goal

  • 自作のユニットテスト用パッケージ(kinotake)をPhoenixで使う
  • 2016年度の夏コミ用の内容を一部公開

Dev-Environment

OS: Windows8.1
Erlang/OTP: v19.0 (EShell v8)
Elixir: v1.3.0
Phoenix: v1.2

Context

夏コミ用に執筆している本の一部を先行して公開します。
公開している内容は、自作パッケージをPhoenixで使っている例になります。
問題があったら消すかもしれません。購入の参考になったら幸いです。
今回は100ページいかないかもしれません・・・(残念)
  • プロジェクトの作成
まずはプロジェクトを作成し、サーバの起動までを確認します。

Example:

> cd path/to/project

> mix phoenix.new example
...

> mix ecto.create
...

> mix phoenix.server
...

Ctrl+C
サーバを起動しhttp://localhost:4000へアクセスします。
Phoenixのウェルカムページが表示されていれば問題ありません。
  • Userモデルの作成
せっかくなのでユーザモデルなどを作成し、少し遊びます。

Example:

> mix phoenix.gen.html User users name:string email:string
...
ユーザ用のルーティングを追加します。

File: web/router.ex

defmodule KinotakeExampleInPhoenix.Router do
  ...

  scope "/", KinotakeExampleInPhoenix do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
    resources "/users", UserController
  end

  ...
end
マイグレーションを実行し、DBへ反映します。

Example:

> mix ecto.migrate
...
  • パッケージの追加
パッケージを利用するため、mix.exsファイルを変更します。
depsへの追加とアプリケーション起動時に立ち上げるためにapplicationsへも追加します。

File: mix.exs

defmodule KinotakeExampleInPhoenix.Mixfile do
  ...

  def application do
    [mod: {KinotakeExampleInPhoenix, []},
     applications: [:phoenix,
                    :phoenix_pubsub,
                    :phoenix_html,
                    :cowboy,
                    :logger,
                    :gettext,
                    :phoenix_ecto,
                    :postgrex,
                    :kinotake]] ## Add
  end

  ...

  defp deps do
    [{:phoenix, "~> 1.2.0"},
     {:phoenix_pubsub, "~> 1.0"},
     {:phoenix_ecto, "~> 3.0"},
     {:postgrex, ">= 0.0.0"},
     {:phoenix_html, "~> 2.6"},
     {:phoenix_live_reload, "~> 1.0", only: :dev},
     {:gettext, "~> 0.11"},
     {:cowboy, "~> 1.0"},
     {:kinotake, github: "darui00kara/kinotake", branch: "master"}] ## Add
  end

  ...
end
パッケージを取得します。

Example:

> mix deps.get
...
  • ユニット用のモジュールを作成
ユニットテスト用のモジュールを作成します。
実行条件は、ユーザのidが1であるときに実行するように設定しています。
実行内容は、表示するユーザの名前を変更するだけです。

File: lib/user_unit.ex

defmodule UserUnit do
  use KinoTake.Unit

  active_if [user: user] do
    user.id == 1
  end

  function(:show, [user: user]) do
    Map.put(user, :name, "unit")
  end
end
  • invokeしてみる
UserControllerのshowアクションへ組み込みます。
use(Invokerモジュール)を追加、invoke処理の追加をしています。

File: web/controllers/user_controller.ex

defmodule KinotakeExampleInPhoenix.UserController do
  use KinotakeExampleInPhoenix.Web, :controller
  use KinoTake.Invoker

  ...

  def show(conn, %{"id" => id}) do
    user = Repo.get!(User, id)

    user = invoke(UserUnit, :show, [locals: [user: user]]) do
      Map.put(user, :name, "default")
    end

    render(conn, "show.html", user: user)
  end

  ...
end
ようやっと実行です。サーバを起動しましょう。

Example:

> mix phoenix.server
...
ユーザを追加する機能も準備の段階で作成されていますので、
ユーザに関しては適当に追加してください。2人ほど追加すれば十分です。
作成したら、ユニットが動作するid:1のユーザを参照してみます。
ユーザの名前がunitに変わっています。
次に、ユニットが動作しないid:2のユーザを参照してみます。
ユーザの名前がdefaultに変わっています。
どうやらちゃんと処理が分けられているようです。やったね!

Bibliography

なし

2016年6月25日

[Elixir]マクロでdo~end記述を作る際の例

とある錬金術師の万能薬(Elixir)

Goal

  • Elixirのマクロでdo~end記述を作る際のテンプレート

Dev-Environment

OS: Windows8.1
Erlang: OTP 19.0(Eshell v8.0)
Elixir: 1.3.0

Text

マクロでdo~end記述をできるようにする際、
同一の記述を何回も使ったのでテンプレートとして残しておく。
参考にしたのはExUnit(v1.2.5)のCaseマクロです。

Example:

defmodule DoEndMacroTemplate do
  defmacro do_block(contents) do
    contents =
      case contents do
        [do: block] ->
          quote do
            unquote(block)
          end
        _ ->
          quote do
            try(unquote(contents))
          end
      end

    contents = Macro.escape(contents, unquote: true)

    quote bind_quoted: binding do
      def get_contents_result(), do: unquote(contents)    end  endend

Example:

defmodule UseExample do
  use DoEndMacroTemplate

  do_block do
    true
  end
end

Example:

iex> UseExample.get_contents_result
true
ただのメモです。
誰かの役に立ったら幸いです。

Bibliography

なし

2016年5月11日

[Elixir]Play with the defdelegate

とある錬金術師の万能薬(Elixir)

Goal

  • defdelegateで標準のモジュールを利用する
  • defdelegateで自作のモジュールを利用する

Dev-Environment

  • OS: Windows8.1
  • Erlang: Eshell V7.3, OTP-Version 18.3
  • Elixir: v1.2.3

Play with the defdelegate

まずは標準で用意されているモジュールをデリゲートで使ってみる。
今回は、Enumのreverseをデリゲートで使ってみよう。

File: delegate_sample.ex

defmodule DelegateSample do
  defdelegate reverse(enumerable), to: Enum
  defdelegate other_reverse(enumrable), to: Enum, as: :reverse
end
  • “to:”でデリゲートしたいモジュールを指定する
  • “as:”でデリゲートする関数を別名で使う場合に元となる関数を指定する

Note:

非推奨(?)になるみたいなので、"append_first:"オプションは割愛しています。

参考: http://d.hatena.ne.jp/hayabusa333/20160116/elixir_changes_20160115
参考: https://github.com/elixir-lang/elixir/issues/4199

Example:

iex> DelegateSample.reverse([1,2,3])
[3, 2, 1]
iex> DelegateSample.other_reverse([1,2,3])
[3, 2, 1]
無事実行できました。
続けて、自作のモジュールをデリゲートで使ってみたいと思います。

File: sample.ex

defmodule Sample do
  def sample do
    IO.puts "sample"
  end
end

File: delegate_sample.ex

defmodule DelegateSample do
  ...

  ## My Module
  defdelegate sample(), to: Sample
  defdelegate other_sample(), to: Sample, as: :sample
end

Example:

iex> DelegateSample.sample
sample
:ok
iex> DelegateSample.other_sample
sample
:ok

Note:

defdelegateはpublic扱いになる。
そのため、privateで使いたいのであればimportで対応をする。
複数のデリゲートを一度に定義はできるのでしょうか?
検証してみましょう。

File: sample.ex

defmodule Sample do
  ...

  def sample2 do
    IO.puts "sample2"
  end

  def sample3 do
    IO.puts "sample3"
  end
end

File: delegate_sample.ex

defmodule DelegateSample do
  ...

  defdelegate [sample2(), sample3()], to: Sample
end

Example:

iex> DelegateSample.sample2()
sample2
:ok
iex> DelegateSample.sample3()
sample3
:ok
問題なく定義できるみたいです。
もう一つ検証してみましょう。
別名は一度につけられるのか?(これは無理だと思いますが・・・)

File: delegate_sample.ex

defmodule DelegateSample do
  ...

  defdelegate [other_sample2(), other_sample3()], to: Sample, as: :sample2, as: :sample3
end
うん、まぁコンパイルは通りますよね。
記法上間違いがあるわけじゃないですし。

Example:

iex> DelegateSample.other_sample2()
sample2
:ok
iex> DelegateSample.other_sample3()
sample2
:ok
実行はできるけど、sample2/0の方が実行されてしまったみたいです。
以上!
誰かの役に立ったなら幸いです。m( )m

Bibliography

2016年5月10日

[Elixir]Play with the Elixir-Macro

とある錬金術師の万能薬(Elixir)

Goal

  • マクロのさび落とし
  • 見た目の記法だけChankoっぽいもの(偽物)を実装する(内部実装は・・・)

Dev-Environment

  • OS: Windows8.1
  • Erlang: Eshell V7.3, OTP-Version 18.3
  • Elixir: v1.2.3

Play with the Elixir-Macro

A/Bテストをやろうと思っていたのに、気が付いたらマクロでやっていました。
(機能の一部で)見た目の記述だけChankoっぽいもの(完全な偽物)を実装してみました。
あくまで使うときの見た目だけなので、実装に関しては残念ながら微妙です。
結局、内部でtry~rescueとifを使って処理しています。
本気でやるならブラッシュアップが必要ですね。
最初のとっかかりとしては、まぁこんなもんでしょう。(残当)
それでは、やっていきます。

Module: Unit

テスト用のモジュールで使うマクロを定義したモジュールです。
  • active_if/1: 実行するのかしないのかのフラグを返すだけの関数をマクロで展開しているだけです。
  • function/2: 関数を動的にマクロで展開しているだけです。func_nameが関数名になります。

File: unit.ex

defmodule Unit do
  defmacro __using__(_options) do
    quote do
      import unquote(__MODULE__)
    end
  end

  defmacro active_if(condition) do
    quote do
      def active? do
        unquote(condition)
      end
    end
  end

  defmacro function(func_name, clauses) do
    if func_name do
      quote do
        def unquote(String.to_atom "#{func_name}")() do
          Keyword.get(unquote(clauses), :do, nil)
        end
      end
    end
  end
end

Module: TestControllerUnit

サンプルとしてUnitモジュールを使ってみたモジュールです。
Unitモジュールを実際に使ってみるとこんな感じになります。

File: test_controller_unit.ex

defmodule TestControllerUnit do
  use Unit

  active_if true

  function(:show) do
    d = 5
    e = d + 6
    f = e + 7
  end
end

Module: Helper

補助関数を定義しているモジュールです。
  • exec/2, exec/3, atom_to_module_string/1: モジュールがatomのままだとapplyで実行できないため、補助関数として作りました。

File: helper.ex

defmodule Helper do
  def exec(module, fun) do
    exec(module, fun, [])
  end
  def exec(module, fun, []) do
    atom_to_module_string(module) |> String.to_atom |> apply(fun, [])
  end
  def exec(module, fun, args) when is_list(args) do
    atom_to_module_string(module) |> String.to_atom |> apply(fun, args)
  end

  def atom_to_module_string(atom) do
    "Elixir." <> (Atom.to_string(atom) |> Mix.Utils.camelize)
  end
end

Module: Invoker

結局、内部でtry~rescueとifを使って、
実コードにてマクロが展開されているだけになってしまいました。
  • invoke/3: テスト対象のモジュールと関数を指定して、デフォルトの処理をdo~endで記述する。

File: invoker.ex

defmodule Invoker do
  defmacro __using__(_options) do
    quote do
      import unquote(__MODULE__)
      import Helper
    end
  end

  defmacro invoke(module_name, func_name, clauses) do
    quote do
      do_clause = Keyword.get(unquote(clauses), :do, nil)

      try do
        if exec(unquote(module_name), :active?) do
          exec(unquote(module_name), unquote(func_name))
        else
          do_clause
        end
      rescue
        _any -> do_clause
      end
    end
  end
end

Module: TestController

サンプルとしてInvokerモジュールを使ってみたモジュールです。
Invokerモジュールを実際に使ってみるとこんな感じになります。

File: test_controller.ex

defmodule TestController do
  use Invoker

  def show do
    invoke(:test_controller_unit, :show) do
      a = 1
      b = a + 2
      c = b + 3
    end
  end
end

Example:

## active_if: true
iex> TestController.show
18

## active_if: false
iex> TestController.show
6
TestControllerUnitでraiseを起こしてみると、rescueの処理が実行される。

Example:

defmodule TestControllerUnit do
  ...

  function(:show) do
    d = 5
    e = d + 6
    raise "oops"
    f = e + 7
  end
end
iex> TestController.show
6
もう少しtry~rescueやifのあたりを何とかしたかったのですが、
マクロの動作に翻弄されて、そこまで手が回りませんでした。
あまり参考になるコードではないと思いますが、誰かの役に立ったなら幸いです。m( )m

Bibliography

2016年5月5日

What is A/B-Test?

Goal

  • A/B-Testについて調査する

Dev-Environment

  • OS: Windows8.1

What is A/B-Test?

A/Bテストをやりたいと思いました。
しかし、A/Bテストと言うものをよく知らない(笑)
そういうわけで知らないなら調べればいいじゃない!
今回は、A/Bテストについて調べた調査記録と言うことになります。
テストについて素人以前の人物がまとめた内容になりますので、
ご指摘等あると思います。その際はコメントにてご教授いただけると幸いです。

A/B-Test is 何?

A/Bテスト・・・それは可能性の検証・・・って書くと少しかっこいい気がする(笑)
下らない妄言は置いておくとして、早速本題に入りましょう。

A/Bテストとは、2つある選択肢のうち、どちらが良い結果をもたらすか見極めること。

別名としてスプリットテスト、スプリットランテストとも言う。(個人的にはこちらの方が好き)

※AとB以外にもテストを用意して行うのは多変量テストと呼ばれている
色々なサイトを見て大体どこにも載っていたの内容があった。

「小さく始める」

なんだが、マイクロサービスやスクラムのようですね(笑)
実際、ある重要な業務においてやったこともない人をいきなりアサインするでしょうか?
いや、しませんね・・・少なくとも私はそうします。もし新しい人を導入するなら小さく補助付きでやらせます。(状況が許せば)
それと同じく、サイトの重要な部分にいきなり着手するのではなく、
ごく些細な部分から始めていくことが大事なようです。
これには別の意味もあると思います。
それは、まず効果を観測/測定すると言うことです。
効果をデータとして出力され観測/測定してから、追加でテストをしていくと言うことですね。

A/B-Test の Point

  • AとBにおける要素の違いを明確にする
例) 女性と男性でメッセージを分けるなど
  • テスト対象とするユーザの条件を明確に分類し行う
全てのユーザに行っても正確なデータが取れるか分からないし、
データを取得するためにかかる時間も多くなるでしょう。
A/Bテストを行うための格言

辛抱強く数%の小さい改善を繰り返し、最終的に数十%の効果にする

つまり忍耐強く耐えましょうってことですね。
すぐには効果が出るかは分かりません。
そのため、単発で終わらせてはいけないと言うことです。
  1. 部長を説得してA/Bテスト導入
  2. しかし、あまり効果は上がらない・・・
  3. 部長「効果が出ないじゃないか、これは意味がないな中止しろ」
  4. A/Bテスト終わり
これはダメですね(笑)
そも前提として、本当に導入する意味はあるのか?っという問題もあるでしょうが・・・

取りとめもない独り言

単純にやるだけなら簡単だと思います。(実際、ツールやライブラリは探せば割とすぐ発見できます)
しかし、効果を出そうとするのが難しいのは別の要因があると思います。
意思決定と同じで評価基準となる部分が話を難しくしているのだと考えます。
データとして結果が出た・・・でも、その解析を間違えていたら?
AとBを用意した・・・それは本当に意味のあるAとBですか?
辛抱強く改善を繰り返していける環境なのか?
などなど、プログラムの部分ではないことが多いのではないかと感じました。
効果や分析については、あらかじめ本や論文で勉強し、
効果のあるパターン(?)のようなものを学ぶのが手っ取り早いと思います。
職場環境、人間関係は・・・私があまりコミュニケーション能力ないので、なんとも言えませんね。(汗)
以上、少しでも誰かの参考になったら、この上ない喜びです><

Bibliography

人気の投稿