So I checked the definition of Phoenix View render_layout function and I would like to write something similar to pass multiple blocks to render into differents parts of my layout template. Here is the function I writed in my layout view module:
If you want something to take a do/end block then that thing has to be a macro, otherwise you take a function as the argument like how Phoenix deals with its form helpers.
You can actually define a function that takes a block, but it works a little differently to how macros that take a block works. For example,
iex(1)> defmodule Test do
...(1)> def blocky(do: block) do
...(1)> IO.puts("hello from function")
...(1)> block
...(1)> end
...(1)> def test do
...(1)> blocky do
...(1)> IO.puts("hello from block")
...(1)> end
...(1)> end
...(1)> end
{:module, Test, <<...>>, {:test, 0}}
iex(2)> Test.test
hello from block
hello from function
:ok
iex(3)> defmodule Test2 do
...(3)> defmacrop blocky(do: block) do
...(3)> quote do
...(3)> IO.puts("hello from macro")
...(3)> unquote(block)
...(3)> end
...(3)> end
...(3)> def test do
...(3)> blocky do
...(3)> IO.puts("hello from block")
...(3)> end
...(3)> end
...(3)> end
{:module, Test2, <<...>>, {:test, 0}}
iex(4)> Test2.test
hello from macro
hello from block
:ok
edit: was thinking some more and I realized that you’re kinda right to recommend passing an anonymous function to a function instead of a block, as that will result in the correct order of side effects same as a macro would. Eg.
iex(7)> defmodule Test3 do
...(7)> defp blocky(func) do
...(7)> IO.puts("hello from function")
...(7)> func.()
...(7)> end
...(7)> def test do
...(7)> blocky fn ->
...(7)> IO.puts("hello from anonymous function")
...(7)> end
...(7)> end
...(7)> end
{:module, Test3, <<...>>, {:test, 0}}
iex(8)> Test3.test
hello from function
hello from anonymous function
:ok