All Projects → arjan → Decorator

arjan / Decorator

Licence: mit
Function decorators for Elixir

Programming Languages

elixir
2628 projects
macros
77 projects

Projects that are alternatives of or similar to Decorator

deco
Minimalist Function Decorators for Elixir
Stars: ✭ 21 (-92.45%)
Mutual labels:  decorators, ast
inmemantlr
ANTLR as a libray for JVM based languages
Stars: ✭ 87 (-68.71%)
Mutual labels:  ast
jsdast
JSDoc Abstract Syntax Tree
Stars: ✭ 20 (-92.81%)
Mutual labels:  ast
bright
Blazing fast parser for BrightScript that gives you ESTree like AST
Stars: ✭ 28 (-89.93%)
Mutual labels:  ast
postcss-rs
🚀 Fast and 100% API compatible postcss replacer, built in Rust
Stars: ✭ 414 (+48.92%)
Mutual labels:  ast
lowcode
React Lowcode - prototype, develop and maintain internal apps easier
Stars: ✭ 32 (-88.49%)
Mutual labels:  ast
parseclj
Clojure Parser for Emacs Lisp
Stars: ✭ 44 (-84.17%)
Mutual labels:  ast
Swiftpascalinterpreter
Simple Swift interpreter for the Pascal language inspired by the Let’s Build A Simple Interpreter article series.
Stars: ✭ 270 (-2.88%)
Mutual labels:  ast
pascal-interpreter
A simple interpreter for a large subset of Pascal language written for educational purposes
Stars: ✭ 21 (-92.45%)
Mutual labels:  ast
ucast
Conditions query translator for everything
Stars: ✭ 76 (-72.66%)
Mutual labels:  ast
ng-morph
Code mutations in schematics were never easier than now.
Stars: ✭ 63 (-77.34%)
Mutual labels:  ast
qunit-migrate
Migrate old QUnit tests to 2.x. Uses regex and ASTs to convert old QUnit code.
Stars: ✭ 17 (-93.88%)
Mutual labels:  ast
php-ast-reverter
Reverts the php-ast AST back into (somewhat) PSR-compliant code
Stars: ✭ 49 (-82.37%)
Mutual labels:  ast
tydoc
The TypeScript documenter that meets you where you are
Stars: ✭ 28 (-89.93%)
Mutual labels:  ast
Rxweb
Tons of extensively featured packages for Angular, VUE and React Projects
Stars: ✭ 262 (-5.76%)
Mutual labels:  decorators
bind-observable
Provides a typescript decorator which binds class properties to observable companion properties.
Stars: ✭ 15 (-94.6%)
Mutual labels:  decorators
vue-decorator
Custom decorators to vue-class-component that fits Vue 3
Stars: ✭ 31 (-88.85%)
Mutual labels:  decorators
giuseppe
A controller routing system for expressJS with TypeScript decorators and annotations
Stars: ✭ 49 (-82.37%)
Mutual labels:  decorators
Jsqlparser
JSqlParser parses an SQL statement and translate it into a hierarchy of Java classes. The generated hierarchy can be navigated using the Visitor Pattern
Stars: ✭ 3,405 (+1124.82%)
Mutual labels:  ast
Helpful Decorators
Helpful decorators for typescript projects
Stars: ✭ 263 (-5.4%)
Mutual labels:  decorators

Elixir function decorators

Build Status Module Version Hex Docs Total Download License Last Updated

A function decorator is a "@decorate" annotation that is put just before a function definition. It can be used to add extra functionality to Elixir functions. The runtime overhead of a function decorator is zero, as it is executed on compile time.

Examples of function decorators include: loggers, instrumentation (timing), precondition checks, et cetera.

Some remarks in advance

Some people think function decorators are a bad idea, as they can perform magic stuff on your functions (side effects!). Personally, I think they are just another form of metaprogramming, one of Elixir's selling points. But use decorators wisely, and always study the decorator code itself, so you know what it is doing.

Decorators are always marked with the @decorate literal, so that it's clear in the code that decorators are being used.

Installation

Add :decorator to your list of dependencies in mix.exs:

def deps do
  [
    {:decorator, "~> 1.2"}
  ]
end

You can now define your function decorators.

Usage

Function decorators are macros which you put just before defining a function. It looks like this:

defmodule MyModule do
  use PrintDecorator

  @decorate print()
  def square(a) do
    a * a
  end
end

Now whenever you call MyModule.square(), you'll see the message: Function called: square in the console.

Defining the decorator is pretty easy. Create a module in which you use the Decorator.Define module, passing in the decorator name and arity, or more than one if you want.

The following declares the above @print decorator which prints a message every time the decorated function is called:

defmodule PrintDecorator do
  use Decorator.Define, [print: 0]

  def print(body, context) do
    quote do
      IO.puts("Function called: " <> Atom.to_string(unquote(context.name)))
      unquote(body)
    end
  end

end

The arguments to the decorator function (the def print(...)) are the function's body (the abstract syntax tree (AST)), as well as a context argument which holds information like the function's name, defining module, arity and the arguments AST.

Compile-time arguments

Decorators can have compile-time arguments passed into the decorator macros.

For instance, you could let the print function only print when a certain logging level has been set:

@decorate print(:debug)
def foo() do
...

In this case, you specify the arity 1 for the decorator:

defmodule PrintDecorator do
  use Decorator.Define, [print: 1]

And then your print/3 decorator function gets the level passed in as the first argument:

def print(level, body, context) do
# ...
end

Decorating all functions in a module

A shortcut to decorate all functions in a module is to use the @decorate_all attribute, as shown below. It is important to note that the @decorate_all attribute only affects the function clauses below its definition.

defmodule MyApp.APIController
  use MyBackend.LoggerDecorator

  @decorate_all log_request()

  def index(_conn, params) do
    # ...
  end

  def detail(_conn, params) do
    # ...
  end

In this example, the log_request() decorator is applied to both index/2 and detail/2.

Functions with multiple clauses

If you have a function with multiple clauses, and only decorate one clause, you will notice that you get compiler warnings about unused variables and other things. For functions with multiple clauses the general advice is this: You should create an empty function head, and call the decorator on that head, like this:

defmodule DecoratorFunctionHead do
  use DecoratorFunctionHead.PrintDecorator

  @decorate print()
  def hello(first_name, last_name \\ nil)

  def hello(:world, _last_name) do
    :world
  end

  def hello(first_name, last_name) do
    "Hello #{first_name} #{last_name}"
  end
end

Decorator context

Besides the function body AST, the decorator function also gets a context argument passed in. This context holds information about the function being decorated, namely its module, function name, arity, and arguments as a list of AST nodes.

The print decorator can print its function name like this:

def print(body, context) do
  Logger.debug("Function #{context.name}/#{context.arity} called in module #{context.module}!")
end

Even more advanced, you can use the function arguments in the decorator. To create an is_authorized decorator which performs some checks on the Phoenix %Conn{} structure, you can create a decorator function like this:

def is_authorized(body, %{args: [conn, _params]}) do
  quote do
    if unquote(conn).assigns.user do
      unquote(body)
    else
      unquote(conn)
      |> send_resp(401, "unauthorized")
      |> halt()
    end
  end
end

Copyright and License

Copyright (c) 2016 Arjan Scherpenisse

This library is MIT licensed. See the LICENSE for details.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].