eac_cli 0.11.1 → 0.12.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 395665ef14dad48803a7bfcdf622beb5838b10fda82ba2a013e5653956ab73dd
4
- data.tar.gz: 7836b0bb93b6ac2b6881a0e232880d94ed6601f79febc1fe45789106bbb29360
3
+ metadata.gz: 592bc723eabd70046851a48462d929f9ec2eda7ee5988cef0cd1bcc1b2df2483
4
+ data.tar.gz: b3f9102bf3a75c8f6e0bc14fcf3a24edb9d7aedf447799adbe834b5a0904c22f
5
5
  SHA512:
6
- metadata.gz: 40b7358bc737f3a6326801698e27f00bb8947a5e7bcbd18e20d8eaa7c31e2d9bc78f13381b04ccdb63b811692d8738acf0eb2dedb0c140facd58d5cd10e533fe
7
- data.tar.gz: 8dc8fafeca39393c741c8f465202a370185e354ef1669b23761fac7e203ead1076af17b0fd61adcf369baf655a9db472e6e2ca30bd081d1200e4aa3a5fdd7a52
6
+ metadata.gz: 3add0014bf31c9a9e02290f26fb01fffc0bf4f6bea6afddd9c93b71028ec0fdc55d50da8a2a11442659b13d98f9e15423f4210c53991470d88b2ea8bd0987e87
7
+ data.tar.gz: b479edbe9720a9c57a3ec84a8711bc030bb50f1716387e3ce9d976f65117c4c7dbbc0273623f0b97001ec1d7841a13dc5eae62a2ab66d32e2e375c71b2de4247
@@ -9,36 +9,30 @@ module EacCli
9
9
  class Definition
10
10
  require_sub __FILE__
11
11
 
12
+ MAIN_ALTERNATIVE_KEY = :main
12
13
  SUBCOMMAND_NAME_ARG = 'subcommand'
13
14
  SUBCOMMAND_ARGS_ARG = 'subcommand_args'
14
15
 
15
16
  attr_accessor :description
16
- attr_accessor :options_argument
17
17
 
18
18
  def initialize
19
19
  self.description = '-- NO DESCRIPTION SET --'
20
- self.options_argument = true
20
+ alternatives_set[MAIN_ALTERNATIVE_KEY] = main_alternative
21
21
  end
22
22
 
23
23
  def alt(&block)
24
- r = ::EacCli::Definition.new
24
+ r = ::EacCli::Definition::Alternative.new
25
25
  r.instance_eval(&block)
26
- alternatives << r
26
+ alternatives_set[new_alternative_key] = r
27
27
  r
28
28
  end
29
29
 
30
30
  def alternatives
31
- @alternatives ||= []
31
+ alternatives_set.values
32
32
  end
33
33
 
34
- def arg_opt(short, long, description, option_options = {})
35
- options << ::EacCli::Definition::ArgumentOption.new(
36
- short, long, description, option_options
37
- )
38
- end
39
-
40
- def bool_opt(short, long, description, option_options = {})
41
- options << ::EacCli::Definition::BooleanOption.new(short, long, description, option_options)
34
+ def alternative(key)
35
+ alternatives_set.fetch(key)
42
36
  end
43
37
 
44
38
  def desc(description)
@@ -49,42 +43,31 @@ module EacCli
49
43
  @help_formatter ||= ::EacCli::Definition::HelpFormatter.new(self)
50
44
  end
51
45
 
52
- def options_arg(options_argument)
53
- self.options_argument = options_argument
54
- end
55
-
56
- def options
57
- @options ||= []
46
+ def main_alternative
47
+ @main_alternative ||= begin
48
+ r = ::EacCli::Definition::Alternative.new
49
+ r.options_argument(true)
50
+ r
51
+ end
58
52
  end
59
53
 
60
- def pos_arg(name, arg_options = {})
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
+ def options_arg(options_argument)
55
+ self.options_argument = options_argument
65
56
  end
66
57
 
67
- def positional
68
- pos_set.to_a
58
+ def options_argument
59
+ main_alternative.options_argument?
69
60
  end
70
61
 
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
62
+ def options_argument=(enable)
63
+ main_alternative.options_argument(enable)
78
64
  end
79
65
 
80
- def subcommands
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
66
+ delegate :arg_opt, :bool_opt, :options, :pos_arg,
67
+ :positional, :subcommands, to: :main_alternative
85
68
 
86
69
  def subcommands?
87
- pos_set.any?(&:subcommand?)
70
+ alternatives.any?(&:subcommands?)
88
71
  end
89
72
 
90
73
  def options_first(enable = true)
@@ -97,6 +80,18 @@ module EacCli
97
80
 
98
81
  private
99
82
 
83
+ def alternatives_set
84
+ @alternatives_set ||= ::ActiveSupport::HashWithIndifferentAccess.new
85
+ end
86
+
87
+ def new_alternative_key
88
+ @last_key ||= 0
89
+ loop do
90
+ @last_key += 1
91
+ break @last_key unless alternatives_set.key?(@last_key)
92
+ end
93
+ end
94
+
100
95
  def pos_set
101
96
  @pos_set ||= []
102
97
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_cli/definition/argument_option'
4
+ require 'eac_cli/definition/boolean_option'
5
+ require 'eac_cli/definition/positional_argument'
6
+
7
+ module EacCli
8
+ class Definition
9
+ class Alternative
10
+ SUBCOMMAND_NAME_ARG = :subcommand
11
+ SUBCOMMAND_ARGS_ARG = :subcommand_args
12
+
13
+ def arg_opt(short, long, description, option_options = {})
14
+ options_set << ::EacCli::Definition::ArgumentOption.new(
15
+ short, long, description, option_options
16
+ )
17
+ end
18
+
19
+ def bool_opt(short, long, description, option_options = {})
20
+ options_set << ::EacCli::Definition::BooleanOption.new(short, long, description,
21
+ option_options)
22
+ end
23
+
24
+ def options
25
+ options_set.to_a
26
+ end
27
+
28
+ def options_argument?
29
+ @options_argument ? true : false
30
+ end
31
+
32
+ def options_argument(enable)
33
+ @options_argument = enable
34
+
35
+ self
36
+ end
37
+
38
+ def pos_arg(name, arg_options = {})
39
+ new_pos_arg = ::EacCli::Definition::PositionalArgument.new(name, arg_options)
40
+ check_positional_blocked(new_pos_arg)
41
+ pos_set << new_pos_arg
42
+ end
43
+
44
+ def positional
45
+ pos_set.to_a
46
+ end
47
+
48
+ def positional_arguments_blocked?(new_pos_arg)
49
+ last = pos_set.last
50
+ return false unless last
51
+ return true if subcommands?
52
+ return true if last.repeat?
53
+ return true if last.optional? && new_pos_arg.if_present(&:required?)
54
+
55
+ false
56
+ end
57
+
58
+ def subcommands
59
+ pos_arg(SUBCOMMAND_NAME_ARG, subcommand: true)
60
+ pos_set << ::EacCli::Definition::PositionalArgument.new(SUBCOMMAND_ARGS_ARG,
61
+ optional: true, repeat: true)
62
+ end
63
+
64
+ def subcommands?
65
+ pos_set.any?(&:subcommand?)
66
+ end
67
+
68
+ private
69
+
70
+ def check_positional_blocked(new_pos_arg)
71
+ raise 'Positional arguments are blocked' if positional_arguments_blocked?(new_pos_arg)
72
+ end
73
+
74
+ def pos_set
75
+ @pos_set ||= []
76
+ end
77
+
78
+ def options_set
79
+ @options_set ||= []
80
+ end
81
+ end
82
+ end
83
+ end
@@ -6,71 +6,49 @@ require 'eac_ruby_utils/console/docopt_runner'
6
6
  module EacCli
7
7
  module Docopt
8
8
  class DocBuilder
9
+ require_sub __FILE__
9
10
  common_constructor :definition
10
11
 
11
12
  SEP = ' '
12
13
  IDENT = SEP * 2
13
14
  OPTION_DESC_SEP = IDENT * 2
14
15
 
15
- def positional_argument(positional)
16
- if positional.subcommand?
17
- ::EacRubyUtils::Console::DocoptRunner::SUBCOMMANDS_MACRO
18
- else
19
- r = "<#{positional.name}>"
20
- r += '...' if positional.repeat?
21
- r = "[#{r}]" if positional.optional?
22
- r
16
+ class << self
17
+ def option_long(option)
18
+ b = option.long
19
+ b += '=<value>' if option.argument?
20
+ b
23
21
  end
24
22
  end
25
23
 
26
- def option_argument(option)
27
- option_long(option)
28
- end
29
-
30
24
  def option_definition(option)
31
- option.short + SEP + option_long(option) + OPTION_DESC_SEP + option.description
32
- end
33
-
34
- def option_long(option)
35
- b = option.long
36
- b += '=<value>' if option.argument?
37
- b
25
+ option.short + SEP + self.class.option_long(option) + OPTION_DESC_SEP + option.description
38
26
  end
39
27
 
40
28
  def section(header, include_header = true)
41
29
  b = include_header ? "#{header.humanize}:\n" : ''
42
30
  b += send("self_#{header}") + "\n"
43
31
  definition.alternatives.each do |alternative|
44
- b += self.class.new(alternative).section(header, false)
32
+ b += IDENT + ::EacCli::Docopt::DocBuilder::Alternative.new(alternative).to_s + "\n"
45
33
  end
46
34
  b
47
35
  end
48
36
 
49
- def self_options
50
- definition.options.map { |option| IDENT + option_definition(option) }.join("\n")
51
- end
52
-
53
- def self_usage
54
- IDENT + self_usage_arguments.join(SEP)
55
- end
56
-
57
- def self_usage_arguments
58
- [::EacRubyUtils::Console::DocoptRunner::PROGRAM_MACRO] +
59
- definition.options_argument.if_present([]) { |_v| ['[options]'] } +
60
- self_usage_arguments_options +
61
- self_usage_arguments_positional
62
- end
63
-
64
- def self_usage_arguments_options
65
- definition.options.select(&:show_on_usage?).map { |option| option_argument(option) }
37
+ def options_section
38
+ "Options:\n" +
39
+ definition.alternatives.flat_map(&:options)
40
+ .map { |option| IDENT + option_definition(option) + "\n" }.join
66
41
  end
67
42
 
68
- def self_usage_arguments_positional
69
- definition.positional.map { |p| positional_argument(p) }
43
+ def usage_section
44
+ "Usage:\n" +
45
+ definition.alternatives.map do |alternative|
46
+ IDENT + ::EacCli::Docopt::DocBuilder::Alternative.new(alternative).to_s + "\n"
47
+ end.join
70
48
  end
71
49
 
72
50
  def to_s
73
- "#{definition.description}\n\n#{section('usage')}\n#{section('options')}\n"
51
+ "#{definition.description}\n\n#{usage_section}\n#{options_section}\n"
74
52
  end
75
53
  end
76
54
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+ require 'eac_ruby_utils/console/docopt_runner'
5
+
6
+ module EacCli
7
+ module Docopt
8
+ class DocBuilder
9
+ class Alternative
10
+ common_constructor :alternative
11
+
12
+ def to_s
13
+ (
14
+ [::EacRubyUtils::Console::DocoptRunner::PROGRAM_MACRO] +
15
+ alternative.options_argument?.if_present([]) { |_v| ['[options]'] } +
16
+ options +
17
+ positionals
18
+ ).join(::EacCli::Docopt::DocBuilder::SEP)
19
+ end
20
+
21
+ def options
22
+ alternative.options.select(&:show_on_usage?).map do |option|
23
+ ::EacCli::Docopt::DocBuilder.option_long(option)
24
+ end
25
+ end
26
+
27
+ def option_argument(option)
28
+ b = option.long
29
+ b += '=<value>' if option.argument?
30
+ b
31
+ end
32
+
33
+ def positionals
34
+ alternative.positional.map { |p| positional(p) }
35
+ end
36
+
37
+ def positional(positional)
38
+ if positional.subcommand?
39
+ ::EacRubyUtils::Console::DocoptRunner::SUBCOMMANDS_MACRO
40
+ else
41
+ r = "<#{positional.name}>"
42
+ r += '...' if positional.repeat?
43
+ r = "[#{r}]" if positional.optional?
44
+ r
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -5,10 +5,30 @@ require 'eac_ruby_utils/core_ext'
5
5
  module EacCli
6
6
  class Parser
7
7
  require_sub __FILE__
8
- common_constructor :definition
8
+ enable_simple_cache
9
+ common_constructor :definition, :argv
9
10
 
10
- def parse(argv)
11
- ::EacCli::Parser::ParseResult.new(definition, argv).result
11
+ private
12
+
13
+ def parsed_uncached
14
+ raise 'Definition has no alternatives' if alternatives.empty?
15
+ raise first_error unless alternatives.select(&:success?).any?
16
+
17
+ alternatives_parsed(true).merge(alternatives_parsed(false))
18
+ end
19
+
20
+ def alternatives_parsed(error)
21
+ alternatives.select { |a| error == a.error? }.map(&:parsed).reverse
22
+ .inject(::EacRubyUtils::Struct.new) { |a, e| a.merge(e) }
23
+ end
24
+
25
+ def alternatives_uncached
26
+ definition.alternatives
27
+ .map { |alternative| ::EacCli::Parser::Alternative.new(alternative, argv) }
28
+ end
29
+
30
+ def first_error_uncached
31
+ alternatives.lazy.select(&:error?).map(&:error).first
12
32
  end
13
33
  end
14
34
  end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_cli/parser/collector'
4
+ require 'eac_cli/parser/error'
5
+ require 'eac_ruby_utils/core_ext'
6
+
7
+ module EacCli
8
+ class Parser
9
+ class Alternative
10
+ require_sub __FILE__, include_modules: true
11
+ enable_listable
12
+ lists.add_symbol :phase, :any, :option_argument, :positional
13
+ attr_reader :error
14
+
15
+ common_constructor :alternative, :argv do
16
+ alternative.assert_argument(::EacCli::Definition::Alternative, :alternative)
17
+ self.phase = PHASE_ANY
18
+ collect
19
+ end
20
+
21
+ def error?
22
+ error.present?
23
+ end
24
+
25
+ def success?
26
+ !error?
27
+ end
28
+
29
+ def parsed
30
+ @parsed ||= collector.to_data.freeze
31
+ end
32
+
33
+ private
34
+
35
+ attr_accessor :phase
36
+
37
+ def any_collect_argv_value
38
+ if argv_current_option?
39
+ option_collect_argv_value
40
+ else
41
+ positional_collect_argv_value
42
+ end
43
+ end
44
+
45
+ def collector
46
+ @collector ||= ::EacCli::Parser::Collector.new(alternative)
47
+ end
48
+
49
+ def collect
50
+ loop do
51
+ break unless argv_pending?
52
+
53
+ collect_argv_value
54
+ end
55
+ validate
56
+ rescue ::EacCli::Parser::Error => e
57
+ @error = e
58
+ end
59
+
60
+ def collect_argv_value
61
+ send("#{phase}_collect_argv_value")
62
+ argv_enum.next
63
+ end
64
+
65
+ def collect_option_argv_value
66
+ alternative.options.each do |option|
67
+ end
68
+
69
+ raise ::EacCli::Parser::Error.new(
70
+ alternative, argv, "Invalid option: #{argv_enum.current}"
71
+ )
72
+ end
73
+
74
+ def raise_error(message)
75
+ raise ::EacCli::Parser::Error.new(alternative, argv, message)
76
+ end
77
+
78
+ def validate
79
+ (alternative.options + alternative.positional).each do |option|
80
+ validate_option(option)
81
+ end
82
+ end
83
+
84
+ def validate_option(option)
85
+ return unless option.required?
86
+ return if collector.supplied?(option)
87
+
88
+ raise_error("Required option/positional #{option} not supplied")
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EacCli
4
+ class Parser
5
+ class Alternative
6
+ module Argv
7
+ def argv_enum
8
+ @argv_enum ||= argv.each
9
+ end
10
+
11
+ def argv_pending?
12
+ argv_enum.ongoing?
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EacCli
4
+ class Parser
5
+ class Alternative
6
+ module DoubleDash
7
+ DOUBLE_DASH = '--'
8
+
9
+ private
10
+
11
+ attr_accessor :double_dash
12
+
13
+ def argv_current_double_dash?
14
+ argv_enum.peek == DOUBLE_DASH && !double_dash
15
+ end
16
+
17
+ def double_dash_collect_argv_value
18
+ self.phase = PHASE_POSITIONAL
19
+ self.double_dash = true
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EacCli
4
+ class Parser
5
+ class Alternative
6
+ module Options
7
+ DOUBLE_DASH = '--'
8
+
9
+ private
10
+
11
+ attr_accessor :argument_option, :double_dash
12
+
13
+ def argument_option_collect_argv(option)
14
+ self.argument_option = option
15
+ self.phase = PHASE_OPTION_ARGUMENT
16
+ end
17
+
18
+ def argv_current_option?
19
+ phase == PHASE_ANY && argv_enum.peek.start_with?('-')
20
+ end
21
+
22
+ def argv_current_double_dash?
23
+ argv_enum.peek == DOUBLE_DASH && !double_dash
24
+ end
25
+
26
+ def boolean_option_collect_argv(option)
27
+ collector.collect(option, true)
28
+ end
29
+
30
+ def option_argument_collect_argv_value
31
+ collector.collect(argument_option, argv_enum.peek)
32
+ self.argument_option = nil
33
+ self.phase = PHASE_ANY
34
+ end
35
+
36
+ def option_collect_argv_value
37
+ return double_dash_collect_argv_value if argv_current_double_dash?
38
+
39
+ alternative.options.any? do |option|
40
+ next false unless [option.short, option.long].include?(argv_enum.peek)
41
+
42
+ if option.argument?
43
+ argument_option_collect_argv(option)
44
+ else
45
+ boolean_option_collect_argv(option)
46
+ end
47
+
48
+ true
49
+ end || raise_argv_current_invalid_option
50
+ end
51
+
52
+ def raise_argv_current_invalid_option
53
+ raise_error "Invalid option: \"#{argv_enum.peek}\""
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EacCli
4
+ class Parser
5
+ class Alternative
6
+ module Positionals
7
+ private
8
+
9
+ def positional_collect_argv_value
10
+ positional_check
11
+ collector.collect(positional_enum.peek, argv_enum.peek)
12
+ positional_next
13
+ end
14
+
15
+ def positional_enum
16
+ @positional_enum ||= alternative.positional.each
17
+ end
18
+
19
+ def positional_check
20
+ raise_error("Invalid positional: #{argv_enum.peek}") if positional_enum.stopped?
21
+ end
22
+
23
+ def positional_next
24
+ self.phase = PHASE_POSITIONAL if positional_enum.peek.subcommand?
25
+ positional_enum.next unless positional_enum.peek.repeat?
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -28,7 +28,7 @@ module EacCli
28
28
  sklass = klass.singleton_class
29
29
  return unless sklass.method_defined?(from)
30
30
 
31
- sklass.alias_method to, from
31
+ sklass.send(:alias_method, to, from)
32
32
  end
33
33
 
34
34
  def build_method_name(name, suffix)
@@ -44,6 +44,8 @@ module EacCli
44
44
  extend AfterClassMethods
45
45
  include InstanceMethods
46
46
  ::EacCli::Docopt::RunnerExtension.check(self)
47
+ include ActiveSupport::Callbacks
48
+ define_callbacks :run
47
49
  end
48
50
 
49
51
  module AfterClassMethods
@@ -55,8 +57,7 @@ module EacCli
55
57
 
56
58
  def run(*runner_context_args)
57
59
  r = create(*runner_context_args)
58
- r.parsed
59
- r.run
60
+ r.run_run
60
61
  r
61
62
  end
62
63
 
@@ -72,6 +73,13 @@ module EacCli
72
73
  end
73
74
 
74
75
  module InstanceMethods
76
+ def run_run
77
+ parsed
78
+ run_callbacks(:run) { run }
79
+ rescue ::EacCli::Runner::Exit # rubocop:disable Lint/SuppressedException
80
+ # Do nothing
81
+ end
82
+
75
83
  def runner_context
76
84
  return @runner_context if @runner_context
77
85
 
@@ -84,7 +92,7 @@ module EacCli
84
92
  end
85
93
 
86
94
  def parsed
87
- @parsed ||= ::EacCli::Parser.new(self.class.runner_definition).parse(runner_context.argv)
95
+ @parsed ||= ::EacCli::Parser.new(self.class.runner_definition, runner_context.argv).parsed
88
96
  end
89
97
  end
90
98
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EacCli
4
+ module Runner
5
+ class Exit < ::StandardError
6
+ attr_reader :status
7
+
8
+ def initialize(status = true)
9
+ @status = status
10
+ end
11
+ end
12
+ end
13
+ end
@@ -10,9 +10,26 @@ module EacCli
10
10
  include ::EacCli::Runner
11
11
 
12
12
  runner_definition.alt do
13
- options_arg false
13
+ options_argument false
14
14
  bool_opt '-h', '--help', 'Show help.', usage: true
15
15
  end
16
+
17
+ set_callback :run, :before do
18
+ help_run
19
+ end
20
+ end
21
+
22
+ def help_run
23
+ return unless parsed.help?
24
+
25
+ puts help_text
26
+ raise ::EacCli::Runner::Exit
27
+ end
28
+
29
+ def help_text
30
+ r = ::EacCli::Docopt::DocBuilder.new(self.class.runner_definition).to_s
31
+ r += help_extra_text if respond_to?(:help_extra_text)
32
+ r
16
33
  end
17
34
  end
18
35
  end
@@ -2,12 +2,16 @@
2
2
 
3
3
  require 'eac_cli/runner'
4
4
  require 'eac_ruby_utils/core_ext'
5
+ require 'eac_ruby_utils/abstract_methods'
5
6
 
6
7
  module EacCli
7
8
  module RunnerWith
8
9
  module OutputFile
9
10
  common_concern do
10
11
  include ::EacCli::Runner
12
+ include ::EacRubyUtils::AbstractMethods
13
+
14
+ abstract_methods :output_content
11
15
 
12
16
  runner_definition do
13
17
  arg_opt '-o', '--output-file', 'Output to file.'
@@ -18,7 +22,7 @@ module EacCli
18
22
  if parsed.output_file.present?
19
23
  ::File.write(parsed.output_file, output_content)
20
24
  else
21
- out output_content
25
+ $stdout.write(output_content)
22
26
  end
23
27
  end
24
28
  end
@@ -31,6 +31,11 @@ module EacCli
31
31
  end
32
32
  end
33
33
 
34
+ def help_extra_text
35
+ (['Subcommands:'] + available_subcommands.keys.map { |s| " #{s}" })
36
+ .map { |v| "#{v}\n" }.join
37
+ end
38
+
34
39
  def method_missing(method_name, *arguments, &block)
35
40
  return run_with_subcommand(*arguments, &block) if
36
41
  run_with_subcommand_alias_run?(method_name)
@@ -44,7 +49,7 @@ module EacCli
44
49
 
45
50
  def run_with_subcommand
46
51
  if subcommand_name
47
- subcommand_runner.run
52
+ subcommand_runner.run_run
48
53
  else
49
54
  run_without_subcommand
50
55
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EacCli
4
- VERSION = '0.11.1'
4
+ VERSION = '0.12.4'
5
5
  end
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.11.1
4
+ version: 0.12.4
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-19 00:00:00.000000000 Z
11
+ date: 2020-12-22 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.50'
19
+ version: '0.55'
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.50'
26
+ version: '0.55'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: eac_ruby_gem_support
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -55,24 +55,29 @@ files:
55
55
  - lib/eac_cli/core_ext.rb
56
56
  - lib/eac_cli/default_runner.rb
57
57
  - lib/eac_cli/definition.rb
58
+ - lib/eac_cli/definition/alternative.rb
58
59
  - lib/eac_cli/definition/argument_option.rb
59
60
  - lib/eac_cli/definition/base_option.rb
60
61
  - lib/eac_cli/definition/boolean_option.rb
61
62
  - lib/eac_cli/definition/help_formatter.rb
62
63
  - lib/eac_cli/definition/positional_argument.rb
63
64
  - lib/eac_cli/docopt/doc_builder.rb
65
+ - lib/eac_cli/docopt/doc_builder/alternative.rb
64
66
  - lib/eac_cli/docopt/runner_extension.rb
65
67
  - lib/eac_cli/parser.rb
68
+ - lib/eac_cli/parser/alternative.rb
69
+ - lib/eac_cli/parser/alternative/argv.rb
70
+ - lib/eac_cli/parser/alternative/double_dash.rb
71
+ - lib/eac_cli/parser/alternative/options.rb
72
+ - lib/eac_cli/parser/alternative/positionals.rb
66
73
  - lib/eac_cli/parser/collector.rb
67
74
  - lib/eac_cli/parser/error.rb
68
- - lib/eac_cli/parser/options_collection.rb
69
- - lib/eac_cli/parser/parse_result.rb
70
- - lib/eac_cli/parser/positional_collection.rb
71
75
  - lib/eac_cli/patches.rb
72
76
  - lib/eac_cli/patches/object.rb
73
77
  - lib/eac_cli/patches/object/runner_with.rb
74
78
  - lib/eac_cli/runner.rb
75
79
  - lib/eac_cli/runner/context.rb
80
+ - lib/eac_cli/runner/exit.rb
76
81
  - lib/eac_cli/runner_with.rb
77
82
  - lib/eac_cli/runner_with/help.rb
78
83
  - lib/eac_cli/runner_with/output_file.rb
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'eac_ruby_utils/core_ext'
4
- require 'optparse'
5
-
6
- module EacCli
7
- class Parser
8
- class OptionsCollection
9
- enable_simple_cache
10
- common_constructor(:definition, :argv, :collector) { collect }
11
- attr_reader :arguments
12
-
13
- def options_first?
14
- definition.options_first? || definition.subcommands?
15
- end
16
-
17
- private
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
-
30
- def collect
31
- build_banner
32
- build_options
33
- parse_argv
34
- check_required_options
35
- end
36
-
37
- def option_parser_uncached
38
- ::OptionParser.new
39
- end
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
-
47
- def build_banner
48
- option_parser.banner = definition.help_formatter.to_banner
49
- end
50
-
51
- def build_options
52
- definition.options.each do |option|
53
- build_option(option)
54
- end
55
- end
56
-
57
- def build_option(option)
58
- option_parser.on(
59
- *[::EacCli::Definition::HelpFormatter.option_short(option),
60
- ::EacCli::Definition::HelpFormatter.option_long(option),
61
- option.description].reject(&:blank?)
62
- ) do |value|
63
- collector.collect(option, value)
64
- end
65
- end
66
- end
67
- end
68
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'eac_cli/parser/error'
4
- require 'eac_ruby_utils/core_ext'
5
-
6
- module EacCli
7
- class Parser
8
- class ParseResult
9
- common_constructor :definition, :argv
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)
26
- ::EacCli::Parser::Collector.to_data(definition) do |collector|
27
- ::EacCli::Parser::PositionalCollection.new(
28
- definition,
29
- ::EacCli::Parser::OptionsCollection.new(definition, argv, collector).arguments,
30
- collector
31
- )
32
- end
33
- rescue ::EacCli::Parser::Error => e
34
- e
35
- end
36
- end
37
- end
38
- end
@@ -1,77 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'eac_ruby_utils/core_ext'
4
- require 'eac_cli/parser/error'
5
-
6
- module EacCli
7
- class Parser
8
- class PositionalCollection
9
- common_constructor(:definition, :argv, :collector) { collect }
10
-
11
- private
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
-
30
- def collected
31
- @collected ||= ::Set.new
32
- end
33
-
34
- def collect
35
- loop do
36
- break unless enums('pending?').any?
37
-
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
48
- end
49
-
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
55
- end
56
-
57
- def positional_enum
58
- @positional_enum ||= definition.positional.each
59
- end
60
-
61
- def positional_pending?
62
- !(positional_enum.stopped? || positional_enum.current.optional? ||
63
- collected.include?(positional_enum.current))
64
- end
65
-
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
- )
74
- end
75
- end
76
- end
77
- end