eac_cli 0.6.1 → 0.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be9a0ff56cf7295f999d8fa365e9eb8b153c9f95c63f645ac0b3bb88e063912a
4
- data.tar.gz: '09e64e9fd8dc0efe62b7c237ee0bb9e960ff21b148da33afa41599e8919f16da'
3
+ metadata.gz: 6bfbec2f4b3875640397fa352c5edde397fc335614d7e95a9200472ae8688819
4
+ data.tar.gz: fcf67763d91ea9674b8355b7e5dd587d6d6b3a9e05ae387eea045d364658481c
5
5
  SHA512:
6
- metadata.gz: d35cf09f5d89dbd5243f827f3b75c95c3f26051be6b248e322f532823ff969296a3094571c8c9d91e275f6cf872ab9116fa8d27294009722df916f12508f7649
7
- data.tar.gz: 1dbb6ef81c81fcaea236339cb530cb0ed7700675c1442e040cdea3b5779ea2a33ef9472db39d7f9a43a101d204bb2b080016b5d6487a3d25ea27495a14dc6138
6
+ metadata.gz: ff15edf91a76df0af27aff006f85071f8ff1b4ae5c70930ad5daf8ae129d372e976742d47a817f142b8fa747d8d5e285806619002eab8602d760e40e83ff89d6
7
+ data.tar.gz: 6ff1ae990f460832abf8cfc50136a28cf09d317c85835e384562640b0b48cff4f63fcb794efcad45784bd168b2872f46403dc658cd4bafeab8cceed37bddf2a8
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+ require 'eac_cli/patches'
@@ -1,22 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_support/concern'
4
- require 'eac_cli/runner'
5
- require 'eac_ruby_utils/console/speaker'
6
- require 'eac_ruby_utils/simple_cache'
3
+ require 'eac_cli/runner_with/help'
4
+ require 'eac_ruby_utils/core_ext'
7
5
 
8
6
  module EacCli
9
7
  module DefaultRunner
10
- extend ::ActiveSupport::Concern
11
-
12
- included do
13
- include ::EacCli::Runner
14
- include ::EacRubyUtils::Console::Speaker
15
- include ::EacRubyUtils::SimpleCache
16
- runner_definition.alt do
17
- options_arg false
18
- bool_opt '-h', '--help', 'Show help.', usage: true
19
- end
8
+ common_concern do
9
+ include ::EacCli::RunnerWith::Help
10
+ enable_console_speaker
11
+ enable_simple_cache
20
12
  end
21
13
  end
22
14
  end
@@ -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
- positional << ::EacCli::Definition::PositionalArgument.new(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
65
  end
55
66
 
56
67
  def positional
57
- @positional ||= []
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
- positional << ::EacCli::Definition::PositionalArgument.new('subcommand', subcommand: true)
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.with_indifferent_access
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,82 @@
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 option_argument(option)
29
+ b = option.long
30
+ b += '=VALUE' if option.argument?
31
+ b
32
+ end
33
+
34
+ def positional_argument(positional)
35
+ if positional.subcommand?
36
+ ::EacRubyUtils::Console::DocoptRunner::SUBCOMMANDS_MACRO
37
+ else
38
+ r = "<#{positional.name}>"
39
+ r += '...' if positional.repeat?
40
+ r = "[#{r}]" if positional.optional?
41
+ r
42
+ end
43
+ end
44
+
45
+ def section(header, include_header = true)
46
+ b = include_header ? "#{header.humanize}:\n" : ''
47
+ b += send("self_#{header}") + "\n"
48
+ # TO-DO: implement alternatives
49
+ b
50
+ end
51
+
52
+ def self_options
53
+ definition.options.map { |option| IDENT + option_definition(option) }.join("\n")
54
+ end
55
+
56
+ def self_usage
57
+ IDENT + self_usage_arguments.join(SEP)
58
+ end
59
+
60
+ def self_usage_arguments
61
+ [::EacRubyUtils::Console::DocoptRunner::PROGRAM_MACRO] +
62
+ definition.options_argument.if_present([]) { |_v| ['[options]'] } +
63
+ self_usage_arguments_options +
64
+ self_usage_arguments_positional
65
+ end
66
+
67
+ def self_usage_arguments_options
68
+ definition.options.select(&:show_on_usage?).map do |option|
69
+ self.class.option_argument(option)
70
+ end
71
+ end
72
+
73
+ def self_usage_arguments_positional
74
+ definition.positional.map { |p| positional_argument(p) }
75
+ end
76
+
77
+ def to_banner
78
+ "#{definition.description}\n\n#{section('usage')}"
79
+ end
80
+ end
81
+ end
82
+ end
@@ -5,22 +5,35 @@ require 'eac_ruby_utils/core_ext'
5
5
  module EacCli
6
6
  class Definition
7
7
  class PositionalArgument
8
- common_constructor :name, :options, default: [{}]
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
- options[:optional]
21
+ !required?
16
22
  end
17
23
 
18
24
  def repeat?
19
- options[:repeat]
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[:subcommand]
36
+ options[OPTION_SUBCOMMAND]
24
37
  end
25
38
  end
26
39
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'eac_cli/docopt/doc_builder'
4
+ require 'eac_cli/runner'
4
5
  require 'eac_ruby_utils/console/docopt_runner'
5
6
 
6
7
  module EacCli
@@ -31,6 +31,10 @@ module EacCli
31
31
  end
32
32
  end
33
33
 
34
+ def supplied?(option)
35
+ data[option].present?
36
+ end
37
+
34
38
  private
35
39
 
36
40
  def data
@@ -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
- @arguments = option_parser.parse(argv)
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 = "#{definition.description}\n\n#{section('usage')}"
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), option_long(option), option.description].reject(&:blank?)
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
- argv.each { |argv_value| colect_argv_value(argv_value) }
19
- return unless pending_required_positional?
35
+ loop do
36
+ break unless enums('pending?').any?
20
37
 
21
- raise ::EacCli::Parser::Error.new(
22
- definition, argv, 'No value for required positional ' \
23
- "\"#{current_positional.identifier}\""
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 colect_argv_value(argv_value)
28
- collector.collect(current_positional, argv_value)
29
- collected << current_positional
30
- positional_enumerator.next unless current_positional.repeat?
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 pending_required_positional?
34
- !(current_positional.blank? || current_positional.optional? ||
35
- collected.include?(current_positional))
57
+ def positional_enum
58
+ @positional_enum ||= definition.positional.each
36
59
  end
37
60
 
38
- def positional_enumerator
39
- @positional_enumerator ||= definition.positional.each
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 current_positional
43
- positional_enumerator.peek
44
- rescue ::StopIteration
45
- nil
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,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/require_sub'
4
+ ::EacRubyUtils.require_sub(__FILE__)
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir["#{File.dirname(__FILE__)}/#{::File.basename(__FILE__, '.*')}/*.rb"].sort.each do |path|
4
+ require path
5
+ 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
@@ -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,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module EacCli
6
+ module RunnerWith
7
+ require_sub __FILE__
8
+ end
9
+ end
@@ -0,0 +1,19 @@
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 Help
9
+ common_concern do
10
+ include ::EacCli::Runner
11
+
12
+ runner_definition.alt do
13
+ options_arg false
14
+ bool_opt '-h', '--help', 'Show help.', usage: true
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
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 OutputFile
9
+ common_concern do
10
+ include ::EacCli::Runner
11
+
12
+ runner_definition do
13
+ arg_opt '-o', '--output-file', 'Output to file.'
14
+ end
15
+ end
16
+
17
+ def run_output
18
+ if parsed.output_file.present?
19
+ ::File.write(parsed.output_file, output_content)
20
+ else
21
+ out output_content
22
+ end
23
+ end
24
+ end
25
+ end
26
+ 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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EacCli
4
- VERSION = '0.6.1'
4
+ VERSION = '0.11.0'
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.6.1
4
+ version: 0.11.0
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-09-23 00:00:00.000000000 Z
11
+ date: 2020-11-16 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.45'
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.45'
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,8 +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
76
+ - lib/eac_cli/runner_with.rb
77
+ - lib/eac_cli/runner_with/help.rb
78
+ - lib/eac_cli/runner_with/output_file.rb
79
+ - lib/eac_cli/runner_with/subcommands.rb
71
80
  - lib/eac_cli/version.rb
72
81
  homepage:
73
82
  licenses: []