Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

— An async template - Oleksandr Khokhlov | Elixir Club Ukraine

24 views

Published on

Slides of Oleksandr Khokhlov, Founder at Nots.io, at Elixir Club Ukraine, Kyiv, 28.09.2019
Next conference - http://www.elixirkyiv.club/

Description of presentation
Elixir and Erlang platform are all about asynchronous communications. Why don’t we use this feature while rendering templates? In this talk, Oleksandr unveils how we in Nots.io split HTML documents into smaller pieces and render them in parallel.

Follow us on social networks @ElixirClubUA and #ElixirClubUA
Announce and materials from conf - https://www.fb.me/ElixirClubUA
News - https://twitter.com/ElixirClubUA
Photo and free atmosphere - https://www.instagram.com/ElixirClubUA
*Organizer’s chanel - https://t.me/incredevly

Published in: Technology
  • Be the first to comment

  • Be the first to like this

— An async template - Oleksandr Khokhlov | Elixir Club Ukraine

  1. 1. Alexander Khokhlov @nots_ioNots.io — Knock, knock — An async template — Who’s there?
  2. 2. Co-Founder Nots.io 01
  3. 3. 01 • Nots.io Company’s documentation: Hard to find Determine if fits the scope Check whether relevant
  4. 4. Nots.io Add docs for block of code, function, module, file, commit or branch 01
  5. 5. We Track Relevance You always know what’s fresh and what’s not. Promotes keeping docs up-to-date. Rewarding when everything is ✅ 01 • Nots.io
  6. 6. Nots.io case 02 🏗
  7. 7. Choose File page 02 • Nots.io case
  8. 8. In the template 02 • Nots.io case .branch-selector = Notsapp.Renderer.simple_component({:"dropdown/dropdown", …}) %h2 Files .tree = Notsapp.Renderer.simple_component({:"code-tree/tree", …})
  9. 9. 02 • Nots.io case
  10. 10. Microservices YAY! 02 • Nots.io case
  11. 11. ⛔ ⛔ Blocking 02 .branch-selector = Notsapp.Renderer.simple_component({:"dropdown/dropdown", …}) %h2 Files .tree = Notsapp.Renderer.simple_component({:"code-tree/tree", …})
  12. 12. But we want non-blocking 02 🚀
  13. 13. Good news: It’s doable! 02
  14. 14. But first 03
  15. 15. 03 Phoenix templates
  16. 16. But first strings 03 • Phoenix templates Bitstrings: << … >> Binaries: bitstring with size(8) String: binary with valid UTF8 codepoints Charlist: list of integer codepoints Phoenix strings: {:safe, String}
  17. 17. But first iolist() | iodata() 03 • Phoenix templates IO.puts(["Hello, ", "Elixir"]) IO.puts ["Hello, ", 69, 108, 105, 120, 105, 114] IO.puts ["Hello, ", [69, [108, [105, [120, [105, [114, ]]]]]]]
  18. 18. But first iolist() | iodata() Does not allocate new strings: Repeated chunks are created only once Does not concatenate strings/chars: Append to the list is O(1) and doesn’t require copying anything. 03 • Phoenix templates
  19. 19. Use iolist for I/O iolist() | iodata() With iolist & iodata work: Console I/O functions File I/O function :gen_tcp.send() :gen_udp.send() 03 • Phoenix templates
  20. 20. What about Elixir & Phoenix? 03 • Phoenix templates
  21. 21. At compile time Phoenix runs through the templates folder Uses EEx engine to convert each template into a function phoenix/lib/phoenix/template.ex 03 • Phoenix templates
  22. 22. At compile time https://www.bignerdranch.com/blog/elixir-and-io-lists-part-2-io-lists-in-phoenix/ 03 • Phoenix templates <body> <h1>Hello, <%= @hello %> </h1> </body>
  23. 23. At compile time https://www.bignerdranch.com/blog/elixir-and-io-lists-part-2-io-lists-in-phoenix/ 03 • Phoenix templates
  24. 24. At compile time 03 • Phoenix templates
  25. 25. At compile time https://www.bignerdranch.com/blog/elixir-and-io-lists-part-2-io-lists-in-phoenix/ 03 • Phoenix templates Reveal macro magic
  26. 26. At compile time https://www.bignerdranch.com/blog/elixir-and-io-lists-part-2-io-lists-in-phoenix/ 03 • Phoenix templates Generated code of view
  27. 27. Gosh! 03 • Phoenix templates
  28. 28. At the end of the day iex(1)> PageView.render("index.html", hello: "Elixir!") {:safe, [“<body>n <h1>Hello, n", "Elixir!", " </h1>n</body>n"]} iolist In runtime 03 • Phoenix templates
  29. 29. Rewind ⏪ 04 .branch-selector = Notsapp.Renderer.simple_component({:"dropdown/dropdown", …}) %h2 Files .tree = Notsapp.Renderer.simple_component({:"code-tree/tree", …})
  30. 30. What if we return Task 🤔 Instead of HTML 04 • The solution task = Task.async(fn -> do_heavy_work() end) IO.puts "Do some more work" result = Task.await(task)
  31. 31. What if we return Task 🤔 in pseudocode 04 • The solution {:safe, [["" | "<div class="branch-selector"""> "], ["" | Task.async(fn -> render_dropdown() end)] , ["" | "n</div>nn"]]}
  32. 32. And when everything is ready — convert 04 • The solution defmodule Notsapp.TemplateRenderer do def process_rendered_template([h|t]) do [process_rendered_template(h) | process_rendered_template(t)] end def process_rendered_template(%Notsapp.CljTask{task: task}) do Task.await(task) end def process_rendered_template(e) do e end defmacro __using__(_opts) do quote do def render(template, assigns) do {:safe, io_list} = render_template(template, assigns) {:safe, process_rendered_template(io_list)} end end end end
  33. 33. Apply to every view 04 • The solution def view do quote do use Phoenix.View use Notsapp.TemplateRenderer # <-- use Phoenix.HTML
  34. 34. In parallel
  35. 35. Thank you 🤘
  36. 36. Alexander Khokhlov point@nots.io Nots.io blog.nots.io t.me/nots_io

×