summaryrefslogtreecommitdiff
path: root/doc/plugins.md
diff options
context:
space:
mode:
authorTristan Sloughter <tristan.sloughter@gmail.com>2015-02-27 16:20:21 -0600
committerTristan Sloughter <tristan.sloughter@gmail.com>2015-02-27 16:20:21 -0600
commit71d70fca1f1182b8f4919e2b3f5a7ff277700271 (patch)
treeef8b1b8a55ca646090ffc89d227b3667acd1874c /doc/plugins.md
parent1ac54972a9c14ac5dddbe463b0c93e5d799d36e0 (diff)
parentc8023cee0bb940da586e3195aafdfdad386b8670 (diff)
Merge pull request #193 from rebar/clear-docs-to-website
Documentation is maintained on www.rebar3.org
Diffstat (limited to 'doc/plugins.md')
-rw-r--r--doc/plugins.md272
1 files changed, 0 insertions, 272 deletions
diff --git a/doc/plugins.md b/doc/plugins.md
deleted file mode 100644
index c8afc91..0000000
--- a/doc/plugins.md
+++ /dev/null
@@ -1,272 +0,0 @@
-# Plugins #
-
-Rebar3's system is based on the concept of *[providers](https://github.com/tsloughter/providers)*. A provider has three callbacks:
-
-- `init(State) -> {ok, NewState}`, which helps set up the state required, state dependencies, etc.
-- `do(State) -> {ok, NewState} | {error, Error}`, which does the actual work.
-- `format_error(Error) -> String`, which allows to print errors when they happen, and to filter out sensitive elements from the state.
-
-A provider should also be an OTP Library application, which can be fetched as any other Erlang dependency, except for Rebar3 rather than your own system or application.
-
-This document contains the following elements:
-
-- [Using a Plugin](#using-a-plugin)
-- [Reference](#reference)
- - [Provider Interface](#provider-interface)
- - [List of Possible Dependencies](#list-of-possible-dependencies)
- - [Rebar State Manipulation](#rebar-state-manipulation)
-- [Tutorial](#tutorial)
-
-## Using a Plugin ##
-
-To use the a plugin, add it to the rebar.config:
-
-```erlang
-{plugins, [
- {plugin_name, ".*", {git, "git@host:user/name-of-plugin.git", {tag, "v1.0.0"}}}
-]}.
-```
-
-Then you can just call it directly:
-
-```
-→ rebar3 plugin_name
-===> Fetching plugin_name
-===> Compiling plugin_name
-<PLUGIN OUTPUT>
-```
-
-## Reference ##
-
-TODO
-
-### Provider Interface ###
-
-TODO
-
-### List of Possible Dependencies ###
-
-TODO
-
-### Rebar State Manipulation ###
-
-TODO
-
-
-## Tutorial ##
-
-### First version ###
-
-In this tutorial, we'll show how to start from scratch, and get a basic plugin written. The plugin will be quite simple: it will look for instances of 'TODO:' lines in comments and report them as warnings. The final code for the plugin can be found on [bitbucket](https://bitbucket.org/ferd/rebar3-todo-plugin).
-
-The first step is to create a new OTP Application that will contain the plugin:
-
- → rebar3 new plugin provider_todo desc="example rebar3 plugin"
- ...
- → cd provider_todo
- → git init
- Initialized empty Git repository in /Users/ferd/code/self/provider_todo/.git/
-
-Open up the `src/provider_todo.erl` file and make sure you have the following skeleton in place:
-
-```erlang
--module(provider_todo).
--behaviour(provider).
-
--export([init/1, do/1, format_error/1]).
-
--define(PROVIDER, todo).
--define(DEPS, [app_discovery]).
-
-%% ===================================================================
-%% Public API
-%% ===================================================================
--spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
-init(State) ->
- Provider = providers:create([
- {name, ?PROVIDER}, % The 'user friendly' name of the task
- {module, ?MODULE}, % The module implementation of the task
- {bare, true}, % The task can be run by the user, always true
- {deps, ?DEPS}, % The list of dependencies
- {example, "rebar provider_todo"}, % How to use the plugin
- {opts, []} % list of options understood by the plugin
- {short_desc, "example rebar3 plugin"},
- {desc, ""}
- ]),
- {ok, rebar_state:add_provider(State, Provider)}.
-
-
--spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
-do(State) ->
- {ok, State}.
-
--spec format_error(any()) -> iolist().
-format_error(Reason) ->
- io_lib:format("~p", [Reason]).
-```
-
-This shows all the basic content needed. Note that we leave the `DEPS` macro to the value `app_discovery`, used to mean that the plugin should at least find the project's source code (excluding dependencies).
-
-In this case, we need to change very little in `init/1`. Here's the new provider description:
-
-```erlang
- Provider = providers:create([
- {name, ?PROVIDER}, % The 'user friendly' name of the task
- {module, ?MODULE}, % The module implementation of the task
- {bare, true}, % The task can be run by the user, always true
- {deps, ?DEPS}, % The list of dependencies
- {example, "rebar todo"}, % How to use the plugin
- {opts, []}, % list of options understood by the plugin
- {short_desc, "Reports TODOs in source code"},
- {desc, "Scans top-level application source and find "
- "instances of TODO: in commented out content "
- "to report it to the user."}
- ]),
-```
-
-Instead, most of the work will need to be done directly in `do/1`. We'll use the `rebar_state` module to fetch all the applications we need. This can be done by calling the `project_apps/1` function, which returns the list of the project's top-level applications.
-
-```erlang
-do(State) ->
- lists:foreach(fun check_todo_app/1, rebar_state:project_apps(State)),
- {ok, State}.
-```
-
-This, on a high level, means that we'll check each top-level app one at a time (there may often be more than one top-level application when working with releases)
-
-The rest is filler code specific to the plugin, in charge of reading each app path, go read code in there, and find instances of 'TODO:' in comments in the code:
-
-```erlang
-check_todo_app(App) ->
- Path = filename:join(rebar_app_info:dir(App),"src"),
- Mods = find_source_files(Path),
- case lists:foldl(fun check_todo_mod/2, [], Mods) of
- [] -> ok;
- Instances -> display_todos(rebar_app_info:name(App), Instances)
- end.
-
-find_source_files(Path) ->
- [filename:join(Path, Mod) || Mod <- filelib:wildcard("*.erl", Path)].
-
-check_todo_mod(ModPath, Matches) ->
- {ok, Bin} = file:read_file(ModPath),
- case find_todo_lines(Bin) of
- [] -> Matches;
- Lines -> [{ModPath, Lines} | Matches]
- end.
-
-find_todo_lines(File) ->
- case re:run(File, "%+.*(TODO:.*)", [{capture, all_but_first, binary}, global, caseless]) of
- {match, DeepBins} -> lists:flatten(DeepBins);
- nomatch -> []
- end.
-
-display_todos(_, []) -> ok;
-display_todos(App, FileMatches) ->
- io:format("Application ~s~n",[App]),
- [begin
- io:format("\t~s~n",[Mod]),
- [io:format("\t ~s~n",[TODO]) || TODO <- TODOs]
- end || {Mod, TODOs} <- FileMatches],
- ok.
-```
-
-Just using `io:format/2` to output is going to be fine.
-
-To test the plugin, push it to a source repository somewhere. Pick one of your projects, and add something to the rebar.config:
-
-```erlang
-{plugins, [
- {provider_todo, ".*", {git, "git@bitbucket.org:ferd/rebar3-todo-plugin.git", {branch, "master"}}}
-]}.
-```
-
-Then you can just call it directly:
-
-```
-→ rebar3 todo
-===> Fetching provider_todo
-===> Compiling provider_todo
-Application merklet
- /Users/ferd/code/self/merklet/src/merklet.erl
- todo: consider endianness for absolute portability
-```
-
-Rebar3 will download and install the plugin, and figure out when to run it. Once compiled, it can be run at any time again.
-
-### Optionally Search Deps ###
-
-Let's extend things a bit. Maybe from time to time (when cutting a release), we'd like to make sure none of our dependencies contain 'TODO:'s either.
-
-To do this, we'll need to go parse command line arguments a bit, and change our execution model. The `?DEPS` macro will now need to specify that the `todo` provider can only run *after* dependencies have been installed:
-
-```erlang
--define(DEPS, [install_deps]).
-```
-
-We can add the option to the list we use to configure the provider in `init/1`:
-
-```erlang
-{opts, [ % list of options understood by the plugin
- {deps, $d, "deps", undefined, "also run against dependencies"}
-]},
-```
-
-Meaning that deps can be flagged in by using the option `-d` (or `--deps`), and if it's not defined, well, we get the default value `undefined`. The last element of the 4-tuple is documentation for the option.
-
-And then we can implement the switch to figure out what to search:
-
-```erlang
-do(State) ->
- Apps = case discovery_type(State) of
- project -> rebar_state:project_apps(State);
- deps -> rebar_state:project_apps(State) ++ rebar_state:all_deps(State)
- end,
- lists:foreach(fun check_todo_app/1, Apps),
- {ok, State}.
-
-[...]
-
-discovery_type(State) ->
- {Args, _} = rebar_state:command_parsed_args(State),
- case proplists:get_value(deps, Args) of
- undefined -> project;
- _ -> deps
- end.
-```
-
-The `deps` option is found using `rebar_state:command_parsed_args(State)`, which will return a proplist of terms on the command-line after 'todo', and will take care of validating whether the flags are accepted or not. The rest can remain the same.
-
-Push the new code for the plugin, and try it again on a project with dependencies:
-
-```
-→ rebar3 todo --deps
-===> Fetching provider_todo
-===> Compiling provider_todo
-===> Fetching bootstrap
-===> Fetching file_monitor
-===> Fetching recon
-[...]
-Application dirmon
- /Users/ferd/code/self/figsync/apps/dirmon/src/dirmon_tracker.erl
- TODO: Peeranha should expose the UUID from a node.
-Application meck
- /Users/ferd/code/self/figsync/_deps/meck/src/meck_proc.erl
- TODO: What to do here?
- TODO: What to do here?
-```
-
-Rebar3 will now go pick dependencies before running the plugin on there.
-
-you can also see that the help will be completed for you:
-
-```
-→ rebar3 help todo
-Scans top-level application source and find instances of TODO: in commented out content to report it to the user.
-
-Usage: rebar todo [-d]
-
- -d, --deps also run against dependencies
-```
-
-That's it, the todo plugin is now complete! It's ready to ship and be included in other repositories.