cliqr 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +95 -0
- data/README.md +9 -71
- data/examples/numbers +1 -2
- data/examples/vagrant +0 -3
- data/lib/cliqr.rb +52 -11
- data/lib/cliqr/argument_validation/argument_type_validator.rb +2 -2
- data/lib/cliqr/argument_validation/validator.rb +3 -3
- data/lib/cliqr/{cli → command}/argument_operator.rb +2 -2
- data/lib/cliqr/{cli → command}/argument_operator_context.rb +1 -1
- data/lib/cliqr/{cli/command.rb → command/base_command.rb} +2 -2
- data/lib/cliqr/command/color.rb +174 -0
- data/lib/cliqr/{cli → command}/command_context.rb +68 -20
- data/lib/cliqr/command/shell_banner_builder.rb +17 -0
- data/lib/cliqr/command/shell_command.rb +125 -0
- data/lib/cliqr/command/shell_prompt_builder.rb +26 -0
- data/lib/cliqr/config/action.rb +226 -0
- data/lib/cliqr/config/base.rb +84 -0
- data/lib/cliqr/config/command.rb +137 -0
- data/lib/cliqr/config/dsl.rb +81 -0
- data/lib/cliqr/config/event.rb +43 -0
- data/lib/cliqr/config/event_based.rb +78 -0
- data/lib/cliqr/config/named.rb +55 -0
- data/lib/cliqr/config/option.rb +95 -0
- data/lib/cliqr/config/option_based.rb +130 -0
- data/lib/cliqr/config/shell.rb +87 -0
- data/lib/cliqr/config/validation/validation_set.rb +66 -0
- data/lib/cliqr/config/validation/validator_factory.rb +403 -0
- data/lib/cliqr/config/validation/verifiable.rb +91 -0
- data/lib/cliqr/error.rb +20 -4
- data/lib/cliqr/events/event.rb +56 -0
- data/lib/cliqr/events/event_context.rb +31 -0
- data/lib/cliqr/events/handler.rb +32 -0
- data/lib/cliqr/events/invoker.rb +70 -0
- data/lib/cliqr/{cli → executor}/command_runner_factory.rb +3 -3
- data/lib/cliqr/{cli → executor}/router.rb +4 -4
- data/lib/cliqr/{cli/executor.rb → executor/runner.rb} +25 -10
- data/lib/cliqr/interface.rb +98 -0
- data/lib/cliqr/parser/token_factory.rb +1 -1
- data/lib/cliqr/usage/command_usage_context.rb +94 -0
- data/lib/cliqr/usage/option_usage_context.rb +86 -0
- data/lib/cliqr/usage/templates/partial/action_list.erb +10 -0
- data/lib/cliqr/usage/templates/partial/command_name.erb +3 -0
- data/lib/cliqr/usage/templates/partial/option_list.erb +18 -0
- data/lib/cliqr/usage/templates/partial/usage_info.erb +5 -0
- data/lib/cliqr/usage/templates/usage/cli.erb +4 -0
- data/lib/cliqr/usage/templates/usage/shell.erb +2 -0
- data/lib/cliqr/usage/usage_builder.rb +59 -0
- data/lib/cliqr/util.rb +81 -34
- data/lib/cliqr/version.rb +1 -1
- data/spec/config/action_config_validator_spec.rb +127 -5
- data/spec/config/config_finalize_spec.rb +3 -3
- data/spec/config/config_validator_spec.rb +120 -17
- data/spec/config/option_config_validator_spec.rb +1 -1
- data/spec/dsl/interface_spec.rb +2 -2
- data/spec/dsl/usage_spec.rb +461 -465
- data/spec/executor/action_executor_spec.rb +1 -1
- data/spec/executor/color_executor_spec.rb +125 -0
- data/spec/executor/command_runner_spec.rb +6 -8
- data/spec/executor/event_executor_spec.rb +365 -0
- data/spec/executor/executor_spec.rb +49 -11
- data/spec/executor/help_executor_spec.rb +107 -103
- data/spec/fixtures/action_reader_command.rb +1 -1
- data/spec/fixtures/test_arg_printer_event_handler.rb +9 -0
- data/spec/fixtures/test_color_shell_prompt.rb +13 -0
- data/spec/fixtures/test_empty_event_handler.rb +5 -0
- data/spec/fixtures/test_invoker_event_handler.rb +9 -0
- data/spec/fixtures/test_shell_banner.rb +8 -0
- data/spec/fixtures/test_shell_prompt.rb +13 -0
- data/spec/shell/shell_executor_spec.rb +700 -0
- data/spec/validation/validation_spec.rb +2 -2
- metadata +65 -27
- data/lib/cliqr/cli/config.rb +0 -554
- data/lib/cliqr/cli/interface.rb +0 -107
- data/lib/cliqr/cli/shell_command.rb +0 -69
- data/lib/cliqr/cli/usage_builder.rb +0 -185
- data/lib/cliqr/config_validation/validation_set.rb +0 -48
- data/lib/cliqr/config_validation/validator_factory.rb +0 -319
- data/lib/cliqr/config_validation/verifiable.rb +0 -89
- data/lib/cliqr/dsl.rb +0 -59
- data/spec/executor/shell_executor_spec.rb +0 -233
- data/templates/usage.erb +0 -39
@@ -61,7 +61,7 @@ module Cliqr
|
|
61
61
|
# @param [String] name Long name of the option
|
62
62
|
# @param [String] arg THe argument that was parsed to get the option name
|
63
63
|
#
|
64
|
-
# @return [Cliqr::CLI::
|
64
|
+
# @return [Cliqr::CLI::Option] Requested option configuration
|
65
65
|
def get_option_config(name, arg)
|
66
66
|
fail Cliqr::Error::UnknownCommandOption,
|
67
67
|
"unknown option \"#{arg}\"" unless @config.option?(name)
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'cliqr/usage/option_usage_context'
|
4
|
+
|
5
|
+
module Cliqr
|
6
|
+
module Usage
|
7
|
+
# The context in which the usage template will be executed
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class CommandUsageContext
|
11
|
+
include Cliqr::Command::Color
|
12
|
+
|
13
|
+
# Name of the current command in context
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
attr_reader :name
|
17
|
+
|
18
|
+
# Description of the current command
|
19
|
+
#
|
20
|
+
# @return [String]
|
21
|
+
attr_reader :description
|
22
|
+
|
23
|
+
# Pre-configured command's actions
|
24
|
+
#
|
25
|
+
# @return [Array<Cliqr::CLI::CommandUsageContext>]
|
26
|
+
attr_reader :actions
|
27
|
+
|
28
|
+
# List of options configured for current context
|
29
|
+
#
|
30
|
+
# @return [Array<Cliqr::CLI::OptionUsageContext>]
|
31
|
+
attr_reader :options
|
32
|
+
|
33
|
+
# Command for the current context
|
34
|
+
#
|
35
|
+
# @return [String]
|
36
|
+
attr_reader :command
|
37
|
+
|
38
|
+
# Wrap a [Cliqr::CLI::Config] instance for usage template
|
39
|
+
def initialize(type, config)
|
40
|
+
super(config)
|
41
|
+
|
42
|
+
@type = type
|
43
|
+
@config = config
|
44
|
+
|
45
|
+
@name = config.name
|
46
|
+
@description = config.description
|
47
|
+
@actions = @config.actions
|
48
|
+
.map { |action| CommandUsageContext.new(type, action) }
|
49
|
+
.select { |action| type == :shell ? action.name != 'shell' : true }
|
50
|
+
@options = @config.options.values.map { |option| Usage::OptionUsageContext.new(option) }
|
51
|
+
@command = @config.command
|
52
|
+
end
|
53
|
+
|
54
|
+
# Check if command has a description
|
55
|
+
def description?
|
56
|
+
non_empty?(@description)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Check if there are any preconfigured options
|
60
|
+
def options?
|
61
|
+
non_empty?(@config.options)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Check if current command allows arguments
|
65
|
+
def arguments?
|
66
|
+
@config.arguments == Cliqr::Config::ENABLE_CONFIG
|
67
|
+
end
|
68
|
+
|
69
|
+
# Check if current command has any actions
|
70
|
+
def actions?
|
71
|
+
non_empty?(@actions)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Check if the help is enabled
|
75
|
+
#
|
76
|
+
# @return [Boolean]
|
77
|
+
def help?
|
78
|
+
@config.help?
|
79
|
+
end
|
80
|
+
|
81
|
+
# Check if running inside shell
|
82
|
+
def shell?
|
83
|
+
@type == :shell
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# Check if a obj is non-empty
|
89
|
+
def non_empty?(obj)
|
90
|
+
!(obj.nil? || obj.empty?)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Cliqr
|
4
|
+
module Usage
|
5
|
+
# Wrapper of [Cliqr::CLI::OptionConfig] to be used in usage rendering
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class OptionUsageContext
|
9
|
+
# Name of the option
|
10
|
+
#
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :name
|
13
|
+
|
14
|
+
# Short name of the option
|
15
|
+
#
|
16
|
+
# @return [String]
|
17
|
+
attr_reader :short
|
18
|
+
|
19
|
+
# Option's type
|
20
|
+
#
|
21
|
+
# @return [Symbol]
|
22
|
+
attr_reader :type
|
23
|
+
|
24
|
+
# Option's description
|
25
|
+
#
|
26
|
+
# @return [String]
|
27
|
+
attr_reader :description
|
28
|
+
|
29
|
+
# Default value for this option
|
30
|
+
#
|
31
|
+
# @return [Object]
|
32
|
+
attr_reader :default
|
33
|
+
|
34
|
+
# Create a new option usage context
|
35
|
+
def initialize(option_config)
|
36
|
+
@option_config = option_config
|
37
|
+
|
38
|
+
@name = @option_config.name
|
39
|
+
@short = @option_config.short
|
40
|
+
@type = @option_config.type
|
41
|
+
@description = @option_config.description
|
42
|
+
@default = @option_config.default
|
43
|
+
end
|
44
|
+
|
45
|
+
# Check if current option is a boolean option
|
46
|
+
def boolean?
|
47
|
+
@option_config.boolean? && !help? && !version?
|
48
|
+
end
|
49
|
+
|
50
|
+
# Check if the option has a short name
|
51
|
+
def short?
|
52
|
+
@option_config.short?
|
53
|
+
end
|
54
|
+
|
55
|
+
# Check if the option has non-empty description
|
56
|
+
def description?
|
57
|
+
@option_config.description?
|
58
|
+
end
|
59
|
+
|
60
|
+
# Assert if the details of this options should be printed
|
61
|
+
def details?
|
62
|
+
@option_config.description? || @option_config.type? || @option_config.default?
|
63
|
+
end
|
64
|
+
|
65
|
+
# Check if the option has a non-default type
|
66
|
+
def type?
|
67
|
+
@option_config.type? && !help? && !version?
|
68
|
+
end
|
69
|
+
|
70
|
+
# check if the option should display default setting
|
71
|
+
def default?
|
72
|
+
@option_config.default? && !help? && !version?
|
73
|
+
end
|
74
|
+
|
75
|
+
# Check if the option is for getting help
|
76
|
+
def help?
|
77
|
+
@option_config.name == 'help'
|
78
|
+
end
|
79
|
+
|
80
|
+
# Check if the option is for version
|
81
|
+
def version?
|
82
|
+
@option_config.name == 'version'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<% if actions? %>
|
2
|
+
<%= bold('Available actions:') %>
|
3
|
+
<%= black("[ Type \"#{"#{command} " unless shell?}help [action-name]\" to get more information about that action ]\n") if help? %>
|
4
|
+
<%
|
5
|
+
actions.each { |action|
|
6
|
+
%> <%= green(action.name) %><%= " -- #{action.description}" if action.description? %>
|
7
|
+
<%
|
8
|
+
}
|
9
|
+
%>
|
10
|
+
<% end %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<% if options? %>
|
2
|
+
<%= bold('Available options:') %>
|
3
|
+
<%
|
4
|
+
options.each { |option|
|
5
|
+
%>
|
6
|
+
--<%=
|
7
|
+
'[no-]' if option.boolean?
|
8
|
+
%><%=
|
9
|
+
option.name
|
10
|
+
%><%=
|
11
|
+
", -#{option.short}" if option.short?
|
12
|
+
%><%=
|
13
|
+
if option.details?
|
14
|
+
" : #{" <#{option.type}>" if option.type?}#{" #{option.description}" if option.description?}#{" (default => #{option.default.inspect})" if option.default?}"
|
15
|
+
end
|
16
|
+
%><%
|
17
|
+
}
|
18
|
+
end %>
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
require 'cliqr/error'
|
5
|
+
require 'cliqr/usage/command_usage_context'
|
6
|
+
|
7
|
+
module Cliqr
|
8
|
+
module Usage
|
9
|
+
# Builds the usage information based on the configuration settings
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
class UsageBuilder
|
13
|
+
TEMPLATES_PATH = "#{File.expand_path(File.dirname(__FILE__))}/templates"
|
14
|
+
|
15
|
+
USAGE_TYPES = {
|
16
|
+
:cli => "#{TEMPLATES_PATH}/usage/cli.erb",
|
17
|
+
:shell => "#{TEMPLATES_PATH}/usage/shell.erb"
|
18
|
+
}
|
19
|
+
|
20
|
+
# Create a new usage builder
|
21
|
+
def initialize(type)
|
22
|
+
@type = type
|
23
|
+
@template_file = USAGE_TYPES[type]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Build the usage information
|
27
|
+
#
|
28
|
+
# @param [Cliqr::Config::Command] config Configuration of the command line interface
|
29
|
+
#
|
30
|
+
# @return [String]
|
31
|
+
def build(config)
|
32
|
+
usage_context = Usage::CommandUsageContext.new(@type, config)
|
33
|
+
usage_context.instance_eval do
|
34
|
+
def render(partial_name)
|
35
|
+
TemplateRenderer.render("#{TEMPLATES_PATH}/partial/#{partial_name}.erb", self)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Add a extra newline at the end of usage text
|
40
|
+
"#{TemplateRenderer.render(@template_file, usage_context)}\n"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Renders a template file based on configuration settings
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
class TemplateRenderer
|
48
|
+
# Render a partial script
|
49
|
+
#
|
50
|
+
# @return [Nothing]
|
51
|
+
def self.render(template_file_path, context)
|
52
|
+
template_file_path = File.expand_path(template_file_path, __FILE__)
|
53
|
+
template = ERB.new(File.new(template_file_path).read, nil, '%')
|
54
|
+
result = template.result(context.instance_eval { binding })
|
55
|
+
Cliqr::Util.trim_newlines(result)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/cliqr/util.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require 'cliqr/
|
3
|
+
require 'cliqr/command/shell_command'
|
4
4
|
|
5
5
|
module Cliqr
|
6
6
|
# Utility methods
|
@@ -17,59 +17,59 @@ module Cliqr
|
|
17
17
|
|
18
18
|
# Build a help action for a parent config
|
19
19
|
#
|
20
|
-
# @return [Cliqr::CLI::
|
20
|
+
# @return [Cliqr::CLI::Action] New action config
|
21
21
|
def self.build_help_action(config)
|
22
|
-
|
23
|
-
name 'help'
|
24
|
-
description
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
Cliqr::Config::Action.new.tap do |action_config|
|
23
|
+
action_config.name = 'help'
|
24
|
+
action_config.description = \
|
25
|
+
"The help action for command \"#{config.command}\" which provides details " \
|
26
|
+
'and usage information on how to use the command.'
|
27
|
+
action_config.handler = Util.help_action_handler(config)
|
28
|
+
action_config.help = :disable if config.help?
|
29
|
+
action_config.finalize
|
29
30
|
end
|
30
|
-
cli.config
|
31
31
|
end
|
32
32
|
|
33
33
|
# Build a help option for a parent config
|
34
34
|
#
|
35
|
-
# @return [Cliqr::CLI::
|
35
|
+
# @return [Cliqr::CLI::Option] New option config
|
36
36
|
def self.build_help_option(config)
|
37
|
-
Cliqr::
|
37
|
+
Cliqr::Config::Option.new.tap do |option_config|
|
38
38
|
option_config.name = 'help'
|
39
39
|
option_config.short = 'h'
|
40
40
|
option_config.description = "Get helpful information for action \"#{config.command}\" " \
|
41
41
|
'along with its usage information.'
|
42
|
-
option_config.type = Cliqr::
|
43
|
-
option_config.operator = Cliqr::
|
42
|
+
option_config.type = Cliqr::Config::BOOLEAN_ARGUMENT_TYPE
|
43
|
+
option_config.operator = Cliqr::Command::ArgumentOperator::DEFAULT_ARGUMENT_OPERATOR
|
44
44
|
option_config.finalize
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
48
|
# Build a version action for a parent config
|
49
49
|
#
|
50
|
-
# @return [Cliqr::CLI::
|
50
|
+
# @return [Cliqr::CLI::Action] New action config
|
51
51
|
def self.build_version_action(config)
|
52
|
-
|
53
|
-
name 'version'
|
54
|
-
description "Get version information for command \"#{config.command}\"."
|
55
|
-
|
52
|
+
Cliqr::Config::Action.new.tap do |action_config|
|
53
|
+
action_config.name = 'version'
|
54
|
+
action_config.description = "Get version information for command \"#{config.command}\"."
|
55
|
+
action_config.arguments = Cliqr::Config::DISABLE_CONFIG
|
56
|
+
action_config.handler = proc do
|
56
57
|
puts config.version
|
57
58
|
end
|
58
|
-
|
59
|
+
action_config.finalize
|
59
60
|
end
|
60
|
-
cli.config
|
61
61
|
end
|
62
62
|
|
63
63
|
# Build a version option for a parent config
|
64
64
|
#
|
65
|
-
# @return [Cliqr::CLI::
|
65
|
+
# @return [Cliqr::CLI::Option] New option config
|
66
66
|
def self.build_version_option(config)
|
67
|
-
Cliqr::
|
67
|
+
Cliqr::Config::Option.new.tap do |option_config|
|
68
68
|
option_config.name = 'version'
|
69
69
|
option_config.short = 'v'
|
70
70
|
option_config.description = "Get version information for command \"#{config.command}\"."
|
71
|
-
option_config.type = Cliqr::
|
72
|
-
option_config.operator = Cliqr::
|
71
|
+
option_config.type = Cliqr::Config::BOOLEAN_ARGUMENT_TYPE
|
72
|
+
option_config.operator = Cliqr::Command::ArgumentOperator::DEFAULT_ARGUMENT_OPERATOR
|
73
73
|
option_config.finalize
|
74
74
|
end
|
75
75
|
end
|
@@ -82,21 +82,53 @@ module Cliqr
|
|
82
82
|
fail Cliqr::Error::IllegalArgumentError,
|
83
83
|
"too many arguments for \"#{command}\" command" if arguments.length > 1
|
84
84
|
action_config = arguments.length == 0 ? config : config.action(arguments.first)
|
85
|
-
puts Cliqr::
|
85
|
+
puts Cliqr::Usage::UsageBuilder.new(environment).build(action_config)
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
89
|
# Build a shell action for a parent config
|
90
90
|
#
|
91
|
-
# @return [Cliqr::CLI::
|
92
|
-
def self.build_shell_action(config)
|
93
|
-
|
94
|
-
name
|
95
|
-
|
96
|
-
|
97
|
-
|
91
|
+
# @return [Cliqr::CLI::Action] New action config
|
92
|
+
def self.build_shell_action(config, shell_config)
|
93
|
+
Cliqr::Config::Action.new.tap do |action_config|
|
94
|
+
if shell_config.name?
|
95
|
+
action_config.name = shell_config.name
|
96
|
+
else
|
97
|
+
action_config.name = 'shell'
|
98
|
+
end
|
99
|
+
|
100
|
+
action_config.handler = Cliqr::Command::ShellCommand.new(shell_config)
|
101
|
+
action_config.arguments = Cliqr::Config::DISABLE_CONFIG
|
102
|
+
|
103
|
+
# allow description to be overridden
|
104
|
+
description = shell_config.description
|
105
|
+
description = "Execute a shell in the context of \"#{config.command}\" command." \
|
106
|
+
unless shell_config.description?
|
107
|
+
action_config.description = description
|
108
|
+
|
109
|
+
action_config.options = shell_config.options
|
110
|
+
action_config.events = shell_config.events
|
111
|
+
|
112
|
+
action_config.instance_eval do
|
113
|
+
def skip_validation?
|
114
|
+
true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
action_config.finalize
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Build shell config for a parent config
|
123
|
+
#
|
124
|
+
# @return [Cliqr::CLI::Shell] New action config
|
125
|
+
def self.build_shell_config(config)
|
126
|
+
Cliqr::Config::Shell.new.tap do |shell_config|
|
127
|
+
shell_config.enabled = config.actions?
|
128
|
+
shell_config.prompt = Command::ShellPromptBuilder.new(config)
|
129
|
+
shell_config.banner = Command::ShellBannerBuilder.new
|
130
|
+
shell_config.finalize
|
98
131
|
end
|
99
|
-
cli.config
|
100
132
|
end
|
101
133
|
|
102
134
|
# Sanitize raw command line arguments
|
@@ -128,8 +160,23 @@ module Cliqr
|
|
128
160
|
# @return [Proc]
|
129
161
|
def self.forward_to_help_handler
|
130
162
|
proc do
|
163
|
+
fail Cliqr::Error::IllegalArgumentError,
|
164
|
+
'no arguments allowed for default help action' unless arguments.empty?
|
131
165
|
forward "#{command} help"
|
132
166
|
end
|
133
167
|
end
|
168
|
+
|
169
|
+
# Remove newlines from the end of a string
|
170
|
+
#
|
171
|
+
# @return [String]
|
172
|
+
def self.trim_newlines(str)
|
173
|
+
index = str.length - 1
|
174
|
+
count = 0
|
175
|
+
while str[index] == "\n" && index >= 0
|
176
|
+
count += 1
|
177
|
+
index -= 1
|
178
|
+
end
|
179
|
+
str[0...-count]
|
180
|
+
end
|
134
181
|
end
|
135
182
|
end
|