defmodule RUtils do
@brank_regex ~r/\A[[:space:]]*\z/
@default_undelegate_functions [:__info__, :module_info, :"MACRO-__using__"]
@moduledoc """
Utils for REnum.
"""
@doc """
Defines in the module that called all the functions of the argument module.
## Examples
iex> defmodule A do
...> defmacro __using__(_opts) do
...> RUtils.define_all_functions!(__MODULE__)
...> end
...>
...> def test do
...> :test
...> end
...> end
iex> defmodule B do
...> use A
...> end
iex> B.test
:test
"""
@spec define_all_functions!(module()) :: list
def define_all_functions!(mod, undelegate_functions \\ []) do
enum_funs =
mod.module_info()[:exports]
|> Enum.filter(fn {fun, _} ->
fun not in (@default_undelegate_functions ++ undelegate_functions)
end)
for {fun, arity} <- enum_funs do
quote do
defdelegate unquote(fun)(unquote_splicing(make_args(arity))), to: unquote(mod)
end
end
end
@doc """
Creates tuple for `unquote_splicing`.
"""
@spec make_args(integer) :: list
def make_args(0), do: []
def make_args(n) do
Enum.map(1..n, fn n -> {String.to_atom("arg#{n}"), [], Elixir} end)
end
@doc """
Return true if object is blank, false, empty, or a whitespace string.
For example, +nil+, '', ' ', [], {}, and +false+ are all blank.
## Examples
iex> RUtils.blank?(%{})
true
iex> RUtils.blank?([1])
false
iex> RUtils.blank?(" ")
true
"""
@spec blank?(any()) :: boolean()
def blank?(map) when map == %{}, do: true
def blank?([]), do: true
def blank?(nil), do: true
def blank?(false), do: true
def blank?(str) when is_bitstring(str) do
str
|> String.match?(@brank_regex)
end
def blank?(_), do: false
@doc """
Returns true if not `RUtils.blank?`
## Examples
iex> RUtils.present?(%{})
false
iex> RUtils.present?([1])
true
iex> RUtils.present?(" ")
false
"""
@spec present?(any()) :: boolean()
def present?(obj) do
!blank?(obj)
end
@doc false
def required_functions(functions, exclude_modules) do
functions
|> Enum.reject(fn method ->
exclude_functions = exclude_modules |> Enum.flat_map(& &1.module_info()[:exports])
already_defined_func =
exclude_functions
|> Keyword.keys()
|> Enum.find(&(&1 == method))
!!already_defined_func || to_string(method) =~ ~r/!/
end)
|> Enum.each(&IO.puts(&1))
end
end