Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions .credo.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
%{
configs: [
%{
name: "default",
files: %{
included: ["lib/", "src/", "web/", "apps/", "test/"],
excluded: [~r"/_build/", ~r"/deps/"]
},
requires: [],
strict: true,
color: true,
checks: [
{Credo.Check.Consistency.ExceptionNames},
{Credo.Check.Consistency.LineEndings},
{Credo.Check.Consistency.MultiAliasImportRequireUse},
{Credo.Check.Consistency.ParameterPatternMatching},
{Credo.Check.Consistency.SpaceAroundOperators},
{Credo.Check.Consistency.SpaceInParentheses},
{Credo.Check.Consistency.TabsOrSpaces},
{Credo.Check.Design.AliasUsage, false},
{Credo.Check.Design.DuplicatedCode, excluded_macros: []},
{Credo.Check.Design.TagFIXME},
{Credo.Check.Design.TagTODO, false},
{Credo.Check.Readability.FunctionNames},
{Credo.Check.Readability.LargeNumbers},
{Credo.Check.Readability.MaxLineLength, max_length: 100},
{Credo.Check.Readability.ModuleAttributeNames},
{Credo.Check.Readability.ModuleDoc},
{Credo.Check.Readability.ModuleNames},
{Credo.Check.Readability.ParenthesesOnZeroArityDefs},
{Credo.Check.Readability.ParenthesesInCondition},
{Credo.Check.Readability.PredicateFunctionNames},
{Credo.Check.Readability.PreferImplicitTry},
{Credo.Check.Readability.RedundantBlankLines},
{Credo.Check.Readability.Semicolons},
{Credo.Check.Readability.SinglePipe, false},
{Credo.Check.Readability.SpaceAfterCommas},
{Credo.Check.Readability.Specs, false},
{Credo.Check.Readability.StringSigils},
{Credo.Check.Readability.TrailingBlankLine},
{Credo.Check.Readability.TrailingWhiteSpace},
{Credo.Check.Readability.VariableNames},
{Credo.Check.Refactor.DoubleBooleanNegation},
{Credo.Check.Refactor.CondStatements},
{Credo.Check.Refactor.CyclomaticComplexity},
{Credo.Check.Refactor.FunctionArity},
{Credo.Check.Refactor.LongQuoteBlocks},
{Credo.Check.Refactor.MatchInCondition},
{Credo.Check.Refactor.NegatedConditionsInUnless},
{Credo.Check.Refactor.NegatedConditionsWithElse},
{Credo.Check.Refactor.Nesting},
{Credo.Check.Refactor.PipeChainStart, false},
{Credo.Check.Refactor.UnlessWithElse},
{Credo.Check.Warning.BoolOperationOnSameValues},
{Credo.Check.Warning.IExPry},
{Credo.Check.Warning.IoInspect},
{Credo.Check.Warning.LazyLogging, false},
{Credo.Check.Warning.OperationOnSameValues},
{Credo.Check.Warning.OperationWithConstantResult},
{Credo.Check.Warning.RaiseInsideRescue},
{Credo.Check.Warning.UnusedEnumOperation},
{Credo.Check.Warning.UnusedFileOperation},
{Credo.Check.Warning.UnusedKeywordOperation},
{Credo.Check.Warning.UnusedListOperation},
{Credo.Check.Warning.UnusedPathOperation},
{Credo.Check.Warning.UnusedRegexOperation},
{Credo.Check.Warning.UnusedStringOperation},
{Credo.Check.Warning.UnusedTupleOperation}
]
}
]
}
20 changes: 20 additions & 0 deletions .github/workflows/elixir-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,31 @@ jobs:
- name: Generate docs
run: mix docs --warnings-as-errors

credo-check:
name: Check Mix Credo Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup Elixir
uses: erlef/setup-elixir@v1
with:
elixir-version: 1.18
otp-version: 28

- name: Fetch Dependencies
run: mix deps.get

- name: Run Credo Linter
run: mix credo --strict --format=flycheck

test:
name: Run Tests
runs-on: ubuntu-latest
needs:
- format-check
- docs-check
- credo-check
strategy:
matrix:
elixir: [1.18, 1.19]
Expand Down Expand Up @@ -79,6 +98,7 @@ jobs:
needs:
- format-check
- docs-check
- credo-check
strategy:
matrix:
elixir: [1.15, 1.16, 1.17, 1.18]
Expand Down
33 changes: 20 additions & 13 deletions lib/cache_decorator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ defmodule CacheDecorator do
Module.make_overridable(env.module, [{name, length(args_ast)}])

key_template = Keyword.fetch!(opts, :key)
key_ast = compile_key_ast!(key_template, args_ast, env.module, name, :cache)
key_ast = compile_key_ast!({key_template, args_ast, env.module, name, :cache})

quote do
def unquote(name)(unquote_splicing(args_ast)) do
Expand Down Expand Up @@ -245,7 +245,7 @@ defmodule CacheDecorator do
Module.make_overridable(env.module, [{name, length(args)}])

raw_key = Keyword.fetch!(opts, :key)
key_ast = compile_key_ast!(raw_key, args, env.module, name, :invalidate)
key_ast = compile_key_ast!({raw_key, args, env.module, name, :invalidate})

invalidate_ast =
quote do
Expand Down Expand Up @@ -298,8 +298,9 @@ defmodule CacheDecorator do
List.flatten(patterns_ast ++ [unmatched_pattern_ast])
end

defp compile_key_ast!(template, args_ast, env_module, fun_name, decorator_type)
when is_binary(template) do
defp compile_key_ast!({template, _, _, _, _} = args) when is_binary(template) do
{template, args_ast, env_module, fun_name, decorator_type} = args

# Collect *all* bound var names from the function head (deep walk)
{^args_ast, arg_ast_by_name} =
Macro.prewalk(args_ast, %{}, fn
Expand All @@ -319,15 +320,7 @@ defmodule CacheDecorator do
# If seg is exactly "{name}", treat it as a variable; otherwise literal.
case Regex.run(~r/^\{([A-Za-z_][A-Za-z0-9_]*)\}$/u, seg) do
[_, var] ->
case Map.fetch(arg_ast_by_name, var) do
{:ok, v_ast} ->
quote(do: to_string(unquote(v_ast)))

:error ->
raise ArgumentError,
"#{inspect(__MODULE__)}: unknown variable {#{var}} in :key for " <>
"@#{decorator_type} #{inspect(env_module)}.#{fun_name}/#{length(args_ast)}"
end
handle_var(var, arg_ast_by_name, args)

nil ->
quote(do: unquote(seg))
Expand All @@ -349,6 +342,20 @@ defmodule CacheDecorator do
end
end

defp handle_var(var, arg_ast_by_name, args) do
{_template, args_ast, env_module, fun_name, decorator_type} = args

case Map.fetch(arg_ast_by_name, var) do
{:ok, v_ast} ->
quote(do: to_string(unquote(v_ast)))

:error ->
raise ArgumentError,
"#{inspect(__MODULE__)}: unknown variable {#{var}} in :key for " <>
"@#{decorator_type} #{inspect(env_module)}.#{fun_name}/#{length(args_ast)}"
end
end

defp invalid_key!(env_module, name, args, key, decorator_type) do
raise ArgumentError,
"#{inspect(__MODULE__)}: invalid value #{inspect(key)} in :key " <>
Expand Down
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ defmodule CacheDecorator.MixProject do
defp deps do
[
{:cachex, "~> 4.1", only: :test},
{:credo, "~> 1.7", only: :dev, runtime: false},
{:ex_doc, "~> 0.38", only: :dev, runtime: false},
{:mockery, "~> 2.3", only: :test}
]
Expand Down
4 changes: 4 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
%{
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"cachex": {:hex, :cachex, "4.1.1", "574c5cd28473db313a0a76aac8c945fe44191659538ca6a1e8946ec300b1a19f", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:ex_hash_ring, "~> 6.0", [hex: :ex_hash_ring, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "d6b7449ff98d6bb92dda58bd4fc3189cae9f99e7042054d669596f56dc503cd8"},
"credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"},
"earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"},
"eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"},
"ex_doc": {:hex, :ex_doc, "0.38.3", "ddafe36b8e9fe101c093620879f6604f6254861a95133022101c08e75e6c759a", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "ecaa785456a67f63b4e7d7f200e8832fa108279e7eb73fd9928e7e66215a01f9"},
"ex_hash_ring": {:hex, :ex_hash_ring, "6.0.4", "bef9d2d796afbbe25ab5b5a7ed746e06b99c76604f558113c273466d52fa6d6b", [:mix], [], "hexpm", "89adabf31f7d3dfaa36802ce598ce918e9b5b33bae8909ac1a4d052e1e567d18"},
"file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"jumper": {:hex, :jumper, "1.0.2", "68cdcd84472a00ac596b4e6459a41b3062d4427cbd4f1e8c8793c5b54f1406a7", [:mix], [], "hexpm", "9b7782409021e01ab3c08270e26f36eb62976a38c1aa64b2eaf6348422f165e1"},
"makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
Expand Down