Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85824261
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
5 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/credo/check/consistency/file_location.ex b/lib/credo/check/consistency/file_location.ex
index 5ef17b894..08be2bcf9 100644
--- a/lib/credo/check/consistency/file_location.ex
+++ b/lib/credo/check/consistency/file_location.ex
@@ -1,132 +1,163 @@
+# Originally taken from
+# https://github.com/VeryBigThings/elixir_common/blob/master/lib/vbt/credo/check/consistency/file_location.ex
+
defmodule Credo.Check.Consistency.FileLocation do
@moduledoc false
# credo:disable-for-this-file Credo.Check.Readability.Specs
@checkdoc """
File location should follow the namespace hierarchy of the module it defines.
Examples:
- `lib/my_system.ex` should define the `MySystem` module
- `lib/my_system/accounts.ex` should define the `MySystem.Accounts` module
"""
@explanation [warning: @checkdoc]
- # `use Credo.Check` required that module attributes are already defined, so we need to place these attributes
+ @special_namespaces [
+ "controllers",
+ "views",
+ "operations",
+ "channels"
+ ]
+
+ # `use Credo.Check` required that module attributes are already defined, so we need
+ # to place these attributes
# before use/alias expressions.
# credo:disable-for-next-line VBT.Credo.Check.Consistency.ModuleLayout
use Credo.Check, category: :warning, base_priority: :high
alias Credo.Code
def run(source_file, params \\ []) do
case verify(source_file, params) do
:ok ->
[]
{:error, module, expected_file} ->
error(IssueMeta.for(source_file, params), module, expected_file)
end
end
defp verify(source_file, params) do
source_file.filename
|> Path.relative_to_cwd()
|> verify(Code.ast(source_file), params)
end
@doc false
def verify(relative_path, ast, params) do
if verify_path?(relative_path, params),
do: ast |> main_module() |> verify_module(relative_path, params),
else: :ok
end
defp verify_path?(relative_path, params) do
case Path.split(relative_path) do
["lib" | _] -> not exclude?(relative_path, params)
["test", "support" | _] -> false
["test", "test_helper.exs"] -> false
["test" | _] -> not exclude?(relative_path, params)
_ -> false
end
end
defp exclude?(relative_path, params) do
params
|> Keyword.get(:exclude, [])
|> Enum.any?(&String.starts_with?(relative_path, &1))
end
defp main_module(ast) do
{_ast, modules} = Macro.prewalk(ast, [], &traverse/2)
Enum.at(modules, -1)
end
defp traverse({:defmodule, _meta, args}, modules) do
[{:__aliases__, _, name_parts}, _module_body] = args
{args, [Module.concat(name_parts) | modules]}
end
defp traverse(ast, state), do: {ast, state}
# empty file - shouldn't really happen, but we'll let it through
defp verify_module(nil, _relative_path, _params), do: :ok
defp verify_module(main_module, relative_path, params) do
parsed_path = parsed_path(relative_path, params)
expected_file =
expected_file_base(parsed_path.root, main_module) <>
Path.extname(parsed_path.allowed)
- if expected_file == parsed_path.allowed,
- do: :ok,
- else: {:error, main_module, expected_file}
+ cond do
+ expected_file == parsed_path.allowed ->
+ :ok
+
+ special_namespaces?(parsed_path.allowed) ->
+ original_path = parsed_path.allowed
+
+ namespace =
+ Enum.find(@special_namespaces, original_path, fn namespace ->
+ String.contains?(original_path, namespace)
+ end)
+
+ allowed = String.replace(original_path, "/" <> namespace, "")
+
+ if expected_file == allowed,
+ do: :ok,
+ else: {:error, main_module, expected_file}
+
+ true ->
+ {:error, main_module, expected_file}
+ end
end
+ defp special_namespaces?(path), do: String.contains?(path, @special_namespaces)
+
defp parsed_path(relative_path, params) do
parts = Path.split(relative_path)
allowed =
Keyword.get(params, :ignore_folder_namespace, %{})
|> Stream.flat_map(fn {root, folders} -> Enum.map(folders, &Path.join([root, &1])) end)
|> Stream.map(&Path.split/1)
|> Enum.find(&List.starts_with?(parts, &1))
|> case do
nil ->
relative_path
ignore_parts ->
Stream.drop(ignore_parts, -1)
|> Enum.concat(Stream.drop(parts, length(ignore_parts)))
|> Path.join()
end
%{root: hd(parts), allowed: allowed}
end
defp expected_file_base(root_folder, module) do
{parent_namespace, module_name} = module |> Module.split() |> Enum.split(-1)
relative_path =
if parent_namespace == [],
do: "",
else: parent_namespace |> Module.concat() |> Macro.underscore()
file_name = module_name |> Module.concat() |> Macro.underscore()
Path.join([root_folder, relative_path, file_name])
end
defp error(issue_meta, module, expected_file) do
format_issue(issue_meta,
message:
"Mismatch between file name and main module #{inspect(module)}. " <>
"Expected file path to be #{expected_file}. " <>
"Either move the file or rename the module.",
line_no: 1
)
end
end
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Oct 29, 1:52 PM (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1798162
Default Alt Text
(5 KB)
Attached To
Mode
rPUBE pleroma-upstream
Attached
Detach File
Event Timeline