Reputation: 17482
I'm still quite new to Elixir and checking out some code in a repository and wondering if there is a way to cut down the following code snippet:
def find(client) do
Common.find(client, @resource)
|> ResponseHandler.handle_response(@model_module)
end
def find(client, identifier) do
Common.find(client, @resource, identifier)
|> ResponseHandler.handle_response(@model_module)
end
def filter(client, filter) do
Common.filter(client, @resource, filter)
|> ResponseHandler.handle_response(@model_module)
end
def create(client, bank_transactions_map) do
Common.create(client, @resource, bank_transactions_map)
|> ResponseHandler.handle_response(@model_module)
end
def update(client, identifier, bank_transactions_map) do
Common.update(client, @resource, identifier, bank_transactions_map)
|> ResponseHandler.handle_response(@model_module)
end
All the functions here pipe into the same function and it seems a little repetitive. Is there a way to set up a function in the module to take in an argument and run the .handle_response line without setting it in each function here?
Upvotes: 0
Views: 992
Reputation: 121000
If that is the real codepiece, meaning the functions are wrappers for the functions from another module having the same names and interspersed with @resource
parameter list, one might define them dynamically:
functions =
[find: ~w|client|a,
find: ~w|client identifier|a,
filter: ~w|client filter|a,
create: ~w|client bank_transactions_map|a,
update: ~w|client identifier bank_transactions_map|a]
Enum.each(functions, fn {fun, args} ->
args = Enum.map args, &{&1, [], Elixir}
wrapped_args = with [h | t] <- args,
do: [h | [ quote(do: @resource) | t ]] # inject @resource
def unquote(fun)(unquote_splicing(args)) do
Common
|> apply(unquote(fun), unquote(wrapped_args))
|> ResponseHandler.handle_response(@model_module)
end
end)
If the wrapping is not as pure, the initial list of functions to wrap should be extended to something like [find: [params: [...], as: :search]
.
The advantage of this approach is that once the function producer is written and well-tested, adding the new wrappers is as easy as updating a config file.
Upvotes: 1
Reputation: 222128
You can define a new function which takes in the function name to call along with the arguments and that function will pipe into ResponseHandler.handle_response(@model_module)
at the end. The following code is untested but should work:
def find(client) do
go(:find, [client, @resource])
end
def find(client, identifier) do
go(:find, [client, @resource, identifier])
end
...
def go(function, args) do
apply(Common, function, args)
|> ResponseHandler.handle_response(@model_module)
end
You can also automatically inject @resource
as the second argument if you want:
def find(client) do
go(:find, [client])
end
def find(client, identifier) do
go(:find, [client, identifier])
end
...
def go(function, [head | tail]) do
apply(Common, function, [head, @resource | tail])
|> ResponseHandler.handle_response(@model_module)
end
You can reduce this even further using macros/metaprogramming but I wouldn't recommend it if you only have 5 cases as it complicates the code. I'd go with the first solution as it's the simplest one.
Upvotes: 2