eac_cli 0.16.1 → 0.19.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/eac_cli/config.rb +19 -0
  3. data/lib/eac_cli/config/entry.rb +48 -0
  4. data/lib/eac_cli/config/entry/options.rb +57 -0
  5. data/lib/eac_cli/config/entry/undefined.rb +26 -0
  6. data/lib/eac_cli/default_runner.rb +1 -1
  7. data/lib/eac_cli/definition/help_formatter.rb +2 -2
  8. data/lib/eac_cli/definition/positional_argument.rb +6 -1
  9. data/lib/eac_cli/docopt/doc_builder.rb +4 -2
  10. data/lib/eac_cli/docopt/doc_builder/alternative.rb +6 -4
  11. data/lib/eac_cli/docopt/runner_extension.rb +2 -2
  12. data/lib/eac_cli/docopt_runner.rb +38 -0
  13. data/lib/eac_cli/docopt_runner/_class_methods.rb +16 -0
  14. data/lib/eac_cli/docopt_runner/_doc.rb +23 -0
  15. data/lib/eac_cli/docopt_runner/_settings.rb +17 -0
  16. data/lib/eac_cli/docopt_runner/_subcommands.rb +152 -0
  17. data/lib/eac_cli/docopt_runner/context.rb +18 -0
  18. data/lib/eac_cli/old_configs.rb +36 -0
  19. data/lib/eac_cli/old_configs/entry_reader.rb +80 -0
  20. data/lib/eac_cli/old_configs/password_entry_reader.rb +16 -0
  21. data/lib/eac_cli/old_configs/read_entry_options.rb +44 -0
  22. data/lib/eac_cli/old_configs/store_passwords_entry_reader.rb +25 -0
  23. data/lib/eac_cli/old_configs_bridge.rb +37 -0
  24. data/lib/eac_cli/patches/module.rb +4 -0
  25. data/lib/eac_cli/patches/module/speaker.rb +10 -0
  26. data/lib/eac_cli/patches/object/runner_with.rb +1 -1
  27. data/lib/eac_cli/runner_with/help.rb +6 -2
  28. data/lib/eac_cli/runner_with/subcommands.rb +10 -2
  29. data/lib/eac_cli/runner_with/subcommands/definition_concern.rb +10 -0
  30. data/lib/eac_cli/speaker.rb +131 -0
  31. data/lib/eac_cli/speaker/_class_methods.rb +37 -0
  32. data/lib/eac_cli/speaker/_constants.rb +12 -0
  33. data/lib/eac_cli/speaker/list.rb +61 -0
  34. data/lib/eac_cli/speaker/node.rb +24 -0
  35. data/lib/eac_cli/version.rb +1 -1
  36. metadata +72 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23a97e547ebd10f3efd4f64da1d46c47942b46baacf7407e0dc9079c76c20f4a
4
- data.tar.gz: ddcabc7f42f2f616c86435098d7e2d9b840342965abf6e65d72f7311a0cdf06d
3
+ metadata.gz: cfee9c78c899be3c4e2d36915e4d0cac08cf5f2a63269813b5c05527a06d0e75
4
+ data.tar.gz: c0374336ed9fea6c351ce00b0a9e5e210b53ca7a34c201dc1a0699fcda12b943
5
5
  SHA512:
6
- metadata.gz: a56fa25e3023ab586c482573f170584927fd451dd9ab8fd0f53ae9707dfc037af24293ab856254512916c31bf218890b89e647a432e4ea1064fc3555ddd61e47
7
- data.tar.gz: 0e303addfc58031901fb5a559184cd07fed828ef5ab80ffd7f6f16ad6f84b02136c181e90bb825799ed6af775747d0668ff6cda2179338f5366e4e7bcb2ec50f
6
+ metadata.gz: d2e1c8009e10490cde982e4d8cc0fce02d67d0114ddb3a852180298c6f83edd7d5e4e2ee0bf04188199d615739297b3fdbf41a61d532e6614b86dece3c63b864
7
+ data.tar.gz: fba7139e87ea3959a2498f20d83a08d19c299acd71e576ed0d446ca47a77bb97ab9e670c4280984001feb6467d6cdd01d3a989dc6b44b5e4d5345d9f87c66a0c
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_config/envvars_node'
4
+ require 'eac_config/yaml_file_node'
5
+
6
+ module EacCli
7
+ class Config
8
+ require_sub __FILE__
9
+ attr_reader :sub
10
+
11
+ def initialize(sub_node)
12
+ @sub = sub_node
13
+ end
14
+
15
+ def entry(path, options = {})
16
+ ::EacCli::Config::Entry.new(self, path, options)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_cli/speaker'
4
+ require 'eac_config/entry_path'
5
+ require 'eac_ruby_utils/core_ext'
6
+
7
+ module EacCli
8
+ class Config
9
+ class Entry
10
+ require_sub __FILE__, include_modules: true
11
+ enable_listable
12
+ enable_simple_cache
13
+ include ::EacCli::Speaker
14
+
15
+ common_constructor :config, :path, :options do
16
+ self.path = ::EacConfig::EntryPath.assert(path)
17
+ self.options = ::EacCli::Config::Entry::Options.new(options)
18
+ end
19
+
20
+ def value
21
+ return sub_value_to_return if sub_entry.found?
22
+ return nil unless options.required?
23
+
24
+ puts "|#{sub_entry.path}|"
25
+
26
+ input_value
27
+ end
28
+
29
+ delegate :value=, to: :sub_entry
30
+
31
+ private
32
+
33
+ def sub_value_to_return
34
+ sub_entry.value.presence || ::EacRubyUtils::BlankNotBlank.instance
35
+ end
36
+
37
+ def sub_entry_uncached
38
+ config.sub.entry(path)
39
+ end
40
+
41
+ def input_value_uncached
42
+ r = send("#{options.type}_value")
43
+ sub_entry.value = r if options.store?
44
+ r
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module EacCli
6
+ class Config
7
+ class Entry
8
+ class Options
9
+ enable_simple_cache
10
+ enable_listable
11
+
12
+ lists.add_symbol :type, :undefined
13
+
14
+ DEFAULT_VALUES = {
15
+ before_input: nil, bool: false, list: false, noecho: false, noenv: false, noinput: false,
16
+ required: true, store: true, type: TYPE_UNDEFINED, validator: nil
17
+ }.freeze
18
+
19
+ lists.add_symbol :option, *DEFAULT_VALUES.keys
20
+
21
+ common_constructor :options do
22
+ self.options = self.class.lists.option.hash_keys_validate!(options)
23
+ end
24
+
25
+ delegate :to_h, to: :options
26
+
27
+ def [](key)
28
+ values.fetch(key.to_sym)
29
+ end
30
+
31
+ def request_input_options
32
+ values.slice(:bool, :list, :noecho)
33
+ end
34
+
35
+ DEFAULT_VALUES.each do |attr, default_value|
36
+ define_method(attr.to_s + ([true, false].include?(default_value) ? '?' : '')) do
37
+ self[attr]
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def values_uncached
44
+ consumer = options.to_options_consumer
45
+ r = {}
46
+ DEFAULT_VALUES.each do |key, default_value|
47
+ value = consumer.consume(key)
48
+ value = default_value if value.nil?
49
+ r[key] = value
50
+ end
51
+ consumer.validate
52
+ r
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module EacCli
6
+ class Config
7
+ class Entry
8
+ module Undefined
9
+ private
10
+
11
+ def undefined_value
12
+ loop do
13
+ entry_value = undefined_value_no_loop
14
+ next unless options[:validator].if_present(true) { |v| v.call(entry_value) }
15
+
16
+ return entry_value
17
+ end
18
+ end
19
+
20
+ def undefined_value_no_loop
21
+ request_input("Value for entry \"#{path}\"", options.request_input_options)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -7,7 +7,7 @@ module EacCli
7
7
  module DefaultRunner
8
8
  common_concern do
9
9
  include ::EacCli::RunnerWith::Help
10
- enable_console_speaker
10
+ enable_speaker
11
11
  enable_simple_cache
12
12
  end
13
13
  end
@@ -27,7 +27,7 @@ module EacCli
27
27
 
28
28
  def positional_argument(positional)
29
29
  if positional.subcommand?
30
- ::EacRubyUtils::Console::DocoptRunner::SUBCOMMANDS_MACRO
30
+ ::EacCli::DocoptRunner::SUBCOMMANDS_MACRO
31
31
  else
32
32
  r = "<#{positional.name}>"
33
33
  r += '...' if positional.repeat?
@@ -52,7 +52,7 @@ module EacCli
52
52
  end
53
53
 
54
54
  def self_usage_arguments
55
- [::EacRubyUtils::Console::DocoptRunner::PROGRAM_MACRO] +
55
+ [::EacCli::DocoptRunner::PROGRAM_MACRO] +
56
56
  definition.options_argument.if_present([]) { |_v| ['[options]'] } +
57
57
  self_usage_arguments_options +
58
58
  self_usage_arguments_positional
@@ -6,9 +6,10 @@ module EacCli
6
6
  class Definition
7
7
  class PositionalArgument
8
8
  DEFAULT_REQUIRED = true
9
+ DEFAULT_VISIBLE = true
9
10
 
10
11
  enable_listable
11
- lists.add_symbol :option, :optional, :repeat, :required, :subcommand
12
+ lists.add_symbol :option, :optional, :repeat, :required, :subcommand, :visible
12
13
  common_constructor :name, :options, default: [{}] do
13
14
  options.assert_valid_keys(self.class.lists.option.values)
14
15
  end
@@ -51,6 +52,10 @@ module EacCli
51
52
  def to_s
52
53
  "#{self.class.name.demodulize}[#{identifier}]"
53
54
  end
55
+
56
+ def visible?
57
+ options.key?(OPTION_VISIBLE) ? options.fetch(OPTION_VISIBLE) : DEFAULT_VISIBLE
58
+ end
54
59
  end
55
60
  end
56
61
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'eac_ruby_utils/core_ext'
4
- require 'eac_ruby_utils/console/docopt_runner'
4
+ require 'eac_cli/docopt_runner'
5
5
 
6
6
  module EacCli
7
7
  module Docopt
@@ -36,7 +36,9 @@ module EacCli
36
36
  end
37
37
 
38
38
  def option_definition(option)
39
- self.class.option_usage_full(option) + OPTION_DESC_SEP + option.description
39
+ self.class.option_usage_full(option) + option.description.if_present('') do |v|
40
+ OPTION_DESC_SEP + v
41
+ end
40
42
  end
41
43
 
42
44
  def section(header, include_header = true)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'eac_ruby_utils/core_ext'
4
- require 'eac_ruby_utils/console/docopt_runner'
4
+ require 'eac_cli/docopt_runner'
5
5
 
6
6
  module EacCli
7
7
  module Docopt
@@ -11,7 +11,7 @@ module EacCli
11
11
 
12
12
  def to_s
13
13
  (
14
- [::EacRubyUtils::Console::DocoptRunner::PROGRAM_MACRO] +
14
+ [::EacCli::DocoptRunner::PROGRAM_MACRO] +
15
15
  alternative.options_argument?.if_present([]) { |_v| ['[options]'] } +
16
16
  options +
17
17
  positionals
@@ -31,12 +31,14 @@ module EacCli
31
31
  end
32
32
 
33
33
  def positionals
34
- alternative.positional.map { |p| positional(p) }
34
+ alternative.positional.map { |p| positional(p) }.reject(&:blank?)
35
35
  end
36
36
 
37
37
  def positional(positional)
38
+ return unless positional.visible?
39
+
38
40
  if positional.subcommand?
39
- ::EacRubyUtils::Console::DocoptRunner::SUBCOMMANDS_MACRO
41
+ ::EacCli::DocoptRunner::SUBCOMMANDS_MACRO
40
42
  else
41
43
  r = "<#{positional.name}>"
42
44
  r += '...' if positional.repeat?
@@ -3,7 +3,7 @@
3
3
  require 'eac_cli/docopt/doc_builder'
4
4
  require 'eac_cli/docopt/runner_context_replacement'
5
5
  require 'eac_cli/runner'
6
- require 'eac_ruby_utils/console/docopt_runner'
6
+ require 'eac_cli/docopt_runner'
7
7
 
8
8
  module EacCli
9
9
  module Docopt
@@ -16,7 +16,7 @@ module EacCli
16
16
 
17
17
  class << self
18
18
  def check(klass)
19
- return unless klass < ::EacRubyUtils::Console::DocoptRunner
19
+ return unless klass < ::EacCli::DocoptRunner
20
20
 
21
21
  ::EacCli::Runner.alias_runner_class_methods(klass, '', 'eac_cli')
22
22
  ::EacCli::Runner.alias_runner_class_methods(klass, 'original', '')
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+ require 'docopt'
5
+
6
+ module EacCli
7
+ class DocoptRunner
8
+ require_sub __FILE__
9
+ include ::EacCli::DocoptRunner::Context
10
+
11
+ class << self
12
+ def create(settings = {})
13
+ new(settings)
14
+ end
15
+ end
16
+
17
+ attr_reader :settings
18
+
19
+ def initialize(settings = {})
20
+ @settings = settings.with_indifferent_access.freeze
21
+ check_subcommands
22
+ end
23
+
24
+ def options
25
+ @options ||= ::Docopt.docopt(target_doc, docopt_options)
26
+ end
27
+
28
+ def parent
29
+ settings[:parent]
30
+ end
31
+
32
+ protected
33
+
34
+ def docopt_options
35
+ settings.slice(:version, :argv, :help, :options_first).to_sym_keys_hash
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EacCli
4
+ class DocoptRunner
5
+ DOCOPT_ERROR_EXIT_CODE = 0xC0
6
+
7
+ class << self
8
+ def run(options = {})
9
+ create(options).send(:run)
10
+ rescue Docopt::Exit => e
11
+ STDERR.write(e.message + "\n")
12
+ ::Kernel.exit(DOCOPT_ERROR_EXIT_CODE)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EacCli
4
+ class DocoptRunner
5
+ PROGRAM_MACRO = '__PROGRAM__'
6
+
7
+ def source_doc
8
+ setting_value(:doc)
9
+ end
10
+
11
+ def target_doc
12
+ source_doc.gsub(PROGRAM_MACRO, target_program_name).strip + "\n"
13
+ end
14
+
15
+ def source_program_name
16
+ setting_value(:program_name, false)
17
+ end
18
+
19
+ def target_program_name
20
+ [source_program_name, ENV['PROGRAM_NAME'], $PROGRAM_NAME].find(&:present?)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/settings_provider'
4
+
5
+ module EacCli
6
+ class DocoptRunner
7
+ include ::EacRubyUtils::SettingsProvider
8
+
9
+ attr_reader :settings
10
+
11
+ private
12
+
13
+ def setting_value(key, required = true)
14
+ super(key, required: required, order: %w[method settings_object constant])
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/string/inflections'
4
+ require 'shellwords'
5
+
6
+ module EacCli
7
+ class DocoptRunner
8
+ SUBCOMMAND_ARG = '<subcommand>'
9
+ SUBCOMMAND_ARGS_ARG = '<subcommand-args>'
10
+ SUBCOMMANDS_MACRO = '__SUBCOMMANDS__'
11
+
12
+ def subcommands?
13
+ source_doc.include?(SUBCOMMANDS_MACRO)
14
+ end
15
+
16
+ def check_subcommands
17
+ return unless subcommands?
18
+
19
+ singleton_class.include(SubcommandsSupport)
20
+ check_subcommands_arg
21
+ return if singleton_class.method_defined?(:run)
22
+
23
+ singleton_class.send(:alias_method, :run, :run_with_subcommand)
24
+ end
25
+
26
+ module SubcommandsSupport
27
+ EXTRA_AVAILABLE_SUBCOMMANDS_METHOD_NAME = :extra_available_subcommands
28
+
29
+ def check_subcommands_arg
30
+ if subcommand_arg_as_list?
31
+ singleton_class.include(SubcommandsSupport::SubcommandArgAsList)
32
+ else
33
+ singleton_class.include(SubcommandsSupport::SubcommandArgAsArg)
34
+ end
35
+ end
36
+
37
+ def run_with_subcommand
38
+ if subcommand_name
39
+ check_valid_subcommand
40
+ subcommand_run
41
+ else
42
+ run_without_subcommand
43
+ end
44
+ end
45
+
46
+ def subcommand
47
+ @subcommand ||= subcommand_class_name(subcommand_name).constantize.create(
48
+ argv: subcommand_args,
49
+ program_name: subcommand_program,
50
+ parent: self
51
+ )
52
+ end
53
+
54
+ def subcommand_run
55
+ if !subcommand.is_a?(::EacCli::DocoptRunner) &&
56
+ subcommand.respond_to?(:run_run)
57
+ subcommand.run_run
58
+ else
59
+ subcommand.run
60
+ end
61
+ end
62
+
63
+ def target_doc
64
+ super.gsub(SUBCOMMANDS_MACRO,
65
+ "#{target_doc_subcommand_arg} [#{SUBCOMMAND_ARGS_ARG}...]") +
66
+ "\n" + subcommands_target_doc
67
+ end
68
+
69
+ def docopt_options
70
+ super.merge(options_first: true)
71
+ end
72
+
73
+ def subcommand_class_name(subcommand)
74
+ "#{self.class.name}::#{subcommand.underscore.camelize}"
75
+ end
76
+
77
+ def subcommand_arg_as_list?
78
+ setting_value(:subcommand_arg_as_list, false) || false
79
+ end
80
+
81
+ def subcommand_args
82
+ options.fetch(SUBCOMMAND_ARGS_ARG)
83
+ end
84
+
85
+ def subcommand_program
86
+ subcommand_name
87
+ end
88
+
89
+ def available_subcommands
90
+ r = ::Set.new(setting_value(:subcommands, false) || auto_available_subcommands)
91
+ if respond_to?(EXTRA_AVAILABLE_SUBCOMMANDS_METHOD_NAME, true)
92
+ r += send(EXTRA_AVAILABLE_SUBCOMMANDS_METHOD_NAME)
93
+ end
94
+ r.sort
95
+ end
96
+
97
+ def auto_available_subcommands
98
+ self.class.constants
99
+ .map { |name| self.class.const_get(name) }
100
+ .select { |c| c.instance_of? Class }
101
+ .select { |c| c < ::EacCli::DocoptRunner }
102
+ .map { |c| c.name.demodulize.underscore.dasherize }
103
+ end
104
+
105
+ def run_without_subcommand
106
+ "Method #{__method__} should be overrided in #{self.class.name}"
107
+ end
108
+
109
+ protected
110
+
111
+ def check_valid_subcommand
112
+ return if available_subcommands.include?(subcommand_name)
113
+
114
+ raise ::Docopt::Exit, "\"#{subcommand_name}\" is not a valid subcommand" \
115
+ " (Valid: #{available_subcommands.join(', ')})"
116
+ end
117
+
118
+ module SubcommandArgAsArg
119
+ def target_doc_subcommand_arg
120
+ SUBCOMMAND_ARG
121
+ end
122
+
123
+ def subcommand_name
124
+ options.fetch(SUBCOMMAND_ARG)
125
+ end
126
+
127
+ def subcommands_target_doc
128
+ available_subcommands.inject("Subcommands:\n") do |a, e|
129
+ a + " #{e}\n"
130
+ end
131
+ end
132
+ end
133
+
134
+ module SubcommandArgAsList
135
+ def target_doc_subcommand_arg
136
+ '(' + available_subcommands.join('|') + ')'
137
+ end
138
+
139
+ def subcommand_name
140
+ available_subcommands.each do |subcommand|
141
+ return subcommand if options.fetch(subcommand)
142
+ end
143
+ nil
144
+ end
145
+
146
+ def subcommands_target_doc
147
+ "\n"
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end