Skip to content

Commit 58fbab9

Browse files
author
Peter Tihanyi
committed
Add admin script, + fixes
1 parent b788275 commit 58fbab9

11 files changed

+95
-46
lines changed

README.md

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
Command Line tool
22
=====
33

4-
Parsing and executing console commands
4+
Parsing and executing console commands
55

66
## Example
77
Until i have time to write proper documentation here is an example how to use it:
88

9+
### Setup release
10+
Add to your project's relx overlay config:
11+
12+
```erlang
13+
{relx, [
14+
...
15+
{overlay, [{template, "_build/default/lib/cli_console/rel/admin.sh", "bin/admin"}
16+
]}
17+
]}.
18+
```
19+
20+
In the release you can use it ex: `./bin/admin help`
21+
922
### Defining arguments
1023
Available argument types: `flag | atom | string | binary | integer`
1124
Arguments converted to target type automatically.

elvis.config

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[{elvis,
2+
[{config,
3+
[#{dirs => ["src"],
4+
filter => "cli_console_output.erl",
5+
rules => [{elvis_style, no_debug_call, disable}],
6+
ruleset => erl_files
7+
},
8+
#{dirs => ["src"],
9+
filter => "*.erl",
10+
ruleset => erl_files,
11+
ignore => [cli_console_output]
12+
},
13+
#{dirs => ["."],
14+
filter => "rebar.config",
15+
ruleset => rebar_config
16+
}
17+
]
18+
}]
19+
}].

rel/admin.sh

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/sh
2+
# -*- tab-width:4;indent-tabs-mode:nil -*-
3+
# ex: ts=4 sw=4 et
4+
5+
$(dirname "${BASH_SOURCE[0]}")/{{release_name}} rpc cli_console run "$@"

src/cli_console.erl

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
-export([run/1, register/4]).
1515

1616
%% @doc Execute commands
17-
-spec run(string()) -> ok.
17+
-spec run(string() | list(string())) -> ok.
18+
run([Item | _] = Commands) when is_list(Item) ->
19+
run(string:join(Commands, " "));
1820
run(ConsoleCommand) ->
1921
{ok, Command, Arguments} = cli_console_parser:parse(ConsoleCommand),
2022
Result = cli_console_command:run(Command, Arguments),

src/cli_console_app.erl

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ stop(_State) ->
2929
-spec get_help(term()) -> output_format().
3030
get_help(_) ->
3131
{ok, Data} = cli_console_command:get_help([]),
32-
Data.
32+
Data.

src/cli_console_command.erl

+32-26
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@ get_help(Command) ->
4949
%%% Spawning and gen_server implementation
5050
%%%===================================================================
5151

52+
-spec start_link() ->
53+
{ok, pid()} | {error, {already_started, pid()}} | {error, term()}.
5254
start_link() ->
53-
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
55+
gen_server:start_link({local, ?SERVER}, ?MODULE, [],
56+
[{hibernate_after, timer:seconds(160)}]).
5457

5558
-spec init(term()) -> {ok, state()}.
5659
init(_) ->
@@ -65,9 +68,10 @@ handle_call({run, Command, Args}, From, State = #state{}) ->
6568
[] ->
6669
{reply, {error, command_not_found}, State};
6770
[{_, ArgsDef, Fun, _Description}] ->
68-
CommandPid = run_command(Command, Args, ArgsDef, From, Fun),
71+
CommandPid = spawn_run_command(Command, Args, ArgsDef, From, Fun),
6972
RunningCommands = State#state.running_commands,
70-
{noreply, State#state{running_commands = [{CommandPid, From} | RunningCommands]}}
73+
{noreply,
74+
State#state{running_commands = [{CommandPid, From} | RunningCommands]}}
7175
end;
7276
handle_call({get_help, Command}, _From, State = #state{}) ->
7377
Help = do_get_help(Command),
@@ -84,37 +88,39 @@ handle_cast(_Request, State = #state{}) ->
8488
-spec handle_info(Info :: term(), State :: term()) ->
8589
{noreply, NewState :: term()}.
8690
handle_info({'EXIT', Pid, normal}, #state{running_commands = Rc} = State) ->
87-
{noreply, State#state{running_commands = lists:delete(Pid, Rc)}};
91+
{noreply, State#state{running_commands = proplists:delete(Pid, Rc)}};
8892
handle_info({'EXIT', Pid, Reason}, #state{running_commands = Rc} = State) ->
8993
case lists:keyfind(Pid, 1, Rc) of
9094
false ->
9195
ok;
9296
{_, From} ->
9397
gen_server:reply(From, {error, Reason})
9498
end,
95-
{noreply, State#state{running_commands = lists:delete(Pid, Rc)}}.
99+
{noreply, State#state{running_commands = proplists:delete(Pid, Rc)}}.
96100

97101
%%%===================================================================
98102
%%% Internal functions
99103
%%%===================================================================
100-
101-
run_command(Command, Args, ArgsDef, From, Fun) ->
104+
spawn_run_command(Command, Args, ArgsDef, From, Fun) ->
102105
spawn_link(fun() ->
103-
Result =
104-
case convert(ArgsDef, Args) of
105-
{ok, NewArgs} ->
106-
case is_not_in_arg_def_but_set(ArgsDef, Args, "help") of
107-
true ->
108-
get_help(Command);
109-
_ ->
110-
MissingArguments = get_missing_arguments(ArgsDef, NewArgs),
111-
evaluate_argument_check(MissingArguments, fun() -> execute_fun(Fun, NewArgs) end)
112-
end;
113-
Else ->
114-
Else
115-
end,
116-
gen_server:reply(From, Result)
117-
end).
106+
CommandResult = run_command(Command, Args, ArgsDef, Fun),
107+
gen_server:reply(From, CommandResult)
108+
end).
109+
110+
run_command(Command, Args, ArgsDef, Fun) ->
111+
case convert(ArgsDef, Args) of
112+
{ok, NewArgs} ->
113+
case is_not_in_arg_def_but_set(ArgsDef, Args, "help") of
114+
true ->
115+
get_help(Command);
116+
_ ->
117+
MissingArguments = get_missing_arguments(ArgsDef, NewArgs),
118+
evaluate_argument_check(MissingArguments,
119+
fun() -> execute_fun(Fun, NewArgs) end)
120+
end;
121+
Else ->
122+
Else
123+
end.
118124

119125
execute_fun(Fun, NewArgs) ->
120126
try
@@ -262,22 +268,22 @@ format_command({Commands, Desc, Args}) ->
262268
[format_arg(Arg) || Arg <- Args]];
263269
format_command({Commands, Desc}) ->
264270
CommandStr = string:pad(string:join(Commands, " "), 32),
265-
cli_console_formatter:text("~s ~s", [CommandStr, Desc]).
271+
cli_console_formatter:text("~ts ~ts", [CommandStr, Desc]).
266272

267273
format_arg(#argument{name = Name, optional = Optional,
268274
default = Default, description = Desc}) ->
269275

270276
CommandStr = string:pad(Name, 30),
271277
ArgData = get_default_string(Default, get_optional_string(Optional, "")),
272-
cli_console_formatter:text(" -~s ~s~n~s~s",
278+
cli_console_formatter:text(" -~ts ~ts~n~ts~ts",
273279
[CommandStr, Desc, lists:duplicate(32, " "), ArgData]).
274280

275281
get_default_string(undefined, Acc) ->
276282
Acc;
277283
get_default_string(Default, Acc) when is_integer(Default) ->
278-
Acc ++ " Default value: " ++ io_lib:format("~s", [integer_to_list(Default)]);
284+
Acc ++ " Default value: " ++ io_lib:format("~ts", [integer_to_list(Default)]);
279285
get_default_string(Default, Acc) ->
280-
Acc ++ " Default value: " ++ io_lib:format("~s", [Default]).
286+
Acc ++ " Default value: " ++ io_lib:format("~ts", [Default]).
281287

282288
get_optional_string(true, Acc) ->
283289
Acc;

src/cli_console_formatter.erl

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515
-behavior(cli_console_output).
1616

1717
%% API
18-
-export([text/1, text/2, title/1,
18+
-export([text/1, text/2,
19+
title/1,
1920
separator/0,
2021
format/1,
2122
alert/1,
2223
list/1,
2324
table/1,
24-
success/1, error/1, warning/1]).
25+
success/1,
26+
error/1,
27+
warning/1]).
2528

2629
-spec text(string()) -> {format, module(), {text, string()}}.
2730
text(String) ->

src/cli_console_output.erl

+3-3
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ output({ok, Data}) ->
3333
format({text, Data}) ->
3434
io:format(Data);
3535
format({format, Module, Format}) ->
36-
io:format(Module:format(Format)).
36+
io:format(apply(Module, format, [Format])).
3737

3838
-spec show_error(command_not_found |
3939
{not_convertible, {Type :: atom(), Value :: term()}} |
4040
{missing_arguments, [command_argument()]}) -> ok.
4141
show_error(command_not_found) ->
4242
io:format("Command not found~n");
4343
show_error({not_convertible, {Name, Type, Value}}) ->
44-
io:format("Illegal parameter: ~s (~p) - ~p~n", [Name, Type, Value]);
44+
io:format("Illegal parameter: ~ts (~p) - ~p~n", [Name, Type, Value]);
4545
show_error({missing_arguments, Args}) ->
4646
io:format("Missing argument: ~n", []),
4747
[missing_argument(Arg) || Arg <- Args],
@@ -53,4 +53,4 @@ show_error(Error) ->
5353
-spec missing_argument(command_argument()) -> ok.
5454
missing_argument(#argument{name = Name, description = Desc}) ->
5555
NameStr = string:pad(Name, 19),
56-
io:format(" * ~s \t ~s~n", [NameStr, Desc]).
56+
io:format(" * ~ts \t ~ts~n", [NameStr, Desc]).

src/cli_console_parser.erl

-2
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,3 @@ equal_sign_parse_evaluate([Key]) ->
7272
{Key, true};
7373
equal_sign_parse_evaluate([NewKey, Data]) ->
7474
{NewKey, Data}.
75-
76-

src/cli_console_sup.erl

+4-9
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,13 @@
1616

1717
-define(SERVER, ?MODULE).
1818

19+
-spec start_link() -> supervisor:startlink_ret().
1920
start_link() ->
2021
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
2122

22-
%% sup_flags() = #{strategy => strategy(), % optional
23-
%% intensity => non_neg_integer(), % optional
24-
%% period => pos_integer()} % optional
25-
%% child_spec() = #{id => child_id(), % mandatory
26-
%% start => mfargs(), % mandatory
27-
%% restart => restart(), % optional
28-
%% shutdown => shutdown(), % optional
29-
%% type => worker(), % optional
30-
%% modules => modules()} % optional
23+
-spec init(Args :: term()) ->
24+
{ok, {SupFlags :: supervisor:sup_flags(),
25+
[ChildSpec :: supervisor:child_spec()]}}.
3126
init([]) ->
3227
SupFlags = #{strategy => one_for_all,
3328
intensity => 1,

test/cli_console_SUITE.erl

+9-1
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,20 @@ simple_command(_Config) ->
160160

161161
?assertEqual("Command not found\n\n\e[37;1mHelp\n\n\e[0mhelp Print help\nshow commands show available commands\nshow tables show database tables\n",
162162
catch_output(fun() -> cli_console:run("unknown command") end)),
163+
?assertEqual("Command not found\n\n\e[37;1mHelp\n\n\e[0mhelp Print help\nshow commands show available commands\nshow tables show database tables\n",
164+
catch_output(fun() -> cli_console:run(["unknown", "command"]) end)),
163165
?assertEqual("Commands\n * show tables: list tales\n * help: print help\n",
164166
catch_output(fun() -> cli_console:run("show commands") end)),
167+
?assertEqual("Commands\n * show tables: list tales\n * help: print help\n",
168+
catch_output(fun() -> cli_console:run(["show", "commands"]) end)),
165169
?assertEqual("\e[37;1mTables\n\n\e[0m * users\n * permissions\n * tweats\n",
166170
catch_output(fun() -> cli_console:run("show tables") end)),
171+
?assertEqual("\e[37;1mTables\n\n\e[0m * users\n * permissions\n * tweats\n",
172+
catch_output(fun() -> cli_console:run(["show", "tables"]) end)),
173+
?assertEqual("\e[37;1mTables\n\n\e[0mListing all tables\n---------------------------------------------------------------\n * users\n * permissions\n * tweats\n",
174+
catch_output(fun() -> cli_console:run("show tables --all") end)),
167175
?assertEqual("\e[37;1mTables\n\n\e[0mListing all tables\n---------------------------------------------------------------\n * users\n * permissions\n * tweats\n",
168-
catch_output(fun() -> cli_console:run("show tables --all") end)).
176+
catch_output(fun() -> cli_console:run(["show", "tables", "--all"]) end)).
169177

170178

171179
table(_Config) ->

0 commit comments

Comments
 (0)