By the way, I’ve continued with the method of passing context functions, but have moved to about as explicit method as you can get. Previously, I was passing a list of public functions that will later be run in succession as if they were piped like so…
# controller
Accounts.list_users([
&Accounts.join_user_profile/1,
&Accounts.filter_non_executive_users/1,
&Accounts.filter_users_by_company(&1, company),
&Accounts.order_users_by_first_name/1
])
# context
def list_users(queries \\ []) do
from(User)
|> QueryRunner.run(queries)
|> Repo.all()
end
# QueryRunner utility
defmodule Utilities.QueryRunner do
def run(query, additional_queries) when is_list(additional_queries) do
Enum.reduce(additional_queries, query, fn additional_query, query ->
additional_query.(query)
end)
end
def run(query, additional_query) do
run(query, [additional_query])
end
end
However, since all we really want to do is inject into the context query and run as piped, why not just pass an anonymous function that is piped in the first place?
# controller
Accounts.list_users(
&(Accounts.join_user_profile(&1)
|> Accounts.filter_non_executive_users()
|> Accounts.filter_users_by_company(company)
|> Accounts.order_users_by_first_name())
)
# context
def list_users(queries \\ &(&1)) do
from(User)
|> queries.()
|> Repo.all()
end
This requires no additional code and is more obvious to me anyway. Using fn
over capture is arguably more readable, but that just comes down to preference.
# controller
Accounts.list_users(fn query ->
query
|> Accounts.join_user_profile()
|> Accounts.filter_non_executive_users()
|> Accounts.filter_users_by_company(company)
|> Accounts.order_users_by_first_name()
end)