eac_cli 0.7.0 → 0.11.1
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/lib/eac_cli/core_ext.rb +4 -0
- data/lib/eac_cli/definition.rb +35 -3
- data/lib/eac_cli/definition/base_option.rb +17 -1
- data/lib/eac_cli/definition/help_formatter.rb +76 -0
- data/lib/eac_cli/definition/positional_argument.rb +21 -4
- data/lib/eac_cli/docopt/runner_extension.rb +1 -0
- data/lib/eac_cli/parser/collector.rb +4 -0
- data/lib/eac_cli/parser/options_collection.rb +27 -64
- data/lib/eac_cli/parser/parse_result.rb +17 -0
- data/lib/eac_cli/parser/positional_collection.rb +47 -19
- data/lib/eac_cli/patches.rb +4 -0
- data/lib/eac_cli/patches/object.rb +5 -0
- data/lib/eac_cli/patches/object/runner_with.rb +25 -0
- data/lib/eac_cli/runner.rb +5 -1
- data/lib/eac_cli/runner/context.rb +19 -2
- data/lib/eac_cli/runner_with/subcommands.rb +96 -0
- data/lib/eac_cli/version.rb +1 -1
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 395665ef14dad48803a7bfcdf622beb5838b10fda82ba2a013e5653956ab73dd
|
4
|
+
data.tar.gz: 7836b0bb93b6ac2b6881a0e232880d94ed6601f79febc1fe45789106bbb29360
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 40b7358bc737f3a6326801698e27f00bb8947a5e7bcbd18e20d8eaa7c31e2d9bc78f13381b04ccdb63b811692d8738acf0eb2dedb0c140facd58d5cd10e533fe
|
7
|
+
data.tar.gz: 8dc8fafeca39393c741c8f465202a370185e354ef1669b23761fac7e203ead1076af17b0fd61adcf369baf655a9db472e6e2ca30bd081d1200e4aa3a5fdd7a52
|
data/lib/eac_cli/definition.rb
CHANGED
@@ -8,6 +8,10 @@ require 'eac_ruby_utils/core_ext'
|
|
8
8
|
module EacCli
|
9
9
|
class Definition
|
10
10
|
require_sub __FILE__
|
11
|
+
|
12
|
+
SUBCOMMAND_NAME_ARG = 'subcommand'
|
13
|
+
SUBCOMMAND_ARGS_ARG = 'subcommand_args'
|
14
|
+
|
11
15
|
attr_accessor :description
|
12
16
|
attr_accessor :options_argument
|
13
17
|
|
@@ -41,6 +45,10 @@ module EacCli
|
|
41
45
|
self.description = description
|
42
46
|
end
|
43
47
|
|
48
|
+
def help_formatter
|
49
|
+
@help_formatter ||= ::EacCli::Definition::HelpFormatter.new(self)
|
50
|
+
end
|
51
|
+
|
44
52
|
def options_arg(options_argument)
|
45
53
|
self.options_argument = options_argument
|
46
54
|
end
|
@@ -50,15 +58,33 @@ module EacCli
|
|
50
58
|
end
|
51
59
|
|
52
60
|
def pos_arg(name, arg_options = {})
|
53
|
-
|
61
|
+
new_pos_arg = ::EacCli::Definition::PositionalArgument.new(name, arg_options)
|
62
|
+
raise 'Positional arguments are blocked' if positional_arguments_blocked?(new_pos_arg)
|
63
|
+
|
64
|
+
pos_set << new_pos_arg
|
54
65
|
end
|
55
66
|
|
56
67
|
def positional
|
57
|
-
|
68
|
+
pos_set.to_a
|
69
|
+
end
|
70
|
+
|
71
|
+
def positional_arguments_blocked?(new_pos_arg)
|
72
|
+
last = pos_set.last
|
73
|
+
return false unless last
|
74
|
+
return true if last.repeat?
|
75
|
+
return true if last.optional? && new_pos_arg.required?
|
76
|
+
|
77
|
+
false
|
58
78
|
end
|
59
79
|
|
60
80
|
def subcommands
|
61
|
-
|
81
|
+
pos_arg(SUBCOMMAND_NAME_ARG, subcommand: true)
|
82
|
+
pos_set << ::EacCli::Definition::PositionalArgument.new(SUBCOMMAND_ARGS_ARG,
|
83
|
+
optional: true, repeat: true)
|
84
|
+
end
|
85
|
+
|
86
|
+
def subcommands?
|
87
|
+
pos_set.any?(&:subcommand?)
|
62
88
|
end
|
63
89
|
|
64
90
|
def options_first(enable = true)
|
@@ -68,5 +94,11 @@ module EacCli
|
|
68
94
|
def options_first?
|
69
95
|
@options_first ? true : false
|
70
96
|
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def pos_set
|
101
|
+
@pos_set ||= []
|
102
|
+
end
|
71
103
|
end
|
72
104
|
end
|
@@ -5,19 +5,35 @@ require 'eac_ruby_utils/core_ext'
|
|
5
5
|
module EacCli
|
6
6
|
class Definition
|
7
7
|
class BaseOption
|
8
|
+
DEFAULT_REQUIRED = false
|
9
|
+
|
10
|
+
enable_listable
|
11
|
+
lists.add_symbol :option, :optional, :usage, :required
|
8
12
|
attr_reader :short, :long, :description, :options
|
9
13
|
|
10
14
|
def initialize(short, long, description, options = {})
|
11
15
|
@short = short
|
12
16
|
@long = long
|
13
17
|
@description = description
|
14
|
-
@options = options.
|
18
|
+
@options = options.symbolize_keys
|
19
|
+
@options.assert_valid_keys(::EacCli::Definition::BaseOption.lists.option.values)
|
15
20
|
end
|
16
21
|
|
17
22
|
def identifier
|
18
23
|
long.to_s.variableize.to_sym
|
19
24
|
end
|
20
25
|
|
26
|
+
def required?
|
27
|
+
return true if options.key?(:required) && options.fetch(:required)
|
28
|
+
return false if options.key?(:optional) && options.fetch(:optional)
|
29
|
+
|
30
|
+
DEFAULT_REQUIRED
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
"#{self.class.name.demodulize}[#{identifier}]"
|
35
|
+
end
|
36
|
+
|
21
37
|
def show_on_usage?
|
22
38
|
options[:usage]
|
23
39
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'eac_ruby_utils/core_ext'
|
4
|
+
|
5
|
+
module EacCli
|
6
|
+
class Definition
|
7
|
+
class HelpFormatter
|
8
|
+
SEP = ' '
|
9
|
+
IDENT = SEP * 2
|
10
|
+
OPTION_DESC_SEP = IDENT * 2
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def option_long(option)
|
14
|
+
b = option.long
|
15
|
+
b += '=VALUE' if option.argument?
|
16
|
+
b
|
17
|
+
end
|
18
|
+
|
19
|
+
def option_short(option)
|
20
|
+
b = option.short
|
21
|
+
b += 'VALUE' if option.argument?
|
22
|
+
b
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
common_constructor :definition
|
27
|
+
|
28
|
+
def positional_argument(positional)
|
29
|
+
if positional.subcommand?
|
30
|
+
::EacRubyUtils::Console::DocoptRunner::SUBCOMMANDS_MACRO
|
31
|
+
else
|
32
|
+
r = "<#{positional.name}>"
|
33
|
+
r += '...' if positional.repeat?
|
34
|
+
r = "[#{r}]" if positional.optional?
|
35
|
+
r
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def section(header, include_header = true)
|
40
|
+
b = include_header ? "#{header.humanize}:\n" : ''
|
41
|
+
b += send("self_#{header}") + "\n"
|
42
|
+
# TO-DO: implement alternatives
|
43
|
+
b
|
44
|
+
end
|
45
|
+
|
46
|
+
def self_options
|
47
|
+
definition.options.map { |option| IDENT + option_definition(option) }.join("\n")
|
48
|
+
end
|
49
|
+
|
50
|
+
def self_usage
|
51
|
+
IDENT + self_usage_arguments.join(SEP)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self_usage_arguments
|
55
|
+
[::EacRubyUtils::Console::DocoptRunner::PROGRAM_MACRO] +
|
56
|
+
definition.options_argument.if_present([]) { |_v| ['[options]'] } +
|
57
|
+
self_usage_arguments_options +
|
58
|
+
self_usage_arguments_positional
|
59
|
+
end
|
60
|
+
|
61
|
+
def self_usage_arguments_options
|
62
|
+
definition.options.select(&:show_on_usage?).map do |option|
|
63
|
+
self.class.option_long(option)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def self_usage_arguments_positional
|
68
|
+
definition.positional.map { |p| positional_argument(p) }
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_banner
|
72
|
+
"#{definition.description}\n\n#{section('usage')}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -5,22 +5,39 @@ require 'eac_ruby_utils/core_ext'
|
|
5
5
|
module EacCli
|
6
6
|
class Definition
|
7
7
|
class PositionalArgument
|
8
|
-
|
8
|
+
DEFAULT_REQUIRED = true
|
9
|
+
|
10
|
+
enable_listable
|
11
|
+
lists.add_symbol :option, :optional, :repeat, :required, :subcommand
|
12
|
+
common_constructor :name, :options, default: [{}] do
|
13
|
+
options.assert_valid_keys(self.class.lists.option.values)
|
14
|
+
end
|
9
15
|
|
10
16
|
def identifier
|
11
17
|
name.to_s.variableize.to_sym
|
12
18
|
end
|
13
19
|
|
14
20
|
def optional?
|
15
|
-
|
21
|
+
!required?
|
16
22
|
end
|
17
23
|
|
18
24
|
def repeat?
|
19
|
-
options[
|
25
|
+
options[OPTION_REPEAT]
|
26
|
+
end
|
27
|
+
|
28
|
+
def required?
|
29
|
+
return true if options.key?(OPTION_REQUIRED) && options.fetch(OPTION_REQUIRED)
|
30
|
+
return false if options.key?(OPTION_OPTIONAL) && options.fetch(OPTION_OPTIONAL)
|
31
|
+
|
32
|
+
DEFAULT_REQUIRED
|
20
33
|
end
|
21
34
|
|
22
35
|
def subcommand?
|
23
|
-
options[
|
36
|
+
options[OPTION_SUBCOMMAND]
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
"#{self.class.name.demodulize}[#{identifier}]"
|
24
41
|
end
|
25
42
|
end
|
26
43
|
end
|
@@ -6,28 +6,46 @@ require 'optparse'
|
|
6
6
|
module EacCli
|
7
7
|
class Parser
|
8
8
|
class OptionsCollection
|
9
|
-
SEP = ' '
|
10
|
-
IDENT = SEP * 2
|
11
|
-
OPTION_DESC_SEP = IDENT * 2
|
12
|
-
|
13
9
|
enable_simple_cache
|
14
10
|
common_constructor(:definition, :argv, :collector) { collect }
|
15
11
|
attr_reader :arguments
|
16
12
|
|
13
|
+
def options_first?
|
14
|
+
definition.options_first? || definition.subcommands?
|
15
|
+
end
|
16
|
+
|
17
17
|
private
|
18
18
|
|
19
|
+
def check_required_options
|
20
|
+
definition.options.each do |option|
|
21
|
+
next unless option.required?
|
22
|
+
next if collector.supplied?(option)
|
23
|
+
|
24
|
+
raise ::EacCli::Parser::Error.new(
|
25
|
+
definition, argv, "Option \"#{option}\" is required and a value was not supplied"
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
19
30
|
def collect
|
20
31
|
build_banner
|
21
32
|
build_options
|
22
|
-
|
33
|
+
parse_argv
|
34
|
+
check_required_options
|
23
35
|
end
|
24
36
|
|
25
37
|
def option_parser_uncached
|
26
38
|
::OptionParser.new
|
27
39
|
end
|
28
40
|
|
41
|
+
def parse_argv
|
42
|
+
@arguments = options_first? ? option_parser.order(argv) : option_parser.parse(argv)
|
43
|
+
rescue ::OptionParser::InvalidOption => e
|
44
|
+
raise ::EacCli::Parser::Error.new(definition, argv, e.message)
|
45
|
+
end
|
46
|
+
|
29
47
|
def build_banner
|
30
|
-
option_parser.banner =
|
48
|
+
option_parser.banner = definition.help_formatter.to_banner
|
31
49
|
end
|
32
50
|
|
33
51
|
def build_options
|
@@ -38,68 +56,13 @@ module EacCli
|
|
38
56
|
|
39
57
|
def build_option(option)
|
40
58
|
option_parser.on(
|
41
|
-
*[option_short(option),
|
59
|
+
*[::EacCli::Definition::HelpFormatter.option_short(option),
|
60
|
+
::EacCli::Definition::HelpFormatter.option_long(option),
|
61
|
+
option.description].reject(&:blank?)
|
42
62
|
) do |value|
|
43
63
|
collector.collect(option, value)
|
44
64
|
end
|
45
65
|
end
|
46
|
-
|
47
|
-
def positional_argument(positional)
|
48
|
-
if positional.subcommand?
|
49
|
-
::EacRubyUtils::Console::DocoptRunner::SUBCOMMANDS_MACRO
|
50
|
-
else
|
51
|
-
r = "<#{positional.name}>"
|
52
|
-
r += '...' if positional.repeat?
|
53
|
-
r = "[#{r}]" if positional.optional?
|
54
|
-
r
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def option_argument(option)
|
59
|
-
option_long(option)
|
60
|
-
end
|
61
|
-
|
62
|
-
def option_long(option)
|
63
|
-
b = option.long
|
64
|
-
b += '=VALUE' if option.argument?
|
65
|
-
b
|
66
|
-
end
|
67
|
-
|
68
|
-
def option_short(option)
|
69
|
-
b = option.short
|
70
|
-
b += 'VALUE' if option.argument?
|
71
|
-
b
|
72
|
-
end
|
73
|
-
|
74
|
-
def section(header, include_header = true)
|
75
|
-
b = include_header ? "#{header.humanize}:\n" : ''
|
76
|
-
b += send("self_#{header}") + "\n"
|
77
|
-
# TO-DO: implement alternatives
|
78
|
-
b
|
79
|
-
end
|
80
|
-
|
81
|
-
def self_options
|
82
|
-
definition.options.map { |option| IDENT + option_definition(option) }.join("\n")
|
83
|
-
end
|
84
|
-
|
85
|
-
def self_usage
|
86
|
-
IDENT + self_usage_arguments.join(SEP)
|
87
|
-
end
|
88
|
-
|
89
|
-
def self_usage_arguments
|
90
|
-
[::EacRubyUtils::Console::DocoptRunner::PROGRAM_MACRO] +
|
91
|
-
definition.options_argument.if_present([]) { |_v| ['[options]'] } +
|
92
|
-
self_usage_arguments_options +
|
93
|
-
self_usage_arguments_positional
|
94
|
-
end
|
95
|
-
|
96
|
-
def self_usage_arguments_options
|
97
|
-
definition.options.select(&:show_on_usage?).map { |option| option_argument(option) }
|
98
|
-
end
|
99
|
-
|
100
|
-
def self_usage_arguments_positional
|
101
|
-
definition.positional.map { |p| positional_argument(p) }
|
102
|
-
end
|
103
66
|
end
|
104
67
|
end
|
105
68
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'eac_cli/parser/error'
|
3
4
|
require 'eac_ruby_utils/core_ext'
|
4
5
|
|
5
6
|
module EacCli
|
@@ -8,6 +9,20 @@ module EacCli
|
|
8
9
|
common_constructor :definition, :argv
|
9
10
|
|
10
11
|
def result
|
12
|
+
result_or_error = parse(definition)
|
13
|
+
return result_or_error unless result_or_error.is_a?(::Exception)
|
14
|
+
|
15
|
+
definition.alternatives.each do |alternative|
|
16
|
+
alt_result_or_error = parse(alternative)
|
17
|
+
return alt_result_or_error unless alt_result_or_error.is_a?(::Exception)
|
18
|
+
end
|
19
|
+
|
20
|
+
raise result_or_error
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def parse(definition)
|
11
26
|
::EacCli::Parser::Collector.to_data(definition) do |collector|
|
12
27
|
::EacCli::Parser::PositionalCollection.new(
|
13
28
|
definition,
|
@@ -15,6 +30,8 @@ module EacCli
|
|
15
30
|
collector
|
16
31
|
)
|
17
32
|
end
|
33
|
+
rescue ::EacCli::Parser::Error => e
|
34
|
+
e
|
18
35
|
end
|
19
36
|
end
|
20
37
|
end
|
@@ -10,39 +10,67 @@ module EacCli
|
|
10
10
|
|
11
11
|
private
|
12
12
|
|
13
|
+
def argv_enum
|
14
|
+
@argv_enum ||= argv.each
|
15
|
+
end
|
16
|
+
|
17
|
+
def argv_pending?
|
18
|
+
argv_enum.ongoing?
|
19
|
+
end
|
20
|
+
|
21
|
+
def argv_pending_check
|
22
|
+
return unless argv_pending?
|
23
|
+
return unless positional_enum.stopped?
|
24
|
+
|
25
|
+
raise ::EacCli::Parser::Error.new(
|
26
|
+
definition, argv, "No positional left for argv \"#{argv_enum.current}\""
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
13
30
|
def collected
|
14
31
|
@collected ||= ::Set.new
|
15
32
|
end
|
16
33
|
|
17
34
|
def collect
|
18
|
-
|
19
|
-
|
35
|
+
loop do
|
36
|
+
break unless enums('pending?').any?
|
20
37
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
38
|
+
enums('pending_check')
|
39
|
+
collect_argv_value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def collect_argv_value
|
44
|
+
collector.collect(*enums('enum', &:peek))
|
45
|
+
collected << positional_enum.peek
|
46
|
+
positional_enum.next unless positional_enum.peek.repeat?
|
47
|
+
argv_enum.next
|
25
48
|
end
|
26
49
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
50
|
+
def enums(method_suffix, &block)
|
51
|
+
%w[positional argv].map do |method_prefix|
|
52
|
+
v = send("#{method_prefix}_#{method_suffix}")
|
53
|
+
block ? block.call(v) : v
|
54
|
+
end
|
31
55
|
end
|
32
56
|
|
33
|
-
def
|
34
|
-
|
35
|
-
collected.include?(current_positional))
|
57
|
+
def positional_enum
|
58
|
+
@positional_enum ||= definition.positional.each
|
36
59
|
end
|
37
60
|
|
38
|
-
def
|
39
|
-
|
61
|
+
def positional_pending?
|
62
|
+
!(positional_enum.stopped? || positional_enum.current.optional? ||
|
63
|
+
collected.include?(positional_enum.current))
|
40
64
|
end
|
41
65
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
|
66
|
+
def positional_pending_check
|
67
|
+
return unless positional_pending?
|
68
|
+
return unless argv_enum.stopped?
|
69
|
+
|
70
|
+
raise ::EacCli::Parser::Error.new(
|
71
|
+
definition, argv, 'No value for required positional ' \
|
72
|
+
"\"#{positional_enum.current.identifier}\""
|
73
|
+
)
|
46
74
|
end
|
47
75
|
end
|
48
76
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'eac_ruby_utils/core_ext'
|
4
|
+
require 'eac_cli/runner'
|
5
|
+
require 'eac_cli/runner_with'
|
6
|
+
|
7
|
+
class Object
|
8
|
+
def runner_with(*runners, &block)
|
9
|
+
include ::EacCli::Runner
|
10
|
+
enable_simple_cache
|
11
|
+
enable_console_speaker
|
12
|
+
runners.each do |runner|
|
13
|
+
include runner_with_to_module(runner)
|
14
|
+
end
|
15
|
+
runner_definition(&block) if block
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def runner_with_to_module(runner)
|
21
|
+
return runner if runner.is_a?(::Module)
|
22
|
+
|
23
|
+
"EacCli::RunnerWith::#{runner.to_s.camelize}".constantize
|
24
|
+
end
|
25
|
+
end
|
data/lib/eac_cli/runner.rb
CHANGED
@@ -18,6 +18,10 @@ module EacCli
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
+
def runner?(object)
|
22
|
+
object.is_a?(::Class) && object.included_modules.include?(::EacCli::Runner)
|
23
|
+
end
|
24
|
+
|
21
25
|
private
|
22
26
|
|
23
27
|
def alias_class_method(klass, from, to)
|
@@ -45,7 +49,7 @@ module EacCli
|
|
45
49
|
module AfterClassMethods
|
46
50
|
def create(*runner_context_args)
|
47
51
|
r = new
|
48
|
-
r.runner_context = ::EacCli::Runner::Context.new(*runner_context_args)
|
52
|
+
r.runner_context = ::EacCli::Runner::Context.new(r, *runner_context_args)
|
49
53
|
r
|
50
54
|
end
|
51
55
|
|
@@ -5,13 +5,30 @@ require 'eac_ruby_utils/core_ext'
|
|
5
5
|
module EacCli
|
6
6
|
module Runner
|
7
7
|
class Context
|
8
|
-
attr_reader :argv, :parent, :program_name
|
8
|
+
attr_reader :argv, :parent, :program_name, :runner
|
9
9
|
|
10
|
-
def initialize(*context_args)
|
10
|
+
def initialize(runner, *context_args)
|
11
11
|
options = context_args.extract_options!
|
12
12
|
@argv = (context_args[0] || options.delete(:argv) || ARGV).dup.freeze
|
13
13
|
@parent = context_args[1] || options.delete(:parent)
|
14
14
|
@program_name = options.delete(:program_name)
|
15
|
+
@runner = runner
|
16
|
+
end
|
17
|
+
|
18
|
+
# Call a method in the runner or in one of it ancestors.
|
19
|
+
def call(method_name, *args)
|
20
|
+
return runner.send(method_name, *args) if runner.respond_to?(method_name)
|
21
|
+
return parent_call(method_name, *args) if parent.present?
|
22
|
+
|
23
|
+
raise ::NameError, "No method \"#{method_name}\" found in #{runner} or in its ancestors"
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def parent_call(method_name, *args)
|
29
|
+
return parent.context(method_name, *args) if parent.respond_to?(:context)
|
30
|
+
|
31
|
+
parent.runner_context.call(method_name, *args)
|
15
32
|
end
|
16
33
|
end
|
17
34
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'eac_cli/runner'
|
4
|
+
require 'eac_ruby_utils/core_ext'
|
5
|
+
|
6
|
+
module EacCli
|
7
|
+
module RunnerWith
|
8
|
+
module Subcommands
|
9
|
+
common_concern do
|
10
|
+
include ::EacCli::Runner
|
11
|
+
end
|
12
|
+
|
13
|
+
EXTRA_AVAILABLE_SUBCOMMANDS_METHOD_NAME = :extra_available_subcommands
|
14
|
+
|
15
|
+
def available_subcommands
|
16
|
+
@available_subcommands ||= available_subcommands_auto.merge(available_subcommands_extra)
|
17
|
+
end
|
18
|
+
|
19
|
+
def available_subcommands_auto
|
20
|
+
self.class.constants
|
21
|
+
.map { |name| [name.to_s.underscore.gsub('_', '-'), self.class.const_get(name)] }
|
22
|
+
.select { |c| ::EacCli::Runner.runner?(c[1]) }
|
23
|
+
.to_h.with_indifferent_access
|
24
|
+
end
|
25
|
+
|
26
|
+
def available_subcommands_extra
|
27
|
+
if respond_to?(EXTRA_AVAILABLE_SUBCOMMANDS_METHOD_NAME, true)
|
28
|
+
send(EXTRA_AVAILABLE_SUBCOMMANDS_METHOD_NAME)
|
29
|
+
else
|
30
|
+
{}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing(method_name, *arguments, &block)
|
35
|
+
return run_with_subcommand(*arguments, &block) if
|
36
|
+
run_with_subcommand_alias_run?(method_name)
|
37
|
+
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
def respond_to_missing?(method_name, include_private = false)
|
42
|
+
run_with_subcommand_alias_run?(method_name) || super
|
43
|
+
end
|
44
|
+
|
45
|
+
def run_with_subcommand
|
46
|
+
if subcommand_name
|
47
|
+
subcommand_runner.run
|
48
|
+
else
|
49
|
+
run_without_subcommand
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def run_with_subcommand_alias_run?(method_name)
|
54
|
+
subcommands? && method_name.to_sym == :run
|
55
|
+
end
|
56
|
+
|
57
|
+
def run_without_subcommand
|
58
|
+
"Method #{__method__} should be overrided in #{self.class.name}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def subcommands?
|
62
|
+
self.class.runner_definition.subcommands?
|
63
|
+
end
|
64
|
+
|
65
|
+
def subcommand_args
|
66
|
+
parsed.fetch(::EacCli::Definition::SUBCOMMAND_ARGS_ARG)
|
67
|
+
end
|
68
|
+
|
69
|
+
def subcommand_class
|
70
|
+
available_subcommands[subcommand_name].if_present { |v| return v }
|
71
|
+
|
72
|
+
raise(::EacCli::Parser::Error.new(
|
73
|
+
self.class.runner_definition, runner_context.argv,
|
74
|
+
"Subcommand \"#{subcommand_name}\" not found " \
|
75
|
+
"(Available: #{available_subcommands.keys}"
|
76
|
+
))
|
77
|
+
end
|
78
|
+
|
79
|
+
def subcommand_name
|
80
|
+
parsed.fetch(::EacCli::Definition::SUBCOMMAND_NAME_ARG)
|
81
|
+
end
|
82
|
+
|
83
|
+
def subcommand_program
|
84
|
+
subcommand_name
|
85
|
+
end
|
86
|
+
|
87
|
+
def subcommand_runner
|
88
|
+
@subcommand_runner ||= subcommand_class.create(
|
89
|
+
argv: subcommand_args,
|
90
|
+
program_name: subcommand_program,
|
91
|
+
parent: self
|
92
|
+
)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/eac_cli/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eac_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Esquilo Azul Company
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: eac_ruby_utils
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.50'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
26
|
+
version: '0.50'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: eac_ruby_gem_support
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,11 +52,13 @@ extra_rdoc_files: []
|
|
52
52
|
files:
|
53
53
|
- Gemfile
|
54
54
|
- lib/eac_cli.rb
|
55
|
+
- lib/eac_cli/core_ext.rb
|
55
56
|
- lib/eac_cli/default_runner.rb
|
56
57
|
- lib/eac_cli/definition.rb
|
57
58
|
- lib/eac_cli/definition/argument_option.rb
|
58
59
|
- lib/eac_cli/definition/base_option.rb
|
59
60
|
- lib/eac_cli/definition/boolean_option.rb
|
61
|
+
- lib/eac_cli/definition/help_formatter.rb
|
60
62
|
- lib/eac_cli/definition/positional_argument.rb
|
61
63
|
- lib/eac_cli/docopt/doc_builder.rb
|
62
64
|
- lib/eac_cli/docopt/runner_extension.rb
|
@@ -66,11 +68,15 @@ files:
|
|
66
68
|
- lib/eac_cli/parser/options_collection.rb
|
67
69
|
- lib/eac_cli/parser/parse_result.rb
|
68
70
|
- lib/eac_cli/parser/positional_collection.rb
|
71
|
+
- lib/eac_cli/patches.rb
|
72
|
+
- lib/eac_cli/patches/object.rb
|
73
|
+
- lib/eac_cli/patches/object/runner_with.rb
|
69
74
|
- lib/eac_cli/runner.rb
|
70
75
|
- lib/eac_cli/runner/context.rb
|
71
76
|
- lib/eac_cli/runner_with.rb
|
72
77
|
- lib/eac_cli/runner_with/help.rb
|
73
78
|
- lib/eac_cli/runner_with/output_file.rb
|
79
|
+
- lib/eac_cli/runner_with/subcommands.rb
|
74
80
|
- lib/eac_cli/version.rb
|
75
81
|
homepage:
|
76
82
|
licenses: []
|