I want to 'extend' a built-in IO/File functions of Elixir in several (independent) ways.
I came up with the following code pattern:
defmodule FileExtension1 do
# arguments are different for different extensions,
# but I guess I could just stick with single map/list
# argument for uniformity
def open!(filename, some_param \\ true) do
filename
|> File.open!
|> attach(some_param)
end
def close(io) do
io |> File.close
end
def attach(io, some_param \\ false) do
spawn_link fn ->
file_manager(io, some_param)
end
end
def detach(io) do
io |> send {:detach, self}
receive do
{:will_detach, ^io} -> :ok
end
end
defp file_manager(io, some_param, state \\ <<>>) do
if Process.alive?(io) do
receive do
{:detach, sender} ->
sender |> send {:will_detach, self}
{:custom_request, sender, reference, count} ->
# {result, new_state} = do_job(...)
sender |> send {:custom_reply, reference, result}
file_manager(io, some_param, new_state)
{:io_request, sender, reference, {:some_pattern}} ->
# {result, new_state} = do_job(...)
sender |> send {:io_reply, reference, result}
file_manager(io, some_param, new_state)
x ->
io |> proxy(x)
file_manager(io, some_param, state)
end
end
end
defp proxy(io, data) do
{request_type, original_from, original_reference, command} = data
reference = make_ref
io |> send {request_type, self, reference, command}
receive do
{response_type, ^reference, result} -> original_from |> send {response_type, original_reference, result}
end
end
end
Basically, it does the following:
FileNow I can transparently stack these things on top of each other (i.e. attach first one to the File, then second one to the first one, etc.).
The problem is: now I have three modules which follows the same pattern I described above. I want to somehow remove code duplication.
What should I look into?
IO contains shared functions for several devices, or Enum contains shared functions for several types. Could you give me a small example how that would work in my case?GenServer, tuned to my needs. Then again, if I should use that, small example will help here.Bonus question: How do I test that shared functionality using ExUnit?
A very simple example of passing a function to another function:
#PassInFunction
defmodule PIF do
def hello(name), do: IO.puts("Hello #{name}!")
def execf(name, f), do: f.(name)
end
PIF.execf("Onorio",&PIF.hello/1)
In your particular case I'd use the file type (or extension as the case may be) to determine which function to pass in.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With