-
-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Run common checks for every single solution; Add common check for snake_case #102
Merged
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
01d6f38
Introduce comment struct
angelikatyborska 3243405
Remove mentions of statuses, approval etc.
angelikatyborska 49648e9
Hide expected error message in test
angelikatyborska 1d25200
Revert "Remove mentions of statuses, approval etc."
angelikatyborska b9d84bb
Remove mentions of statuses, approval etc. part 1
angelikatyborska b67f29d
Hide expected error message in test again
angelikatyborska 0a9c5b9
Rename approved_solution
angelikatyborska 81ef14d
Rename referred_solution
angelikatyborska b014ea7
Remove unused disapproved_solution
angelikatyborska 35abd2c
Add moduledoc
angelikatyborska 287c1ba
Merge branch 'main' into refactoring
angelikatyborska 2be4861
Add a check for snake_case module attribute names
angelikatyborska f4562e2
Run common checks when no dedicated module
angelikatyborska 2b5b3a7
Add a check for snake_case function names
angelikatyborska 6e9a93b
Update lib/elixir_analyzer/test_suite/default.ex
neenjaw 7e2ae18
Merge branch 'main' into camel-case-checks
neenjaw fb22547
Update lib/elixir_analyzer/exercise_test/common_checks/function_names.ex
angelikatyborska e39b2cb
Simplify further
angelikatyborska File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
defmodule ElixirAnalyzer.ExerciseTest.CommonChecks do | ||
@moduledoc """ | ||
This module aggregates all common checks that should be run on every single solution. | ||
""" | ||
|
||
alias ElixirAnalyzer.ExerciseTest.CommonChecks.FunctionNames | ||
alias ElixirAnalyzer.ExerciseTest.CommonChecks.ModuleAttributeNames | ||
alias ElixirAnalyzer.Comment | ||
|
||
@spec run(Macro.t(), String.t()) :: [{:pass | :fail | :skip, %Comment{}}] | ||
def run(code_ast, code_as_string) when is_binary(code_as_string) do | ||
[ | ||
FunctionNames.run(code_ast), | ||
ModuleAttributeNames.run(code_ast) | ||
] | ||
|> List.flatten() | ||
end | ||
end |
63 changes: 63 additions & 0 deletions
63
lib/elixir_analyzer/exercise_test/common_checks/function_names.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
defmodule ElixirAnalyzer.ExerciseTest.CommonChecks.FunctionNames do | ||
@moduledoc """ | ||
Reports the first function/macro/guard with a name that's not snake_case. | ||
|
||
Doesn't report more if there are more. | ||
A single comment should be enough for the student to know what to fix. | ||
|
||
Common check to be run on every single solution. | ||
""" | ||
|
||
alias ElixirAnalyzer.Constants | ||
alias ElixirAnalyzer.Comment | ||
|
||
@def_ops [:def, :defp, :defmacro, :defmacrop, :defguard, :defguardp] | ||
|
||
@spec run(Macro.t()) :: [{:pass | :fail | :skip, %Comment{}}] | ||
def run(ast) do | ||
{_, names} = Macro.prewalk(ast, [], &traverse/2) | ||
wrong_name = List.last(names) | ||
|
||
if wrong_name do | ||
wrong_name = to_string(wrong_name) | ||
correct_name = to_snake_case(wrong_name) | ||
|
||
[ | ||
{:fail, | ||
%Comment{ | ||
type: :actionable, | ||
comment: Constants.solution_function_name_snake_case(), | ||
params: %{ | ||
expected: correct_name, | ||
actual: wrong_name | ||
} | ||
}} | ||
] | ||
else | ||
[] | ||
end | ||
end | ||
|
||
defp traverse({op, _meta, [{name, _meta2, _arguments} | _]} = ast, names) when op in @def_ops do | ||
if snake_case?(name) do | ||
{ast, names} | ||
else | ||
{ast, [name | names]} | ||
end | ||
end | ||
|
||
defp traverse(ast, names) do | ||
{ast, names} | ||
end | ||
|
||
defp snake_case?(name) do | ||
# the code had to compile and pass all the tests to get to the analyzer | ||
# so we can assume the name is otherwise valid | ||
to_snake_case(name) == to_string(name) | ||
end | ||
|
||
defp to_snake_case(name) do | ||
# Macro.underscore is good enough because a module attribute name must be a valid Elixir identifier anyway | ||
Macro.underscore(to_string(name)) | ||
end | ||
end |
61 changes: 61 additions & 0 deletions
61
lib/elixir_analyzer/exercise_test/common_checks/module_attribute_names.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
defmodule ElixirAnalyzer.ExerciseTest.CommonChecks.ModuleAttributeNames do | ||
@moduledoc """ | ||
Reports the first module attribute with a name that's not snake_case. | ||
|
||
Doesn't report more if there are more. | ||
A single comment should be enough for the student to know what to fix. | ||
|
||
Common check to be run on every single solution. | ||
""" | ||
|
||
alias ElixirAnalyzer.Constants | ||
alias ElixirAnalyzer.Comment | ||
|
||
@spec run(Macro.t()) :: [{:pass | :fail | :skip, %Comment{}}] | ||
def run(ast) do | ||
{_, names} = Macro.prewalk(ast, [], &traverse/2) | ||
wrong_name = List.last(names) | ||
|
||
if wrong_name do | ||
wrong_name = to_string(wrong_name) | ||
correct_name = to_snake_case(wrong_name) | ||
|
||
[ | ||
{:fail, | ||
%Comment{ | ||
type: :actionable, | ||
comment: Constants.solution_module_attribute_name_snake_case(), | ||
params: %{ | ||
expected: correct_name, | ||
actual: wrong_name | ||
} | ||
}} | ||
] | ||
else | ||
[] | ||
end | ||
end | ||
|
||
defp traverse({:@, _meta, [{name, _meta2, _arguments}]} = ast, names) do | ||
if snake_case?(name) do | ||
{ast, names} | ||
else | ||
{ast, [name | names]} | ||
end | ||
end | ||
|
||
defp traverse(ast, names) do | ||
{ast, names} | ||
end | ||
|
||
defp snake_case?(name) do | ||
# the code had to compile and pass all the tests to get to the analyzer | ||
# so we can assume the name is otherwise valid | ||
to_snake_case(name) == to_string(name) | ||
end | ||
|
||
defp to_snake_case(name) do | ||
# Macro.underscore is good enough because a module attribute name must be a valid Elixir identifier anyway | ||
Macro.underscore(to_string(name)) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
defmodule ElixirAnalyzer.TestSuite.Default do | ||
@moduledoc """ | ||
This is the default exercise analyzer extension module. | ||
|
||
It will be run for any exercise submission that doesn't have its own extension module. | ||
It's intentionally empty, which means it will only run the common checks. | ||
""" | ||
|
||
use ElixirAnalyzer.ExerciseTest | ||
end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
100% agree, despite the warning on the function not to use, I see it used everywhere in non-core code :P