$30 off During Our Annual Pro Sale. View Details »

Power Assert Inside in Elixir

Power Assert Inside in Elixir

Slides at Elixir Meetup #1 in Drecom
http://beam-lang.connpass.com/event/23013/

Takayuki Matsubara

January 11, 2016
Tweet

More Decks by Takayuki Matsubara

Other Decks in Programming

Transcript

  1. Power Assert Inside
    in Elixir

    View Slide

  2. self-introduction
    • Takayuki Matsubara
    • ma2gedev @github
    • ma2ge @twitter
    • Application Engineer @
    M3, Inc.
    • Rails/Java/JavaScript
    application
    • Ruby: breadcrumble,
    chrono_logger, bundle-star
    • Elixir: power_assert_ex
    • HR7@MHX

    View Slide

  3. precondition
    Understood
    • the basic syntax of Elixir
    • about Macro

    View Slide

  4. github.com/ma2gedev ⭐

    View Slide

  5. What is Power Assert?

    View Slide

  6. Test code
    test "Enum.at should return the element at the given index" do
    array = [1, 2, 3, 4, 5, 6]; index = 2; two = 2
    assert array |> Enum.at(index) == two
    end

    View Slide

  7. in ExUnit
    1) test Enum.at should return the elem...
    test/power_assert_sample_test.exs:5
    Assertion with == failed
    code: array |> Enum.at(index) == two
    lhs: 3
    rhs: 2
    stacktrace:
    test/power_assert_sample_test.exs:7

    View Slide

  8. in Power Assert
    1) test Enum.at should return the elem...
    test/power_assert_sample_test.exs:5
    array |> Enum.at(index) == two
    | | | |
    | 3 2 2
    [1, 2, 3, 4, 5, 6]
    stacktrace:
    test/power_assert_sample_test.exs:7

    View Slide

  9. failed results with
    descriptive message

    View Slide

  10. • Power Assert (Groovy, Spock framework)
    • power-assert-js (JavaScript)
    • power_assert (Ruby)
    • power-assert-rs (Rust)
    • vital-power-assert (Vim script)
    • PowerAssert.Net (.Net)
    • power_assert.cr (Crystal)
    • PAssert (Swift)
    • PowerAssertEx (Elixir)

    View Slide

  11. • Power Assert (Groovy, Spock framework)
    • power-assert-js (JavaScript)
    • power_assert (Ruby)
    • power-assert-rs (Rust)
    • vital-power-assert (Vim script)
    • PowerAssert.Net (.Net)
    • power_assert.cr (Crystal)
    • PAssert (Swift)
    • PowerAssertEx (Elixir)

    View Slide

  12. PowerAssertEx Concept
    • Simple - provide only assert method
    • Reversible - can go back to ExUnit

    View Slide

  13. PowerAssertEx |> expose

    View Slide

  14. Point
    # test code(call `assert` method same as ExUnit)
    assert [1, 2, 3] |> Enum.reverse() |> Enum.sum() == 5
    # output(need positions and values)
    [1, 2, 3] |> Enum.reverse() |> Enum.sum() == 5
    | |
    [3, 2, 1] 6

    View Slide

  15. tl;dr
    • assert
    • get positions and values
    • output

    View Slide

  16. tl;dr
    • assert
    • get positions and values
    • output

    View Slide

  17. review about Elixir's
    macro

    View Slide

  18. how to handle AST
    defmacro assert(ast) do
    # receive an elixir's Abstract Syntax Tree
    end

    View Slide

  19. AST in Elixir
    iex> quote do: sum(1, 2)
    {:sum, [], [1, 2]}
    iex> quote do: x
    {:x, [], Elixir}
    iex> quote do: Enum.sum(1, 2)
    {{:., [], [{:__aliases__, [alias: false], [:Enum]}, :sum]}, [], [1, 2]}

    View Slide

  20. change AST into string of code
    iex> Macro.to_string(quote do: Enum.sum(1, 2))
    "Enum.sum(1, 2)"

    View Slide

  21. Positions and Values

    View Slide

  22. Positions
    [1, 2, 3] |> Enum.reverse() |> Enum.sum()
    | |
    [3, 2, 1] 6

    View Slide

  23. How to detect
    • traverse AST for collecting position each
    expression
    • detect position
    • convert each expression's AST to string of
    code
    • detect position with above string of code

    View Slide

  24. target
    iex> quote do: [1, 2, 3] |> Enum.reverse() |> Enum.sum()
    {:|>, [context: Elixir, import: Kernel],
    [{:|>, [context: Elixir, import: Kernel],
    [[1, 2, 3],
    {{:., [], [{:__aliases__, [alias: false], [:Enum]}, :reverse]}, [], []}]},
    {{:., [], [{:__aliases__, [alias: false], [:Enum]}, :sum]}, [], []}]}

    View Slide

  25. traverse
    Macro.prewalk(ast, acc, fun)
    Macro.postwalk(ast, acc, fun)
    Macro.traverse(ast, acc, pre, post)
    # traverse/4 enabled after Elixir v1.2.0
    `fun` is function and receives AST each expression

    View Slide

  26. Enum.sum(1,2,3)'s AST

    View Slide

  27. prewalk

    View Slide

  28. postwalk

    View Slide

  29. traverse with Macro.postwalk
    iex> Macro.postwalk(quote(do: [1, 2, 3] |> Enum.reverse() |> Enum.sum()), fn(ast) -> IO.inspect ast end)
    1
    2
    3
    [1, 2, 3]
    :Enum
    {:__aliases__, [alias: false], [:Enum]}
    :reverse
    {:., [], [{:__aliases__, [alias: false], [:Enum]}, :reverse]}
    {{:., [], [{:__aliases__, [alias: false], [:Enum]}, :reverse]}, [], []}
    {:|>, [context: Elixir, import: Kernel],
    [[1, 2, 3],
    {{:., [], [{:__aliases__, [alias: false], [:Enum]}, :reverse]}, [], []}]}
    :Enum
    {:__aliases__, [alias: false], [:Enum]}
    :sum
    {:., [], [{:__aliases__, [alias: false], [:Enum]}, :sum]}
    {{:., [], [{:__aliases__, [alias: false], [:Enum]}, :sum]}, [], []}
    {:|>, [context: Elixir, import: Kernel],
    [{:|>, [context: Elixir, import: Kernel],
    [[1, 2, 3],
    {{:., [], [{:__aliases__, [alias: false], [:Enum]}, :reverse]}, [], []}]},
    {{:., [], [{:__aliases__, [alias: false], [:Enum]}, :sum]}, [], []}]}

    View Slide

  30. filtered by pattern match
    # we need function call
    {{:., [], [{:__aliases__, [alias: false], [:Enum]}, :reverse]}, [], []}
    # |> Macro.to_string
    # "Enum.reverse()"
    # disregard
    [1, 2, 3]

    View Slide

  31. convert AST to string of code and find
    position with regex
    code = {{:., [], [{:__aliases__, [alias: false], [:Enum]}, :reverse]}, [], []}
    |> Macro.to_string
    # "Enum.reverse()"
    # find position with `Regex.scan` by `code`

    View Slide

  32. Values
    [1, 2, 3] |> Enum.reverse() |> Enum.sum()
    | |
    [3, 2, 1] 6

    View Slide

  33. How to collect values
    • traverse AST
    • inject an AST to collect values
    • collect values at runtime

    View Slide

  34. inject an AST
    Macro.postwalk(ast, acc, fun)
    • fun function can return modified AST

    View Slide

  35. inject an AST to collect values
    # before
    Enum.reverse([1, 2, 3])
    # after injected an AST
    (
    v = Enum.reverse([1, 2, 3])
    Agent.update(buffer, &[[pos, v] | &1])
    v
    )

    View Slide

  36. Agent!!!
    # after injected an AST
    {:ok, buffer} = Agent.start_link(fn -> [] end)
    ...
    (
    v = Enum.reverse([1, 2, 3])
    Agent.update(buffer, &[[pos, v] | &1])
    v
    )
    ...
    values = Agent.get(buffer, &(&1))
    Agent.stop(buffer)

    View Slide

  37. inject an AST when |> operator
    # before
    [1, 2, 3] |> Enum.reverse
    # after injected an AST
    [1, 2, 3] |> (
    v = Enum.reverse # <= Does not work!!!
    Agent.update(buffer, &[[pos, v] | &1])
    v
    )

    View Slide

  38. modify an AST when |> operator
    # before
    [1, 2, 3] |> Enum.reverse
    # after injected an AST
    l_value = [1, 2, 3]
    (
    v = Enum.reverse(l_value) # <= inject `l_value`
    # into first argument of rhs
    Agent.update(buffer, &[[pos, v] | &1])
    v
    )

    View Slide

  39. Positions and Values

    View Slide

  40. summary
    • traverse AST
    • analyze AST and detect position of expression
    • inject AST to collect value and get result of
    expression

    View Slide

  41. ⌚ hereafter
    • please use power assert
    • welcome any feedback

    View Slide

  42. ! Resources - PowerAssertEx
    • PowerAssertEx
    • github.com/ma2gedev/powerassertex
    • hex.pm/packages/power_assert

    View Slide

  43. ! Resources - Books
    • Programming Elixir
    • pragprog.com/book/elixir/programming-
    elixir
    • Metaprogramming Elixir
    • pragprog.com/book/cmelixir/
    metaprogramming-elixir
    • defmacro
    • leanpub.com/defmacro

    View Slide

  44. ! Resources - Web
    • macro with elixir
    • www.slideshare.net/k1complete/elixir-
    macroinaction1-14083087
    • Understanding Elixir Macros, Part 1 - Basics
    • theerlangelist.com/article/macros_1

    View Slide

  45. ! Resources - Web
    • power-assert, mechanism and philosophy
    • www.slideshare.net/t_wada/power-assert-
    nodefest-2014
    • Power Assert in Ruby
    • speakerdeck.com/k_tsj/power-assert-in-
    ruby

    View Slide

  46. END

    View Slide