zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

module_doc.ex (3838B)


      1 defmodule Credo.Check.Readability.ModuleDoc do
      2   use Credo.Check,
      3     param_defaults: [
      4       ignore_names: [
      5         ~r/(\.\w+Controller|\.Endpoint|\.\w+Live(\.\w+)?|\.Repo|\.Router|\.\w+Socket|\.\w+View)$/
      6       ]
      7     ],
      8     explanations: [
      9       check: """
     10       Every module should contain comprehensive documentation.
     11 
     12           # preferred
     13 
     14           defmodule MyApp.Web.Search do
     15             @moduledoc \"\"\"
     16             This module provides a public API for all search queries originating
     17             in the web layer.
     18             \"\"\"
     19           end
     20 
     21           # also okay: explicitly say there is no documentation
     22 
     23           defmodule MyApp.Web.Search do
     24             @moduledoc false
     25           end
     26 
     27       Many times a sentence or two in plain english, explaining why the module
     28       exists, will suffice. Documenting your train of thought this way will help
     29       both your co-workers and your future-self.
     30 
     31       Other times you will want to elaborate even further and show some
     32       examples of how the module's functions can and should be used.
     33 
     34       In some cases however, you might not want to document things about a module,
     35       e.g. it is part of a private API inside your project. Since Elixir prefers
     36       explicitness over implicit behaviour, you should "tag" these modules with
     37 
     38           @moduledoc false
     39 
     40       to make it clear that there is no intention in documenting it.
     41       """,
     42       params: [
     43         ignore_names: "All modules matching this regex (or list of regexes) will be ignored."
     44       ]
     45     ]
     46 
     47   alias Credo.Code.Module
     48 
     49   @doc false
     50   def run(%SourceFile{filename: filename} = source_file, params \\ []) do
     51     if Path.extname(filename) == ".exs" do
     52       []
     53     else
     54       issue_meta = IssueMeta.for(source_file, params)
     55       ignore_names = Params.get(params, :ignore_names, __MODULE__)
     56 
     57       {_continue, issues} =
     58         Credo.Code.prewalk(
     59           source_file,
     60           &traverse(&1, &2, issue_meta, ignore_names),
     61           {true, []}
     62         )
     63 
     64       issues
     65     end
     66   end
     67 
     68   defp traverse(
     69          {:defmodule, meta, _arguments} = ast,
     70          {true, issues},
     71          issue_meta,
     72          ignore_names
     73        ) do
     74     mod_name = Module.name(ast)
     75 
     76     if matches_any?(mod_name, ignore_names) do
     77       {ast, {false, issues}}
     78     else
     79       exception? = Module.exception?(ast)
     80 
     81       case Module.attribute(ast, :moduledoc) do
     82         {:error, _} when not exception? ->
     83           {
     84             ast,
     85             {true,
     86              [
     87                issue_for(
     88                  "Modules should have a @moduledoc tag.",
     89                  issue_meta,
     90                  meta[:line],
     91                  mod_name
     92                )
     93              ] ++ issues}
     94           }
     95 
     96         string when is_binary(string) ->
     97           if String.trim(string) == "" do
     98             {
     99               ast,
    100               {true,
    101                [
    102                  issue_for(
    103                    "Use `@moduledoc false` if a module will not be documented.",
    104                    issue_meta,
    105                    meta[:line],
    106                    mod_name
    107                  )
    108                ] ++ issues}
    109             }
    110           else
    111             {ast, {true, issues}}
    112           end
    113 
    114         _ ->
    115           {ast, {true, issues}}
    116       end
    117     end
    118   end
    119 
    120   defp traverse(ast, {continue, issues}, _issue_meta, _ignore_names) do
    121     {ast, {continue, issues}}
    122   end
    123 
    124   defp matches_any?(name, list) when is_list(list) do
    125     Enum.any?(list, &matches_any?(name, &1))
    126   end
    127 
    128   defp matches_any?(name, string) when is_binary(string) do
    129     String.contains?(name, string)
    130   end
    131 
    132   defp matches_any?(name, regex) do
    133     String.match?(name, regex)
    134   end
    135 
    136   defp issue_for(message, issue_meta, line_no, trigger) do
    137     format_issue(
    138       issue_meta,
    139       message: message,
    140       trigger: trigger,
    141       line_no: line_no
    142     )
    143   end
    144 end