Ruby-like modules in Elixir using __using__

Elijah Kim
2 min readMay 12, 2016

--

I was recently tasked with creating filters on an admin dashboard. For those that don’t know what I mean by filters, think of it as a search bar where you can search by a number of attributes. In the case of this app, one filter was for Orders where the user could filter by id, the store it was placed at, dates, and even the inclusions or any combination of that list. There are libraries to help you out such as Filtrex but I felt like our use case was quite complex and I couldn’t figure out how to do joins for the life of me. So I decided to build my own (really, try not to do this too often). Here’s how I solved it.

Here is the first solution that I was happy with:

The secret sauce of getting this to work is in query_from_params. Here’s how it works:

  1. It takes a query and a map with a key of “search”.
  2. It whitelists the search params with the @queryable list.
  3. It loops over the remaining list and calls get_query with the key and value of each search parameter (get_query is expected to return an Ecto Query).
  4. Then it joins them into a single Query.

In its current form, the filter only works with an id which is found in @queryable. To extend the filter to multiple fields, the code only has to change in a few places.

  1. Add the field name to @queryable
  2. Create a get_query function with the first argument being a tuple of the attribute, and it’s value and returns a query.

This is close to the open for extension and closed for modification principle and is also, in my opinion, quite easy to understand.

Although this query_from_params function was great. I did have to add similar functionality in other parts of the app. I wanted to be able to share query_from_params instead of duplicating the code everywhere.

Enter the Macro

Specifically, the __using__ macro. Instead of trying to explain it. I’m going to delegate the explanation of it to this article which does a great job; mostly because I don’t really understand the macro system fully. What I do know however is that you can use it similarly to Modules in Ruby. Let me show you.

I moved query_from_params to its own module called QueryBuilder and overrode its __using__ macro. Now we’re able to use QueryBuilder anywhere we like and pass in the list of attributes we want to query on. When using QueryBuilder, it expects the module to define all of its get_query functions but otherwise, you’re able to call query_from_params from your controller now and it will delegate to QueryBuilder.

Great success. We now have DRY code. I hope this demystified macros a little bit!

--

--