{"id":16656684,"url":"https://github.com/arjan/decorator","last_synced_at":"2025-05-15T11:02:26.868Z","repository":{"id":44648189,"uuid":"71458306","full_name":"arjan/decorator","owner":"arjan","description":"Function decorators for Elixir","archived":false,"fork":false,"pushed_at":"2024-02-09T19:42:20.000Z","size":86,"stargazers_count":393,"open_issues_count":13,"forks_count":23,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-07T08:12:08.812Z","etag":null,"topics":["ast","decorators","elixir","macros"],"latest_commit_sha":null,"homepage":null,"language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arjan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-20T11:56:00.000Z","updated_at":"2025-03-26T10:29:31.000Z","dependencies_parsed_at":"2024-06-18T13:56:57.694Z","dependency_job_id":null,"html_url":"https://github.com/arjan/decorator","commit_stats":{"total_commits":86,"total_committers":15,"mean_commits":5.733333333333333,"dds":0.2790697674418605,"last_synced_commit":"a32053ae884014320940d2a8cefe55181203db93"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arjan%2Fdecorator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arjan%2Fdecorator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arjan%2Fdecorator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arjan%2Fdecorator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arjan","download_url":"https://codeload.github.com/arjan/decorator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248911456,"owners_count":21182092,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ast","decorators","elixir","macros"],"created_at":"2024-10-12T09:58:19.135Z","updated_at":"2025-04-14T15:53:19.445Z","avatar_url":"https://github.com/arjan.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Elixir function decorators\n\n[![Build Status](https://github.com/arjan/decorator/workflows/test/badge.svg)](https://github.com/arjan/decorator)\n[![Module Version](https://img.shields.io/hexpm/v/decorator.svg)](https://hex.pm/packages/decorator)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/decorator/)\n[![Total Download](https://img.shields.io/hexpm/dt/decorator.svg)](https://hex.pm/packages/decorator)\n[![License](https://img.shields.io/hexpm/l/decorator.svg)](https://github.com/arjan/decorator/blob/master/LICENSE)\n[![Last Updated](https://img.shields.io/github/last-commit/arjan/decorator.svg)](https://github.com/arjan/decorator/commits/master)\n\nA function decorator is a \"`@decorate`\" annotation that is put just\nbefore a function definition.  It can be used to add extra\nfunctionality to Elixir functions. The runtime overhead of a function\ndecorator is zero, as it is executed on compile time.\n\nExamples of function decorators include: loggers, instrumentation\n(timing), precondition checks, et cetera.\n\n\n## Some remarks in advance\n\nSome people think function decorators are a bad idea, as they can\nperform magic stuff on your functions (side effects!). Personally, I\nthink they are just another form of metaprogramming, one of Elixir's\nselling points. But use decorators wisely, and always study the\ndecorator code itself, so you know what it is doing.\n\nDecorators are always marked with the `@decorate` literal, so that\nit's clear in the code that decorators are being used.\n\n\n## Installation\n\nAdd `:decorator` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:decorator, \"~\u003e 1.2\"}\n  ]\nend\n```\n\nYou can now define your function decorators.\n\n## Usage\n\nFunction decorators are macros which you put just before defining a\nfunction. It looks like this:\n\n```elixir\ndefmodule MyModule do\n  use PrintDecorator\n\n  @decorate print()\n  def square(a) do\n    a * a\n  end\nend\n```\n\nNow whenever you call `MyModule.square()`, you'll see the message: `Function\ncalled: square` in the console.\n\nDefining the decorator is pretty easy. Create a module in which you\n*use* the `Decorator.Define` module, passing in the decorator name and\narity, or more than one if you want.\n\nThe following declares the above `@print` decorator which prints a\nmessage every time the decorated function is called:\n\n```elixir\ndefmodule PrintDecorator do\n  use Decorator.Define, [print: 0]\n\n  def print(body, context) do\n    quote do\n      IO.puts(\"Function called: \" \u003c\u003e Atom.to_string(unquote(context.name)))\n      unquote(body)\n    end\n  end\n\nend\n```\n\nThe arguments to the decorator function (the `def print(...)`) are the\nfunction's body (the abstract syntax tree (AST)), as well as a `context`\nargument which holds information like the function's name, defining module,\narity and the arguments AST.\n\n\n### Compile-time arguments\n\nDecorators can have compile-time arguments passed into the decorator\nmacros.\n\nFor instance, you could let the print function only print when a\ncertain logging level has been set:\n\n```elixir\n@decorate print(:debug)\ndef foo() do\n...\n```\n\nIn this case, you specify the arity 1 for the decorator:\n\n```elixir\ndefmodule PrintDecorator do\n  use Decorator.Define, [print: 1]\n```\n\nAnd then your `print/3` decorator function gets the level passed in as\nthe first argument:\n\n```elixir\ndef print(level, body, context) do\n# ...\nend\n```\n\n### Decorating all functions in a module\n\nA shortcut to decorate all functions in a module is to use the `@decorate_all` attribute, as shown below. It is\nimportant to note that the `@decorate_all` attribute only\naffects the function clauses below its definition.\n\n```elixir\ndefmodule MyApp.APIController\n  use MyBackend.LoggerDecorator\n\n  @decorate_all log_request()\n\n  def index(_conn, params) do\n    # ...\n  end\n\n  def detail(_conn, params) do\n    # ...\n  end\n```\n\nIn this example, the `log_request()` decorator is applied to both\n`index/2` and `detail/2`.\n\n\n### Functions with multiple clauses\n\nIf you have a function with multiple clauses, and only decorate one\nclause, you will notice that you get compiler warnings about unused\nvariables and other things. For functions with multiple clauses the\ngeneral advice is this: You should create an empty function head, and\ncall the decorator on that head, like this:\n\n```elixir\ndefmodule DecoratorFunctionHead do\n  use DecoratorFunctionHead.PrintDecorator\n\n  @decorate print()\n  def hello(first_name, last_name \\\\ nil)\n\n  def hello(:world, _last_name) do\n    :world\n  end\n\n  def hello(first_name, last_name) do\n    \"Hello #{first_name} #{last_name}\"\n  end\nend\n```\n\n\n### Decorator context\n\nBesides the function body AST, the decorator function also gets a\n*context* argument passed in. This context holds information about the\nfunction being decorated, namely its module, function name, arity, function\nkind, and arguments as a list of AST nodes.\n\nThe print decorator can print its function name like this:\n\n```elixir\ndef print(body, context) do\n  Logger.debug(\"Function #{context.name}/#{context.arity} with kind #{context.kind} called in module #{context.module}!\")\nend\n```\n\nEven more advanced, you can use the function arguments in the\ndecorator.  To create an `is_authorized` decorator which performs some\nchecks on the Phoenix %Conn{} structure, you can create a decorator\nfunction like this:\n\n```elixir\ndef is_authorized(body, %{args: [conn, _params]}) do\n  quote do\n    if unquote(conn).assigns.user do\n      unquote(body)\n    else\n      unquote(conn)\n      |\u003e send_resp(401, \"unauthorized\")\n      |\u003e halt()\n    end\n  end\nend\n```\n\n## Copyright and License\n\nCopyright (c) 2016 Arjan Scherpenisse\n\nThis library is MIT licensed. See the\n[LICENSE](https://github.com/arjan/decorator/blob/master/LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farjan%2Fdecorator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farjan%2Fdecorator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farjan%2Fdecorator/lists"}