zf

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

plugin.ex (13692B)


      1 defmodule Credo.Plugin do
      2   @moduledoc """
      3   Plugins are module which can provide additional functionality to Credo.
      4 
      5   A plugin is basically just a module that provides an `init/1` callback.
      6 
      7       defmodule CredoDemoPlugin do
      8         def init(exec) do
      9           # but what do we do here??
     10           exec
     11         end
     12       end
     13 
     14   The `Credo.Plugin` module provides a number of functions for extending Credo's core features.
     15 
     16       defmodule CredoDemoPlugin do
     17         @config_file File.read!(".credo.exs")
     18 
     19         import Credo.Plugin
     20 
     21         def init(exec) do
     22           exec
     23           |> register_default_config(@config_file)
     24           |> register_command("demo", CredoDemoPlugin.DemoCommand)
     25           |> register_cli_switch(:castle, :string, :X)
     26           |> prepend_task(:set_default_command, CredoDemoPlugin.SetDemoAsDefaultCommand)
     27         end
     28       end
     29 
     30   """
     31 
     32   require Credo.Execution
     33 
     34   pipeline_main_group_names_as_bullet_list = """
     35   - `:parse_cli_options`
     36   - `:initialize_plugins`
     37   - `:determine_command`
     38   - `:set_default_command`
     39   - `:initialize_command`
     40   - `:parse_cli_options_final`
     41   - `:validate_cli_options`
     42   - `:convert_cli_options_to_config`
     43   - `:resolve_config`
     44   - `:validate_config`
     45   - `:run_command`
     46   - `:halt_execution`
     47   """
     48 
     49   pipeline_existing_commands_group_names_as_bullet_list = """
     50   - `Credo.CLI.Command.Suggest.SuggestCommand` (run via `mix credo suggest`)
     51     - `:load_and_validate_source_files`
     52     - `:prepare_analysis`
     53     - `:print_before_analysis`
     54     - `:run_analysis`
     55     - `:filter_issues`
     56     - `:print_after_analysis`
     57 
     58   - `Credo.CLI.Command.List.ListCommand` (run via `mix credo list`)
     59     - `:load_and_validate_source_files`
     60     - `:prepare_analysis`
     61     - `:print_before_analysis`
     62     - `:run_analysis`
     63     - `:filter_issues`
     64     - `:print_after_analysis`
     65 
     66   - `Credo.CLI.Command.Diff.DiffCommand` (run via `mix credo diff`)
     67     - `:load_and_validate_source_files`
     68     - `:prepare_analysis`
     69     - `:print_previous_analysis`
     70     - `:run_analysis`
     71     - `:filter_issues`
     72     - `:print_after_analysis`
     73     - `:filter_issues_for_exit_status`
     74 
     75   - `Credo.CLI.Command.Info.InfoCommand` (run via `mix credo info`)
     76     - `:load_and_validate_source_files`
     77     - `:prepare_analysis`
     78     - `:print_info`
     79   """
     80 
     81   alias Credo.Execution
     82 
     83   @doc """
     84   Appends a `Credo.Execution.Task` module to Credo's main execution pipeline.
     85 
     86   Credo's execution pipeline consists of several steps, each with a group of tasks, which you can hook into.
     87 
     88   Appending tasks to these steps is easy:
     89 
     90       # credo_demo_plugin.ex
     91       defmodule CredoDemoPlugin do
     92         import Credo.Plugin
     93 
     94         def init(exec) do
     95           append_task(exec, :set_default_command, CredoDemoPlugin.SetDemoAsDefaultCommand)
     96         end
     97       end
     98 
     99   Note how `Credo.Plugin.append_task/3` takes two arguments after the `Credo.Execution` struct: the name of the group to be modified and the module that should be executed.
    100 
    101   The group names of Credo's main pipeline are:
    102 
    103   #{pipeline_main_group_names_as_bullet_list}
    104 
    105   The module appended to these groups should use `Credo.Execution.Task`:
    106 
    107       # credo_demo_plugin/set_demo_as_default_command.ex
    108       defmodule CredoDemoPlugin.SetDemoAsDefaultCommand do
    109         use Credo.Execution.Task
    110 
    111         alias Credo.CLI.Options
    112 
    113         def call(exec, _opts) do
    114           set_command(exec, exec.cli_options.command || "demo")
    115         end
    116 
    117         defp set_command(exec, command) do
    118           %Execution{exec | cli_options: %Options{exec.cli_options | command: command}}
    119         end
    120       end
    121 
    122   This example would have the effect that typing `mix credo` would no longer run the built-in `Suggest` command, but rather our plugin's `Demo` command.
    123   """
    124   def append_task(%Execution{initializing_plugin: plugin_mod} = exec, group_name, task_mod) do
    125     Execution.append_task(exec, plugin_mod, nil, group_name, task_mod)
    126   end
    127 
    128   @doc """
    129   Appends a `Credo.Execution.Task` module to the execution pipeline of an existing Command.
    130 
    131   Credo's commands can also have an execution pipeline of their own, which is executed when the command is used and which you can hook into as well.
    132 
    133   Appending tasks to these steps is easy:
    134 
    135       # credo_demo_plugin.ex
    136       defmodule CredoDemoPlugin do
    137         import Credo.Plugin
    138 
    139         def init(exec) do
    140           append_task(exec, Credo.CLI.Command.Suggest.SuggestCommand, :print_after_analysis, CredoDemoPlugin.WriteFile)
    141         end
    142       end
    143 
    144   Note how `Credo.Plugin.append_task/4` takes three arguments after the `Credo.Execution` struct: the pipeline and the name of the group to be modified and the module that should be executed.
    145 
    146   Here are the pipeline keys and group names:
    147 
    148   #{pipeline_existing_commands_group_names_as_bullet_list}
    149 
    150   The module appended to these groups should use `Credo.Execution.Task`:
    151 
    152       # credo_demo_plugin/write_file.ex
    153       defmodule CredoDemoPlugin.WriteFile do
    154         use Credo.Execution.Task
    155 
    156         alias Credo.CLI.Options
    157 
    158         def call(exec, _opts) do
    159           issue_count = exec |> Execution.get_issues() |> Enum.count
    160           File.write!("demo.json", ~q({"issue_count": \#{issue_count}}))
    161 
    162           exec
    163         end
    164       end
    165 
    166   This example would have the effect that running `mix credo suggest` would write the issue count in a JSON file.
    167   """
    168   def append_task(
    169         %Execution{initializing_plugin: plugin_mod} = exec,
    170         pipeline_key,
    171         group_name,
    172         task_mod
    173       ) do
    174     Execution.append_task(exec, plugin_mod, pipeline_key, group_name, task_mod)
    175   end
    176 
    177   @doc """
    178   Prepends a `Credo.Execution.Task` module to Credo's main execution pipeline.
    179 
    180   Credo's execution pipeline consists of several steps, each with a group of tasks, which you can hook into.
    181 
    182   Prepending tasks to these steps is easy:
    183 
    184       # credo_demo_plugin.ex
    185       defmodule CredoDemoPlugin do
    186         import Credo.Plugin
    187 
    188         def init(exec) do
    189           prepend_task(exec, :set_default_command, CredoDemoPlugin.SetDemoAsDefaultCommand)
    190         end
    191       end
    192 
    193   Note how `Credo.Plugin.prepend_task/3` takes two arguments after the `Credo.Execution` struct: the name of the group to be modified and the module that should be executed.
    194 
    195   The group names of Credo's main pipeline are:
    196 
    197   #{pipeline_main_group_names_as_bullet_list}
    198 
    199   The module prepended to these groups should use `Credo.Execution.Task`:
    200 
    201       # credo_demo_plugin/set_demo_as_default_command.ex
    202       defmodule CredoDemoPlugin.SetDemoAsDefaultCommand do
    203         use Credo.Execution.Task
    204 
    205         alias Credo.CLI.Options
    206 
    207         def call(exec, _opts) do
    208           set_command(exec, exec.cli_options.command || "demo")
    209         end
    210 
    211         defp set_command(exec, command) do
    212           %Execution{exec | cli_options: %Options{exec.cli_options | command: command}}
    213         end
    214       end
    215 
    216   This example would have the effect that typing `mix credo` would no longer run the built-in `Suggest` command, but rather our plugin's `Demo` command.
    217   """
    218   def prepend_task(%Execution{initializing_plugin: plugin_mod} = exec, group_name, task_mod) do
    219     Execution.prepend_task(exec, plugin_mod, nil, group_name, task_mod)
    220   end
    221 
    222   @doc """
    223   Prepends a `Credo.Execution.Task` module to the execution pipeline of an existing Command.
    224 
    225   Credo's commands can also have an execution pipeline of their own, which is executed when the command is used and which you can hook into as well.
    226 
    227   Prepending tasks to these steps is easy:
    228 
    229       # credo_demo_plugin.ex
    230       defmodule CredoDemoPlugin do
    231         import Credo.Plugin
    232 
    233         def init(exec) do
    234           prepend_task(exec, Credo.CLI.Command.Suggest.SuggestCommand, :print_after_analysis, CredoDemoPlugin.WriteFile)
    235         end
    236       end
    237 
    238   Note how `Credo.Plugin.prepend_task/4` takes three arguments after the `Credo.Execution` struct: the pipeline and the name of the group to be modified and the module that should be executed.
    239 
    240   Here are the pipeline keys and group names:
    241 
    242   #{pipeline_existing_commands_group_names_as_bullet_list}
    243 
    244   The module prepended to these groups should use `Credo.Execution.Task`:
    245 
    246       # credo_demo_plugin/write_file.ex
    247       defmodule CredoDemoPlugin.WriteFile do
    248         use Credo.Execution.Task
    249 
    250         alias Credo.CLI.Options
    251 
    252         def call(exec, _opts) do
    253           issue_count = exec |> Execution.get_issues() |> Enum.count
    254           File.write!("demo.json", ~q({"issue_count": \#{issue_count}}))
    255 
    256           exec
    257         end
    258       end
    259 
    260   This example would have the effect that running `mix credo suggest` would write the issue count in a JSON file.
    261   """
    262   def prepend_task(
    263         %Execution{initializing_plugin: plugin_mod} = exec,
    264         pipeline_key,
    265         group_name,
    266         task_mod
    267       ) do
    268     Execution.prepend_task(exec, plugin_mod, pipeline_key, group_name, task_mod)
    269   end
    270 
    271   @doc """
    272   Adds a CLI switch to Credo.
    273 
    274   For demo purposes, we are writing a command called `demo` (see `register_command/3`):
    275 
    276       # credo_demo_plugin/demo_command.ex
    277       defmodule CredoDemoPlugin do
    278         import Credo.Plugin
    279 
    280         def init(exec) do
    281           exec
    282           |> register_command("demo", CredoDemoPlugin.DemoCommand)
    283         end
    284       end
    285 
    286       # credo_demo_plugin/demo_command.ex
    287       defmodule CredoDemoPlugin.DemoCommand do
    288         alias Credo.CLI.Output.UI
    289         alias Credo.Execution
    290 
    291         def call(exec, _) do
    292           castle = Execution.get_plugin_param(exec, CredoDemoPlugin, :castle)
    293 
    294           UI.puts("By the power of \#{castle}!")
    295 
    296           exec
    297         end
    298       end
    299 
    300   Since Plugins can be configured by params in `.credo.exs`, we can add the `:castle` param:
    301 
    302       # .credo.exs
    303       {CredoDemoPlugin, [castle: "Grayskull"]}
    304 
    305   And get the following output:
    306 
    307   ```bash
    308   $ mix credo demo
    309   By the power of Grayskull!
    310   ```
    311 
    312   Plugins can provide custom CLI options as well, so we can do something like:
    313 
    314   ```bash
    315   $ mix credo demo --castle Winterfell
    316   Unknown switch: --castle
    317   ```
    318 
    319   Registering a custom CLI switch for this is easy:
    320 
    321       defmodule CredoDemoPlugin do
    322         import Credo.Plugin
    323 
    324         def init(exec) do
    325           exec
    326           |> register_command("demo", CredoDemoPlugin.DemoCommand)
    327           |> register_cli_switch(:castle, :string, :X)
    328         end
    329       end
    330 
    331   Every registered CLI switch is automatically converted into a plugin param of the same name, which is why we get the following output:
    332 
    333   ```bash
    334   $ mix credo demo --castle Winterfell
    335   By the power of Winterfell!
    336 
    337   $ mix credo demo -X Camelot
    338   By the power of Camelot!
    339   ```
    340 
    341   Plugin authors can also provide a function to control the plugin param's name and value more granularly:
    342 
    343       defmodule CredoDemoPlugin do
    344         import Credo.Plugin
    345 
    346         def init(exec) do
    347           register_cli_switch(exec, :kastle, :string, :X, fn(switch_value) ->
    348             {:castle, String.upcase(switch_value)}
    349           end)
    350         end
    351       end
    352 
    353   And get the following output:
    354 
    355   ```bash
    356   $ mix credo demo --kastle Winterfell
    357   By the power of WINTERFELL!
    358   ```
    359 
    360   """
    361   def register_cli_switch(
    362         %Execution{initializing_plugin: plugin_mod} = exec,
    363         name,
    364         type,
    365         alias_name \\ nil,
    366         convert_to_param \\ true
    367       ) do
    368     exec
    369     |> Execution.put_cli_switch(plugin_mod, name, type)
    370     |> Execution.put_cli_switch_alias(plugin_mod, name, alias_name)
    371     |> Execution.put_cli_switch_plugin_param_converter(plugin_mod, name, convert_to_param)
    372   end
    373 
    374   @doc ~S"""
    375   Registers and initializes a Command module with a given `name`.
    376 
    377   ## Add new commands
    378 
    379   Commands are just modules with a call function and adding new commands is easy.
    380 
    381       # credo_demo_plugin.ex
    382       defmodule CredoDemoPlugin do
    383         import Credo.Plugin
    384 
    385         def init(exec) do
    386           register_command(exec, "demo", CredoDemoPlugin.DemoCommand)
    387         end
    388       end
    389 
    390       # credo_demo_plugin/demo_command.ex
    391       defmodule CredoDemoPlugin.DemoCommand do
    392         alias Credo.CLI.Output.UI
    393         alias Credo.Execution
    394 
    395         def call(exec, _) do
    396           castle = Execution.get_plugin_param(exec, CredoDemoPlugin, :castle)
    397 
    398           UI.puts("By the power of #{castle}!")
    399 
    400           exec
    401         end
    402       end
    403 
    404   Users can use this command by typing
    405 
    406   ```bash
    407   $ mix credo demo
    408   By the power of !
    409   ```
    410 
    411   ## Override an existing command
    412 
    413   Since commands are just modules with a call function, overriding existing commands is easy.
    414 
    415       defmodule CredoDemoPlugin do
    416         import Credo.Plugin
    417 
    418         def init(exec) do
    419           register_command(exec, "explain", CredoDemoPlugin.MyBetterExplainCommand)
    420         end
    421       end
    422 
    423   This example would have the effect that typing `mix credo lib/my_file.ex:42` would no longer run the built-in `Explain` command, but rather our plugin's `MyBetterExplain` command.
    424   """
    425   def register_command(%Execution{initializing_plugin: plugin_mod} = exec, name, command_mod) do
    426     Execution.put_command(exec, plugin_mod, name, command_mod)
    427   end
    428 
    429   @doc """
    430   Registers the contents of a config file.
    431 
    432   This registers the contents of a config file as default config, loading it after Credo's own default config but before the [config files loaded from the current working directory](config_file.html#transitive-configuration-files).
    433 
    434       defmodule CredoDemoPlugin do
    435         @config_file File.read!(".credo.exs")
    436 
    437         import Credo.Plugin
    438 
    439         def init(exec) do
    440           register_default_config(exec, @config_file)
    441         end
    442       end
    443   """
    444   def register_default_config(
    445         %Execution{initializing_plugin: plugin_mod} = exec,
    446         config_file_string
    447       ) do
    448     Execution.append_config_file(exec, {:plugin, plugin_mod, config_file_string})
    449   end
    450 end