Skip to content
Oeiuwq Faith Blog OpenSource Porfolio

vic/deco

Minimalist Function Decorators for Elixir

vic/deco.json
{
"defaultBranch": "master",
"description": "Minimalist Function Decorators for Elixir",
"fullName": "vic/deco",
"homepage": "",
"language": "Elixir",
"name": "deco",
"pushedAt": "2018-03-07T07:05:42Z",
"stargazersCount": 23,
"updatedAt": "2025-09-22T13:18:17Z",
"url": "https://github.com/vic/deco"
}

Deco - Minimalist function decorators and around-advice for Elixir.

Section titled “Deco - Minimalist function decorators and around-advice for Elixir.”

hexdocs.

Yes, yet another package for decorating elixir functions :).

However, deco’s core is minimalist in comparission to others, uses much less magic, does not overrides anything like Kernel.def nor any operator.

deco has only one macro, decorators themselves are just plain functions Macro.t() -> Macro.t(), and imposes no run-time overhead because function decoration is performed at compile-time.

use Deco

The syntax is deco DECORATORS in FORM where decorators is a tuple of one or more decorators, and form is tipically a function definition.

decorators are just plain functions you write that take the AST of FORM and just return a modified AST of it. The Deco module has some convenience functions for updating the AST.

Since deco runs at compile time, you cannot use functions being defined in the same module that is using deco. Normally it’s better to define your decorators on a separate module.

The following example decorates the function with a tracer that will use Logger to print the arguments given before applying the original function, and the its final result.

deco { Trace.trace() } in
def foo(x) do
x
end

Decorators can take arguments, the AST will be prepended to the list of arguments given by you.

Also, because decorators operate on the AST they have full access to it allowing them to for example, introduce new guards or remove them.

deco { Deco.update_guard(fn _ -> nil end) } in
def foo(x) when is_atom(x) do
x
end
foo("hey")
=> "hey"

Decorators can be composed, the one at the end will take the original AST and produce a new one for the one on top of it.

deco {
Deco.pipe_result(String.capitalize),
Deco.pipe_result(String.reverse),
Deco.pipe_result(to_string)
} in
def foo(x) do
x
end
foo(:john)
=> "Nhoj

You can decorate using a simple function, without having to mess with the AST if you dont want to. The Deco.around decorator will act as an around advice and will give you a reference to the decorated function (made private) and all the arguments given on invocation.

# our private around advice
defp bar_wrapper(decorated, name, say) do
"#{say} #{decorated.(name)}"
end
deco {Deco.around( bar_wrapper("hello") )} in
def bar(name) do
name |> String.capitalize
end
bar("world")
=> "hello World"

For more examples, see the tests or use the source, Luke

This example was adapted from arjan/decorator to show how it would look like using deco.

defp is_authorized(decorated, conn, params) do
if conn.assigns.user do
decorated.(conn, params)
else
conn
|> send_resp(401, "unauthorized")
|> halt()
end
end
deco {Deco.around(is_authorized)} in
def create(conn, params) do
...
end
def deps do
[
{:deco, "~> 0.1"}
]
end