Skip to content

Commit

Permalink
Merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
TataR21 committed Oct 23, 2023
2 parents 18d89fd + 1310f05 commit 36561a2
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 44 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Add the following to your `mix.exs` file:
```elixir
defp deps do
[
{:contexted, "~> 0.1.4"}
{:contexted, "~> 0.1.11"}
]
end
```
Expand Down
34 changes: 32 additions & 2 deletions lib/contexted/delegator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ defmodule Contexted.Delegator do
enable_recompilation: true
"""

alias Contexted.ModuleAnalyzer
alias Contexted.{ModuleAnalyzer, Utils}

@doc """
Delegates all public functions of the given module.
Expand Down Expand Up @@ -75,17 +75,47 @@ defmodule Contexted.Delegator do
Enum.map(functions, fn {name, _arity, args, doc, spec} ->
quote do
if unquote(doc), do: unquote(Code.string_to_quoted!(doc))
if unquote(spec), do: unquote(Code.string_to_quoted!(spec))

if unquote(spec) && Utils.recompilation_enabled?(),
do: unquote(Code.string_to_quoted!(spec))

defdelegate unquote(name)(unquote_splicing(args)),
to: unquote(module),
as: unquote(name)
end
end)

types =
case Code.Typespec.fetch_types(module) do
{:ok, types} ->
Enum.map(types, fn
{type_of_type, type} ->
Code.Typespec.type_to_quoted(type)
|> add_ast_for_type(type_of_type)

_ ->
nil
end)

_ ->
[]
end

# Combine the generated delegates into a single AST
quote do
(unquote_splicing(types))
(unquote_splicing(delegates))
end
end

@spec add_ast_for_type(tuple(), atom()) :: tuple()
defp add_ast_for_type(ast, type_of_type) do
{:@, [context: Elixir, imports: [{1, Kernel}]],
[
{type_of_type, [context: Elixir],
[
ast
]}
]}
end
end
9 changes: 2 additions & 7 deletions lib/contexted/mix/tasks/compile/contexted.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Mix.Tasks.Compile.Contexted do
use Mix.Task.Compiler

alias Contexted.Tracer
alias Contexted.{Tracer, Utils}
alias Mix.Task.Compiler

@moduledoc """
Expand All @@ -16,7 +16,7 @@ defmodule Mix.Tasks.Compile.Contexted do
@spec run(any()) :: :ok
def run(_argv) do
if Enum.count(@contexts) > 0 do
if recompilation_enabled?() do
if Utils.recompilation_enabled?() do
Compiler.after_compiler(:app, &Tracer.after_compiler/1)
end

Expand All @@ -26,9 +26,4 @@ defmodule Mix.Tasks.Compile.Contexted do
:ok
end
end

@spec recompilation_enabled? :: boolean()
defp recompilation_enabled? do
Application.get_env(:contexted, :enable_recompilation)
end
end
45 changes: 15 additions & 30 deletions lib/contexted/module_analyzer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -88,37 +88,22 @@ defmodule Contexted.ModuleAnalyzer do
end

@spec build_spec(tuple()) :: String.t()
defp build_spec({{function_name, _arity}, spec}) do
{:type, _, :fun, [arg_types, return_type]} = hd(spec)
arg_types_string = format_arg_types(arg_types)
return_type_string = format_type(return_type)

function_with_args = "#{function_name}(#{arg_types_string})"
return_value = return_type_string

"@spec #{function_with_args} :: #{return_value}"
end

@spec format_arg_types(tuple()) :: String.t()
defp format_arg_types({:type, _, :product, []}), do: ""

defp format_arg_types({:type, _, :product, arg_types}) do
Enum.map_join(arg_types, ",", &format_type/1)
end

@spec format_type(tuple()) :: String.t()
defp format_type({:type, _, :union, types}) do
Enum.map_join(types, " | ", &format_type/1)
defp build_spec({{function_name, _arity}, specs}) do
Enum.map_join(specs, "\n", fn spec ->
Code.Typespec.spec_to_quoted(function_name, spec)
|> add_spec_ast()
|> Macro.to_string()
end)
end

defp format_type({:atom, _, atom}), do: ":#{Atom.to_string(atom)}"
defp format_type({:type, _, type_name, _}), do: "#{type_name}()"

defp format_type({:remote_type, _, [{:atom, _, module}, {:atom, _, type}, _list]}) do
if module == :elixir do
"#{type}()"
else
"#{module}.#{type}()"
end
@spec add_spec_ast(tuple()) :: tuple()
defp add_spec_ast(ast) do
{:@, [context: Elixir, imports: [{1, Kernel}]],
[
{:spec, [context: Elixir],
[
ast
]}
]}
end
end
12 changes: 9 additions & 3 deletions lib/contexted/tracer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,10 @@ defmodule Contexted.Tracer do
@spec map_module_to_context_module(module()) :: module() | nil
defp map_module_to_context_module(module) do
Enum.find(@contexts, fn context ->
stringified_context = Atom.to_string(context)
regex = build_regex(context)
stringified_module = Atom.to_string(module)

String.contains?(stringified_module, stringified_context) ||
stringified_context == stringified_module
Regex.match?(regex, stringified_module)
end)
end

Expand All @@ -112,4 +111,11 @@ defmodule Contexted.Tracer do
&String.contains?(file, &1)
)
end

@spec build_regex(module()) :: Regex.t()
defp build_regex(context) do
context
|> Atom.to_string()
|> then(&~r/\b#{&1}\b/)
end
end
13 changes: 13 additions & 0 deletions lib/contexted/utils.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
defmodule Contexted.Utils do
@moduledoc """
The `Contexted.Utils` defines utils functions for other modules.
"""

@doc """
Checks is `enable_recompilation` option is set.
"""
@spec recompilation_enabled? :: boolean()
def recompilation_enabled? do
Application.get_env(:contexted, :enable_recompilation, false)
end
end
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule Contexted.MixProject do
app: :contexted,
description:
"Contexted is an Elixir library designed to streamline the management of complex Phoenix contexts in your projects, offering tools for module separation, subcontext creation, and auto-generating CRUD operations for improved code maintainability.",
version: "0.1.4",
version: "0.1.11",
elixir: "~> 1.14",
start_permanent: Mix.env() == :prod,
deps: deps(),
Expand Down

0 comments on commit 36561a2

Please sign in to comment.