eac_cli 0.10.0 → 0.12.2

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: 607e900ac4f5c7b6e7531c4ce9f9f6cfc8fd8ffce30f7b4d2a22ff91cdfd99bb
4
- data.tar.gz: 0f49af96a89a527390ba34d2e6ecc17d2942aea7200bebbab1ba1f540398f413
3
+ metadata.gz: 90408a7ab93c85784cf6f5946a034567a222ecbfb66922903302ab50a5a38d94
4
+ data.tar.gz: 87f6ab796eb1c6734fa615500cb5d7721d3fb1477ce7059bf7da434f60543b01
5
5
  SHA512:
6
- metadata.gz: 1e2a23e42fb5df31819b499f8dc1448f6ecc9d095fc22c9a0ceabe1acadf9ba6398a62d04a09943b233feeeb5e07325f62069ee883fb388b29424e7656ebe500
7
- data.tar.gz: 1fefcf204e43d2ccfcf45c8507be13be885d46da72f0d6eda2ea3c3e6b7b845592e3ed989fc29b8c687a3727b795f8ea3a2d2a6526c3cda61bbf6b6f7da8df86
6
+ metadata.gz: a9a941471808a3827ff4edc8bf6f3e3533e174a62078895514e90fbb32ec53db5f4284da379357fc2bfe64edae36646400688df4386d7344c0cfb087a7887544
7
+ data.tar.gz: d9e54da85eb7487bba85a30550ffd32065d7b419c5384c81d9724b6d33d0385791b9d0bd07b5733e391fd0030faf59d9b21a9a7ff2c91a384bcfcc4b0d1cff94
@@ -9,72 +9,65 @@ 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)
45
39
  self.description = description
46
40
  end
47
41
 
48
- def options_arg(options_argument)
49
- self.options_argument = options_argument
42
+ def help_formatter
43
+ @help_formatter ||= ::EacCli::Definition::HelpFormatter.new(self)
50
44
  end
51
45
 
52
- def options
53
- @options ||= []
46
+ def main_alternative
47
+ @main_alternative ||= begin
48
+ r = ::EacCli::Definition::Alternative.new
49
+ r.options_argument(true)
50
+ r
51
+ end
54
52
  end
55
53
 
56
- def pos_arg(name, arg_options = {})
57
- raise 'Positional arguments are blocked' if positional_arguments_blocked?
58
-
59
- pos_set << ::EacCli::Definition::PositionalArgument.new(name, arg_options)
54
+ def options_arg(options_argument)
55
+ self.options_argument = options_argument
60
56
  end
61
57
 
62
- def positional
63
- pos_set.to_a
58
+ def options_argument
59
+ main_alternative.options_argument?
64
60
  end
65
61
 
66
- def positional_arguments_blocked?
67
- pos_set.any? { |e| e.optional? || e.repeat? }
62
+ def options_argument=(enable)
63
+ main_alternative.options_argument(enable)
68
64
  end
69
65
 
70
- def subcommands
71
- pos_arg(SUBCOMMAND_NAME_ARG, subcommand: true)
72
- pos_set << ::EacCli::Definition::PositionalArgument.new(SUBCOMMAND_ARGS_ARG,
73
- optional: true, repeat: true)
74
- end
66
+ delegate :arg_opt, :bool_opt, :options, :pos_arg,
67
+ :positional, :subcommands, to: :main_alternative
75
68
 
76
69
  def subcommands?
77
- pos_set.any?(&:subcommand?)
70
+ alternatives.any?(&:subcommands?)
78
71
  end
79
72
 
80
73
  def options_first(enable = true)
@@ -87,6 +80,18 @@ module EacCli
87
80
 
88
81
  private
89
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
+
90
95
  def pos_set
91
96
  @pos_set ||= []
92
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
@@ -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,8 +5,10 @@ require 'eac_ruby_utils/core_ext'
5
5
  module EacCli
6
6
  class Definition
7
7
  class PositionalArgument
8
+ DEFAULT_REQUIRED = true
9
+
8
10
  enable_listable
9
- lists.add_symbol :option, :optional, :repeat, :subcommand
11
+ lists.add_symbol :option, :optional, :repeat, :required, :subcommand
10
12
  common_constructor :name, :options, default: [{}] do
11
13
  options.assert_valid_keys(self.class.lists.option.values)
12
14
  end
@@ -16,16 +18,27 @@ module EacCli
16
18
  end
17
19
 
18
20
  def optional?
19
- options[OPTION_OPTIONAL]
21
+ !required?
20
22
  end
21
23
 
22
24
  def repeat?
23
25
  options[OPTION_REPEAT]
24
26
  end
25
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
33
+ end
34
+
26
35
  def subcommand?
27
36
  options[OPTION_SUBCOMMAND]
28
37
  end
38
+
39
+ def to_s
40
+ "#{self.class.name.demodulize}[#{identifier}]"
41
+ end
29
42
  end
30
43
  end
31
44
  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,28 @@ 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
+
16
+ alternatives.each do |alt_parser|
17
+ return alt_parser.parsed unless alt_parser.error?
18
+ end
19
+
20
+ raise first_error
21
+ end
22
+
23
+ def alternatives_uncached
24
+ definition.alternatives
25
+ .map { |alternative| ::EacCli::Parser::Alternative.new(alternative, argv) }
26
+ end
27
+
28
+ def first_error_uncached
29
+ alternatives.lazy.select(&:error?).map(&:error).first
12
30
  end
13
31
  end
14
32
  end
@@ -0,0 +1,88 @@
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 parsed
26
+ @parsed ||= collector.to_data.freeze
27
+ end
28
+
29
+ private
30
+
31
+ attr_accessor :phase
32
+
33
+ def any_collect_argv_value
34
+ if argv_current_option?
35
+ option_collect_argv_value
36
+ else
37
+ positional_collect_argv_value
38
+ end
39
+ end
40
+
41
+ def collector
42
+ @collector ||= ::EacCli::Parser::Collector.new(alternative)
43
+ end
44
+
45
+ def collect
46
+ loop do
47
+ break unless argv_pending?
48
+
49
+ collect_argv_value
50
+ end
51
+ validate
52
+ rescue ::EacCli::Parser::Error => e
53
+ @error = e
54
+ end
55
+
56
+ def collect_argv_value
57
+ send("#{phase}_collect_argv_value")
58
+ argv_enum.next
59
+ end
60
+
61
+ def collect_option_argv_value
62
+ alternative.options.each do |option|
63
+ end
64
+
65
+ raise ::EacCli::Parser::Error.new(
66
+ alternative, argv, "Invalid option: #{argv_enum.current}"
67
+ )
68
+ end
69
+
70
+ def raise_error(message)
71
+ raise ::EacCli::Parser::Error.new(alternative, argv, message)
72
+ end
73
+
74
+ def validate
75
+ (alternative.options + alternative.positional).each do |option|
76
+ validate_option(option)
77
+ end
78
+ end
79
+
80
+ def validate_option(option)
81
+ return unless option.required?
82
+ return if collector.supplied?(option)
83
+
84
+ raise_error("Required option/positional #{option} not supplied")
85
+ end
86
+ end
87
+ end
88
+ 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)
@@ -84,7 +84,7 @@ module EacCli
84
84
  end
85
85
 
86
86
  def parsed
87
- @parsed ||= ::EacCli::Parser.new(self.class.runner_definition).parse(runner_context.argv)
87
+ @parsed ||= ::EacCli::Parser.new(self.class.runner_definition, runner_context.argv).parsed
88
88
  end
89
89
  end
90
90
  end
@@ -10,7 +10,7 @@ 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
16
  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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EacCli
4
- VERSION = '0.10.0'
4
+ VERSION = '0.12.2'
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.10.0
4
+ version: 0.12.2
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-15 00:00:00.000000000 Z
11
+ date: 2020-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: eac_ruby_utils
@@ -55,18 +55,23 @@ 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
62
+ - lib/eac_cli/definition/help_formatter.rb
61
63
  - lib/eac_cli/definition/positional_argument.rb
62
64
  - lib/eac_cli/docopt/doc_builder.rb
65
+ - lib/eac_cli/docopt/doc_builder/alternative.rb
63
66
  - lib/eac_cli/docopt/runner_extension.rb
64
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
65
73
  - lib/eac_cli/parser/collector.rb
66
74
  - lib/eac_cli/parser/error.rb
67
- - lib/eac_cli/parser/options_collection.rb
68
- - lib/eac_cli/parser/parse_result.rb
69
- - lib/eac_cli/parser/positional_collection.rb
70
75
  - lib/eac_cli/patches.rb
71
76
  - lib/eac_cli/patches/object.rb
72
77
  - lib/eac_cli/patches/object/runner_with.rb
@@ -1,127 +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
- SEP = ' '
10
- IDENT = SEP * 2
11
- OPTION_DESC_SEP = IDENT * 2
12
-
13
- enable_simple_cache
14
- common_constructor(:definition, :argv, :collector) { collect }
15
- attr_reader :arguments
16
-
17
- def options_first?
18
- definition.options_first? || definition.subcommands?
19
- end
20
-
21
- private
22
-
23
- def check_required_options
24
- definition.options.each do |option|
25
- next unless option.required?
26
- next if collector.supplied?(option)
27
-
28
- raise ::EacCli::Parser::Error.new(
29
- definition, argv, "Option \"#{option}\" is required and a value was not supplied"
30
- )
31
- end
32
- end
33
-
34
- def collect
35
- build_banner
36
- build_options
37
- parse_argv
38
- check_required_options
39
- end
40
-
41
- def option_parser_uncached
42
- ::OptionParser.new
43
- end
44
-
45
- def parse_argv
46
- @arguments = options_first? ? option_parser.order(argv) : option_parser.parse(argv)
47
- rescue ::OptionParser::InvalidOption => e
48
- raise ::EacCli::Parser::Error.new(definition, argv, e.message)
49
- end
50
-
51
- def build_banner
52
- option_parser.banner = "#{definition.description}\n\n#{section('usage')}"
53
- end
54
-
55
- def build_options
56
- definition.options.each do |option|
57
- build_option(option)
58
- end
59
- end
60
-
61
- def build_option(option)
62
- option_parser.on(
63
- *[option_short(option), option_long(option), option.description].reject(&:blank?)
64
- ) do |value|
65
- collector.collect(option, value)
66
- end
67
- end
68
-
69
- def positional_argument(positional)
70
- if positional.subcommand?
71
- ::EacRubyUtils::Console::DocoptRunner::SUBCOMMANDS_MACRO
72
- else
73
- r = "<#{positional.name}>"
74
- r += '...' if positional.repeat?
75
- r = "[#{r}]" if positional.optional?
76
- r
77
- end
78
- end
79
-
80
- def option_argument(option)
81
- option_long(option)
82
- end
83
-
84
- def option_long(option)
85
- b = option.long
86
- b += '=VALUE' if option.argument?
87
- b
88
- end
89
-
90
- def option_short(option)
91
- b = option.short
92
- b += 'VALUE' if option.argument?
93
- b
94
- end
95
-
96
- def section(header, include_header = true)
97
- b = include_header ? "#{header.humanize}:\n" : ''
98
- b += send("self_#{header}") + "\n"
99
- # TO-DO: implement alternatives
100
- b
101
- end
102
-
103
- def self_options
104
- definition.options.map { |option| IDENT + option_definition(option) }.join("\n")
105
- end
106
-
107
- def self_usage
108
- IDENT + self_usage_arguments.join(SEP)
109
- end
110
-
111
- def self_usage_arguments
112
- [::EacRubyUtils::Console::DocoptRunner::PROGRAM_MACRO] +
113
- definition.options_argument.if_present([]) { |_v| ['[options]'] } +
114
- self_usage_arguments_options +
115
- self_usage_arguments_positional
116
- end
117
-
118
- def self_usage_arguments_options
119
- definition.options.select(&:show_on_usage?).map { |option| option_argument(option) }
120
- end
121
-
122
- def self_usage_arguments_positional
123
- definition.positional.map { |p| positional_argument(p) }
124
- end
125
- end
126
- end
127
- 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