Crowdhailer

Crowdhailer

Creator of Raxx

Implementing middleware using `defoverridable` and `super`

EDIT: I have written a blog post on this topic

I have been implementing middleware using the defoverridable and super. For example

# myapp.ex

defmodule MyApp do
  use Tokumei.Routing
  use Tokumei.Exceptions
  use Tokumei.ContentLength
  use Tokumei.CommonLogger
  use Tokumei.MethodOverride

  # define routes
end

The Routing module creates a function handle_request/2 and in each middle ware this function is defined as overridable and a new definition created that calls into super as needed. For example the implementation of Tokumei.MethodOverride is as follows, link to code on gihtub.

defmodule Tokumei.MethodOverride do
  def override_method(request = %{method: :POST, query: query}) do
    {method, query} = Map.pop(query, "_method")
    case method && String.upcase(method) do
      nil ->
        request
      method when method in ["PUT", "PATCH", "DELETE"] ->
        method = String.to_existing_atom(method)
        %{request | method: method, query: query}
    end
  end
  def override_method(request) do
    request
  end

  defmacro __using__(_opts) do
    quote do
      defoverridable [handle_request: 2]

      def handle_request(request, config) do
        request = unquote(__MODULE__).override_method(request)
        super(request, config)
      end
    end
  end
end

My questions are:

  • Is there any cost to repeatedly overriding a function?
  • Are there any other libraries doing similar?
  • General comments about the approach

Most Liked

josevalim

josevalim

Creator of Elixir

I will change the private name of the autogenerated function to include a random component just so people don’t do that.

OvermindDL1

OvermindDL1

I hope there is not much cost! I’ve been using a lot of super to override action/2 in controllers to handle exceptions such as redirect’s when a permission exception is thrown. ^.^

But yeah, I’ve not looked at how super is implemented, hmm…

So it seems to be implemented at:
https://github.com/elixir-lang/elixir/blob/ff8dd1af7f6b53e482cd8be304093fabd36bc6dc/lib/elixir/src/elixir_overridable.erl#L16-L22

So it looks up the previous function definition and seems to call it, and just above that is where you see it defines in ets the information of what is overridable, with the store function being at the bottom.

So, it looks like the old/parent definitions are defined with a combination of the name and a counter, so always unique, and defp’d, so calls to them are always ‘local’ calls (in erlang parlance), which is fast.

So no issue, it looks like super is fast, I’d not worry about lots of them at all. :slight_smile:

OvermindDL1

OvermindDL1

Also entirely an implementation detail and you should not do this, but you could ‘directly’ call an older version of a function by using its hidden/internal name (for a ‘blah’ function it would be the function name of :"blah (overridable #{count})" for whatever count you want to go ‘up in the stack’). ^.^

Last Post!

Crowdhailer

Crowdhailer

Creator of Raxx

I have publish the following blog post. Describing how to implement middleware with macros. I have been using this method for a while and it has been useful

Where Next?

Popular in Questions Top

JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 44139 214
New

We're in Beta

About us Mission Statement