Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F41667337
file_location.ex
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
4 KB
Referenced Files
None
Subscribers
None
file_location.ex
View Options
# Pleroma: A lightweight social networking server
# Originally taken from
# https://github.com/VeryBigThings/elixir_common/blob/master/lib/vbt/credo/check/consistency/file_location.ex
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
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
]
@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
)
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-ruby
Expires
Sun, Feb 15, 5:48 AM (1 d, 8 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1084733
Default Alt Text
file_location.ex (4 KB)
Attached To
Mode
rPUBE pleroma-upstream
Attached
Detach File
Event Timeline
Log In to Comment