Commit e5ee33bf authored by Mayel's avatar Mayel

improve modularity hacks

parent 6c0b5a87
Pipeline #218991387 failed with stage
in 1 minute and 15 seconds
defmodule ModuleExtend do
defmodule Modularity.Module.Extend do
defmacro extends(module) do
require Logger
module = Macro.expand(module, __CALLER__)
Logger.info("[Modularity.Module.Extend] Extending module #{inspect(module)}")
functions = module.__info__(:functions)
signatures = Enum.map functions, fn { name, arity } ->
args = if arity == 0 do
[]
else
Enum.map 1 .. arity, fn(i) ->
{ String.to_atom(<< ?x, ?A + i - 1 >>), [], nil }
end
end
{ name, [], args }
end
signatures =
Enum.map(functions, fn {name, arity} ->
args =
if arity == 0 do
[]
else
Enum.map(1..arity, fn i ->
{String.to_atom(<<?x, ?A + i - 1>>), [], nil}
end)
end
{name, [], args}
end)
zipped = List.zip([signatures, functions])
for sig_func <- zipped do
quote do
defdelegate unquote(elem(sig_func, 0)), to: unquote(module)
......
defmodule ModuleOverride do
defmodule Modularity.Module.Override do
@moduledoc """
Utility to clone a module under a new name
"""
require Logger
@doc """
Clone the existing module under a new name
"""
def archive_module(module_source_file, extending_module) do
Code.ensure_compiled(extending_module)
def clone(old_module, new_module) when is_atom(old_module) do
Logger.info(
"[Modularity.Module.Override] Cloning module #{module_name_string(old_module)} as #{
module_name_string(new_module)
}"
)
with {:ok, f} <- File.read(module_source_file) do
Code.eval_string(String.replace(f, "defmodule ", "defmodule ModuleOverride."))
with {:module, _module} <- Code.ensure_compiled(old_module),
module_source_file = old_module.module_info()[:compile][:source],
{:ok, f} <- File.read(module_source_file) do
Code.eval_string(
String.replace(
f,
"defmodule #{module_name_string(old_module)}",
"defmodule #{module_name_string(new_module)}"
)
)
# returns name of archived module
_new_module = String.to_existing_atom("Elixir.#{module_name_string(new_module)}")
else
e ->
Logger.error("Could not find source of module #{old_module}: #{inspect(e)}")
nil
end
end
def clone(old_module, prefix) when is_binary(old_module),
do: clone(String.to_existing_atom(old_module), prefix)
def module_name_string(module), do: String.replace("#{module}", "Elixir.", "")
end
defmodule CommonsPub.Utils.TrendyExtended do
require Modularity.Module.Extend
Modularity.Module.Extend.extends CommonsPub.Utils.Trendy
def some(count_or_range \\ 1, fun) do
require Logger
Logger.info("Selecting #{count_or_range} random thing(s) returned by function #{inspect fun}")
# call function from original module:
super(count_or_range, fun)
end
end
alias CommonsPub.Utils.Simulation, as: ExtendingModule
alias ModuleOverride.CommonsPub.Utils.Simulation, as: ArchiveModule
# module that we will override
alias CommonsPub.Utils.Simulation, as: NewModule
# new name for the old module
alias Original.CommonsPub.Utils.Simulation, as: ArchiveModule
ModuleOverride.archive_module(Path.dirname(__ENV__.file) <> "/simulation.ex", ExtendingModule)
# archive the old module
Modularity.Module.Override.clone(NewModule, ArchiveModule)
defmodule ExtendingModule do
require ModuleExtend
ModuleExtend.extends(ArchiveModule)
defmodule NewModule do
require Modularity.Module.Extend
@moduledoc """
(Re)define new or existing functions
"""
# extend the archived module
Modularity.Module.Extend.extends ArchiveModule
####
# (Re)define new or existing functions
####
# example of straight up replacing a function
def name(), do: Faker.Person.last_name()
......
# defmodule CommonsPub.Utils.SimulationCustom do
# require ExtendModule
# ExtendModule.extends CommonsPub.Utils.Simulation
# def location(), do: Faker.Address.country()
# end
# require Logger
# alias CommonsPub.Utils.Simulation, as: Mod
# defmodule ModuleOrigin.Simulation do
# @moduledoc """
# Clone the existing module under a new name
# """
# Code.ensure_compiled(Mod)
# for {func, arity} <- Mod.__info__(:functions) do
# args = Macro.generate_arguments(arity, __MODULE__)
# def unquote(func)(unquote_splicing(args)) do
# Mod.unquote(func)(unquote_splicing(args))
# rescue
# Mod.Error -> handle_error()
# end
# end
# def handle_error(), do: Logger.info("Error while overiding a module")
# end
# defmodule CommonsPub.Utils.Simulation do
# @moduledoc """
# (Re)define functions
# """
# require ModuleExtend
# ModuleExtend.extends ModuleOrigin.Simulation
# def location(), do: Faker.Address.country()
# end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment