computer programs with the ability to treat programs as their data.
class Foo
%w(foo bar baz).each do |v|
define_method(v) do
puts "Hi, I am #{v}"
end
end
end
a rule that specifies how a certain input sequence should be mapped to a replacement output sequence
Transform the program before the compiler runs
#ifdef DEBUG_BUILD
#define DEBUG(x) fprintf(stderr, x)
#else
#define DEBUG(x) do {} while (0)
#endif
(+ 1 2 3)is
+ 1 2 31 2 3It's like Lisp...
but Jose hid the parentheses


Simple expression
iex(1)> quote do: 1 + 2
{:+, [context: Elixir, import: Kernel], [1, 2]}A little more complex expression
iex(2)> quote do
...(2)> if a > 20, do: "major", else: "minor"
...(2)> end
{:if, [context: Elixir, import: Kernel],
[{:>, [context: Elixir, import: Kernel], [{:a, [], Elixir}, 20]},
[do: "major", else: "minor"]]}quote transforms an expression into its AST representation` in Lispunquote allows to inject a value in the AST, in Lispunquote_splicing allows to inject an array in the AST,@ in Lispunquoteiex(1)> a = 5
iex(2)> ast = quote do
...(2)> a + 5
...(2)> end
iex(3)> Code.eval_quoted(ast)
** (CompileError) nofile:1: undefined function a/0unquoteiex(1)> a = 5
iex(2)> ast = quote do
...(2)> unquote(a) + 5
...(2)> end
iex(3)> Code.eval_quoted(ast)
{10, []}unquote_splicingiex(1)> args = ["a,b,c", ","]
iex(2)> ast = quote do
...(2)> String.split(unquote(args))
...(2)> end
iex(3)> Code.eval_quoted(ast)
** (FunctionClauseError) no function clause matching in String.Break.split/1unquote_splicingiex(1)> args = ["a,b,c", ","]
iex(2)> ast = quote do
...(2)> String.split(unquote_splicing(args))
...(2)> end
iex(3)> Code.eval_quoted(ast)
{["a", "b", "c"], []}ifdefmacro if(condition, do: do_clause, else: else_clause) do
quote do
case unquote(condition) do
x when x in [nil, false] -> unquote(else_clause)
_ -> unquote(do_clause)
end
end
end
defdelegatedefdelegate fun(a, b), to: Modshould expand to
def fun(a, b) do
Mod.fun(a, b)
enddefdelegatedefdelegatedefmacro defdelegate(function, to: module) do
{name, _, vars} = function # {:fun, _, [{:a, _, _}, {:b, _, _}]}
quote do
def unquote(name)(unquote_splicing(vars)) do
unquote(module).unquote(name)(unquote_splicing(vars))
end
end
end
Macro.expandMacro.to_stringvar!conn?get "/hello" do
send_resp(conn, 200, "world")
enddefmacro get(route, do: block) do
quote do
def handle_request(conn, :get, unquote(route)) do
unquote(block)
end
end
end
# when trying to use conn
** (CompileError) iex:8: undefined function conn/0defmacro get(route, do: block) do
quote do
def handle_request(var!(conn), :get, unquote(route)) do
unquote(block)
end
end
end
# somewhere else
get "/foo" do
IO.inspect(conn)
enddefmodule MyApp.SampleCLI do
use ExCLI.DSL
name "mycli"
description "My CLI"
long_description "This is my long description"
option :verbose, help: "Increase the verbosity level", aliases: [:v], count: true
command :hello do
description "Greets the user"
long_description """
Gives a nice a warm greeting to whoever would listen
"""
argument :name
option :from, help: "the sender of hello"
run context do
if context.verbose >= 1 do
IO.puts("Running hello command.")
end
if from = context[:from] do
IO.write("#{from} says: ")
end
IO.puts("Hello #{context.name}!")
end
end
end
useuse MyModule, opts is equivalent torequire ModuleModule.__using__(opts)use is (often) used to inject functionality__using__defmacro __using__(_opts) do
quote do
import unquote(__MODULE__)
@app %{commands: []}
@command nil
@before_compile unquote(__MODULE__)
end
end
before_compiledefmacro __before_compile__(_env) do
quote do
def __app__ do
@app
end
end
end
defmacro name(name) do
quote do
@app Map.put(@app, :name, unquote(name))
end
end
defmacro command(name, do: block) do
quote do
@command %{name: unquote(name)}
unquote(block)
@app Map.put(@app, :commands, [@command | @app.commands])
@command nil
end
end
defmacro argument(name) do
quote do
if @command do
@command Map.put(@command, :arguments, [unquote(name), @command.arguments])
else
raise "argument should be called from inside a command"
end
end
end
defmacro run(context, do: block) do
quote bind_quoted: [context: Macro.escape(context), block: Macro.escape(block)] do
def __run__(unquote(@command.name), var!(unquote(context))) do
unquote(block)
end
end
end
defmodule MyCLI do
use MyDSL
name "my_cli"
command :my_command do
argument :hello
run context do
IO.inspect(context)
end
end
end