Hi all
I’ve read an article about Plug and somewhere in the article you find the sentence:
One interesting characteristic of module plugs is that init/1 is executed in compile time, while call/2 happens at run time.
My question is, how can I call a function before it will be compiled? I know elixir provide an attribute @before_compile but it has nothing to do with?
And a code, that makes me confuse:
def call(%Plug.Conn{request_path: "/" <> name} = conn, opts) do
send_resp(conn, 200, "Hello, #{name}")
end
Can someone explain me, how this will be pattern match?
There is probably some macro that can call init/1 at compile time. Macros are basically a pieces of code, that execute at compile time, and they can modify/write/remove other code, by manipulating abstract syntax tree of code of the program. Check out this simple snippet for basic macro, note we do not execute the compiled program anywhere:
$ cat macro.ex
defmodule Foo do
defmacro say_hi do
IO.puts "Hi, I printed at compile time!" # this is a function call
end
end
defmodule Bar do
require Foo
Foo.say_hi
end
$ elixirc macro.ex
Hi, I printed at compile time!
So, there seem to be two unrelated questions here. You don’t actually need a macro in order to call something at compile time, you can simply be in something like a module body which is evaluated at compile time. So for example if you do
defmodule Foo do
IO.puts "yo"
end
"yo" will get printed at compile time, because it calls the IO.puts function at compile time.
How does the question about the Plug.Conn pattern match relate? What do you mean “how this will be pattern match”?
Important to note is that while you can call any functions and macro at compile time, these cannot be functions/macros defined in the module you are currently compiling.
Above code snippet is only understandable in the following context:
def call(%Plug.Conn{request_path: "/" <> name} = conn, opts) do
# ...
end
This is a definition of the call method, with two parameters (call/2). These parameters are named conn and opts in above code.
I myself usually find it nicer to write it like: def call(conn = %Plug.Conn{request_path: "/" <> name}, opts)
because I find it more idiomatic, but as far as I know there is no difference (inside a function’s parameter list! Outside, one can only assign something when it is to the left side of the match operator(=)! This is exactly why I find it more idiomatic to swap them around. )
Lets break it down:
This definition of call takes two parameters:
1: conn = %Plug.Conn{request_path: "/" <> name}. This ensures that:
a) we set the result to a variable conn.
b) This function clause will not match when we pass anything that is not a %Plug.Conn{} as the first parameter.
c) Furthermore, this %Plug.Conn{} is required to have a request_path: field that starts with a “/”.
d) Also, anything that follows the / in the request_path: field is stored in the variable name.