eac_cli 0.16.3 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) 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 +1 -1
  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 +39 -0
  13. data/lib/eac_cli/docopt_runner/_doc.rb +23 -0
  14. data/lib/eac_cli/docopt_runner/_settings.rb +17 -0
  15. data/lib/eac_cli/docopt_runner/_subcommands.rb +152 -0
  16. data/lib/eac_cli/docopt_runner/class_methods.rb +18 -0
  17. data/lib/eac_cli/docopt_runner/context.rb +18 -0
  18. data/lib/eac_cli/old_configs.rb +35 -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/object/runner_with.rb +1 -1
  25. data/lib/eac_cli/runner_with/help.rb +6 -2
  26. data/lib/eac_cli/runner_with/subcommands.rb +10 -2
  27. data/lib/eac_cli/runner_with/subcommands/definition_concern.rb +10 -0
  28. data/lib/eac_cli/speaker.rb +127 -0
  29. data/lib/eac_cli/speaker/_constants.rb +12 -0
  30. data/lib/eac_cli/speaker/list.rb +61 -0
  31. data/lib/eac_cli/speaker/options.rb +38 -0
  32. data/lib/eac_cli/version.rb +1 -1
  33. metadata +67 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30d359d444a24b0a8f618b39e2630c1272b8e7eb372c5785641f3a64931cff93
4
- data.tar.gz: 0e1391a2382ead2b2d2e3d15d0fc036cfb88f6bde71dac7fdf14bcd633cd57bf
3
+ metadata.gz: bc6dcd6871b0568383cc0780ac15f0733f44587081c293ddbac199ebda83ffd4
4
+ data.tar.gz: c7480fc2621cfa9a8c9e40e5784be7ac8a3e2763ba6cb9d289f68f4dc75b0a82
5
5
  SHA512:
6
- metadata.gz: 0a2bb5941764c0c3172edec4468ec9c31c747eb6fdc899575504c43bd6354d361c33808af41b6dd6019e9c6331ffa2a96ec950bf71f33c0ec48d1c8433e7b194
7
- data.tar.gz: bf0c7409ebb3db51fad860006e221730032da4c916d754fda5ace314f1a4d4f2894dbc3ad6a533584336cc039e6e775b09b746c74bd4f7aec79bb850ccb67224
6
+ metadata.gz: 4bb04ad2674595e31a3dbe906fba02c14c83875e099b4af39ff0e51153404bf97f99cd639716e9e7e9095289036f752e7aa6d0bd763c5339bbb6be03b6c587b5
7
+ data.tar.gz: a849110f05d77d66f92a1485e3c6529034c27332cdbeed8458f951a2dff522ac79b57979a78e8cb4d11d2053d838d6f7c377c578dc6c90304b0ced44648db34d
@@ -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
+ enable_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
+ 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
@@ -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,39 @@
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
+ extend ::EacCli::DocoptRunner::ClassMethods
10
+ include ::EacCli::DocoptRunner::Context
11
+
12
+ class << self
13
+ def create(settings = {})
14
+ new(settings)
15
+ end
16
+ end
17
+
18
+ attr_reader :settings
19
+
20
+ def initialize(settings = {})
21
+ @settings = settings.with_indifferent_access.freeze
22
+ check_subcommands
23
+ end
24
+
25
+ def options
26
+ @options ||= ::Docopt.docopt(target_doc, docopt_options)
27
+ end
28
+
29
+ def parent
30
+ settings[:parent]
31
+ end
32
+
33
+ protected
34
+
35
+ def docopt_options
36
+ settings.slice(:version, :argv, :help, :options_first).to_sym_keys_hash
37
+ end
38
+ end
39
+ 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
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'docopt'
4
+
5
+ module EacCli
6
+ class DocoptRunner
7
+ module ClassMethods
8
+ DOCOPT_ERROR_EXIT_CODE = 0xC0
9
+
10
+ def run(options = {})
11
+ create(options).send(:run)
12
+ rescue ::Docopt::Exit => e
13
+ STDERR.write(e.message + "\n")
14
+ ::Kernel.exit(DOCOPT_ERROR_EXIT_CODE)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EacCli
4
+ class DocoptRunner
5
+ # Provides the method context which search and call a method in self and ancestor objects.
6
+ module Context
7
+ def context(method)
8
+ current = self
9
+ while current
10
+ return current.send(method) if current.respond_to?(method)
11
+
12
+ current = current.respond_to?(:parent) ? current.parent : nil
13
+ end
14
+ raise "Context method \"#{method}\" not found for #{self.class}"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module EacCli
6
+ class OldConfigs
7
+ require_sub __FILE__
8
+ enable_speaker
9
+
10
+ class << self
11
+ def entry_key_to_envvar_name(entry_key)
12
+ ::EacCli::OldConfigs::EntryReader.entry_key_to_envvar_name(entry_key)
13
+ end
14
+ end
15
+
16
+ attr_reader :configs
17
+
18
+ def initialize(configs_key, options = {})
19
+ options.assert_argument(::Hash, 'options')
20
+ @configs = ::EacConfig::OldConfigs.new(configs_key, options.merge(autosave: true))
21
+ end
22
+
23
+ def read_password(entry_key, options = {})
24
+ ::EacCli::OldConfigs::PasswordEntryReader.new(self, entry_key, options).read
25
+ end
26
+
27
+ def read_entry(entry_key, options = {})
28
+ ::EacCli::OldConfigs::EntryReader.new(self, entry_key, options).read
29
+ end
30
+
31
+ def store_passwords?
32
+ ::EacCli::OldConfigs::StorePasswordsEntryReader.new(self) == 'yes'
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_cli/old_configs/read_entry_options'
4
+ require 'eac_config/paths_hash'
5
+ require 'eac_ruby_utils/core_ext'
6
+
7
+ module EacCli
8
+ class OldConfigs
9
+ class EntryReader
10
+ enable_speaker
11
+
12
+ class << self
13
+ def entry_key_to_envvar_name(entry_key)
14
+ path = if entry_key.is_a?(::Array)
15
+ entry_key
16
+ else
17
+ ::EacConfig::PathsHash.parse_entry_key(entry_key)
18
+ end
19
+ path.join('_').gsub(/[^a-z0-9_]/i, '').gsub(/\A_+/, '').gsub(/_+\z/, '')
20
+ .gsub(/_{2,}/, '_').upcase
21
+ end
22
+ end
23
+
24
+ common_constructor :console_configs, :entry_key, :options do
25
+ self.options = ::EacCli::OldConfigs::ReadEntryOptions.new(options)
26
+ end
27
+
28
+ def read
29
+ %w[envvars storage console].each do |suffix|
30
+ value = send("read_from_#{suffix}")
31
+ return value if value.present?
32
+ end
33
+ return nil unless options[:required]
34
+
35
+ raise "No value found for entry \"#{entry_key}\""
36
+ end
37
+
38
+ def read_from_storage
39
+ console_configs.configs.read_entry(entry_key)
40
+ end
41
+
42
+ def read_from_envvars
43
+ return if options[:noenv]
44
+
45
+ env_entry_key = self.class.entry_key_to_envvar_name(entry_key)
46
+ return unless ENV.key?(env_entry_key)
47
+
48
+ ENV.fetch(env_entry_key).if_present(::EacRubyUtils::BlankNotBlank.instance)
49
+ end
50
+
51
+ def read_from_console
52
+ return if options[:noinput]
53
+
54
+ options[:before_input].if_present(&:call)
55
+ entry_value = looped_entry_value_from_input
56
+ console_configs.configs.write_entry(entry_key, entry_value) if options[:store]
57
+ entry_value
58
+ end
59
+
60
+ private
61
+
62
+ def looped_entry_value_from_input
63
+ loop do
64
+ entry_value = entry_value_from_input(entry_key, options)
65
+ next if entry_value.blank?
66
+ next if options[:validator] && !options[:validator].call(entry_value)
67
+
68
+ return entry_value
69
+ end
70
+ end
71
+
72
+ def entry_value_from_input(entry_key, options)
73
+ entry_value = input("Value for entry \"#{entry_key}\"",
74
+ options.request_input_options)
75
+ warn('Entered value is blank') if entry_value.blank?
76
+ entry_value
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_cli/old_configs/entry_reader'
4
+
5
+ module EacCli
6
+ class OldConfigs
7
+ class PasswordEntryReader < ::EacCli::OldConfigs::EntryReader
8
+ ENTRY_KEY = 'core.store_passwords'
9
+
10
+ def initialize(console_configs, entry_key, options = {})
11
+ super(console_configs, entry_key, options.merge(noecho: true,
12
+ store: console_configs.store_passwords?))
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module EacCli
6
+ class OldConfigs
7
+ class ReadEntryOptions
8
+ enable_simple_cache
9
+ common_constructor :options do
10
+ self.options = options.to_h.symbolize_keys
11
+ .assert_valid_keys(DEFAULT_VALUES.keys).freeze
12
+ end
13
+
14
+ DEFAULT_VALUES = {
15
+ before_input: nil, bool: false, list: false, noecho: false, noenv: false, noinput: false,
16
+ required: true, store: true, validator: nil
17
+ }.freeze
18
+
19
+ delegate :to_h, to: :options
20
+
21
+ def [](key)
22
+ values.fetch(key.to_sym)
23
+ end
24
+
25
+ def request_input_options
26
+ values.slice(:bool, :list, :noecho)
27
+ end
28
+
29
+ private
30
+
31
+ def values_uncached
32
+ consumer = options.to_options_consumer
33
+ r = {}
34
+ DEFAULT_VALUES.each do |key, default_value|
35
+ value = consumer.consume(key)
36
+ value = default_value if value.nil?
37
+ r[key] = value
38
+ end
39
+ consumer.validate
40
+ r
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_cli/old_configs/entry_reader'
4
+
5
+ module EacCli
6
+ class OldConfigs
7
+ class StorePasswordsEntryReader < ::EacCli::OldConfigs::EntryReader
8
+ ENTRY_KEY = 'core.store_passwords'
9
+
10
+ def initialize(console_configs)
11
+ super(console_configs, ENTRY_KEY,
12
+ before_input: -> { banner },
13
+ validator: ->(entry_value) { %w[yes no].include?(entry_value) }
14
+ )
15
+ end
16
+
17
+ def banner
18
+ infom 'Do you wanna to store passwords?'
19
+ infom 'Warning: the passwords will be store in clear text in ' \
20
+ "\"#{console_configs.configs.storage_path}\""
21
+ infom 'Enter "yes" or "no"'
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_cli/config'
4
+ require 'eac_ruby_utils/core_ext'
5
+
6
+ module EacCli
7
+ class OldConfigsBridge < ::EacCli::Config
8
+ ENTRY_KEY = 'core.store_passwords'
9
+
10
+ class << self
11
+ def new_configs_path(configs_key, options)
12
+ options[:storage_path] || ::File.join(ENV['HOME'], '.config', configs_key, 'settings.yml')
13
+ end
14
+ end
15
+
16
+ def initialize(configs_key, options = {})
17
+ options.assert_argument(::Hash, 'options')
18
+ envvar_node = ::EacConfig::EnvvarsNode.new
19
+ file_node = ::EacConfig::YamlFileNode.new(self.class.new_configs_path(configs_key, options))
20
+ envvar_node.load_path.push(file_node.url)
21
+ envvar_node.write_node = file_node
22
+ super(envvar_node)
23
+ end
24
+
25
+ def read_entry(entry_key, options = {})
26
+ entry(entry_key, options).value
27
+ end
28
+
29
+ def read_password(entry_key, options = {})
30
+ entry(entry_key, options.merge(noecho: true, store: store_passwords?)).value
31
+ end
32
+
33
+ def store_passwords?
34
+ read_entry(ENTRY_KEY, bool: true)
35
+ end
36
+ end
37
+ end
@@ -9,7 +9,7 @@ class Object
9
9
  def runner_with(*runners, &block)
10
10
  include ::EacCli::Runner
11
11
  enable_simple_cache
12
- enable_console_speaker
12
+ enable_speaker
13
13
  runners.each do |runner|
14
14
  include ::EacCli::RunnerWithSet.default.item_to_module(runner)
15
15
  end
@@ -10,8 +10,8 @@ module EacCli
10
10
  include ::EacCli::Runner
11
11
 
12
12
  runner_definition.alt do
13
- options_argument false
14
13
  bool_opt '-h', '--help', 'Show help.', usage: true
14
+ pos_arg :any_arg_with_help, repeat: true, optional: true, visible: false
15
15
  end
16
16
 
17
17
  set_callback :run, :before do
@@ -20,7 +20,7 @@ module EacCli
20
20
  end
21
21
 
22
22
  def help_run
23
- return unless parsed.help?
23
+ return unless show_help?
24
24
 
25
25
  puts help_text
26
26
  raise ::EacCli::Runner::Exit
@@ -31,6 +31,10 @@ module EacCli
31
31
  r += help_extra_text if respond_to?(:help_extra_text)
32
32
  r
33
33
  end
34
+
35
+ def show_help?
36
+ parsed.help? && !if_respond(:run_subcommand?, false)
37
+ end
34
38
  end
35
39
  end
36
40
  end
@@ -6,16 +6,20 @@ require 'eac_ruby_utils/core_ext'
6
6
  module EacCli
7
7
  module RunnerWith
8
8
  module Subcommands
9
+ require_sub __FILE__
10
+
9
11
  class << self
10
12
  def runner?(object)
11
13
  ::EacCli::Runner.runner?(object) || (
12
- object.is_a?(::Class) && object < ::EacRubyUtils::Console::DocoptRunner
14
+ object.is_a?(::Class) && object < ::EacCli::DocoptRunner
13
15
  )
14
16
  end
15
17
  end
16
18
 
17
19
  common_concern do
18
20
  include ::EacCli::Runner
21
+ runner_definition.singleton_class
22
+ .include(::EacCli::RunnerWith::Subcommands::DefinitionConcern)
19
23
  end
20
24
 
21
25
  EXTRA_AVAILABLE_SUBCOMMANDS_METHOD_NAME = :extra_available_subcommands
@@ -56,7 +60,7 @@ module EacCli
56
60
  end
57
61
 
58
62
  def run_with_subcommand
59
- if subcommand_name
63
+ if run_subcommand?
60
64
  if subcommand_runner.respond_to?(:run_run)
61
65
  subcommand_runner.run_run
62
66
  else
@@ -75,6 +79,10 @@ module EacCli
75
79
  "Method #{__method__} should be overrided in #{self.class.name}"
76
80
  end
77
81
 
82
+ def run_subcommand?
83
+ subcommand_name.present?
84
+ end
85
+
78
86
  def subcommands?
79
87
  self.class.runner_definition.subcommands?
80
88
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EacCli
4
+ module RunnerWith
5
+ module Subcommands
6
+ module DefinitionConcern
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'colorize'
4
+ require 'eac_ruby_utils/core_ext'
5
+ require 'eac_ruby_utils/speaker/receiver'
6
+
7
+ module EacCli
8
+ class Speaker
9
+ require_sub __FILE__, include_modules: true
10
+ include ::EacRubyUtils::Speaker::Receiver
11
+
12
+ common_constructor :options, default: [{}] do
13
+ self.options = self.class.lists.option.hash_keys_validate!(options)
14
+ end
15
+
16
+ def puts(string = '')
17
+ string.to_s.each_line do |line|
18
+ err_out.puts(err_line_prefix.to_s + line)
19
+ end
20
+ end
21
+
22
+ def out(string = '')
23
+ out_out.write(string.to_s)
24
+ end
25
+
26
+ def fatal_error(string)
27
+ puts "ERROR: #{string}".white.on_red
28
+ Kernel.exit 1
29
+ end
30
+
31
+ def title(string)
32
+ string = string.to_s
33
+ puts(('-' * (8 + string.length)).green)
34
+ puts("--- #{string} ---".green)
35
+ puts(('-' * (8 + string.length)).green)
36
+ puts
37
+ end
38
+
39
+ def info(string)
40
+ puts string.to_s.white
41
+ end
42
+
43
+ def infom(string)
44
+ puts string.to_s.light_yellow
45
+ end
46
+
47
+ def warn(string)
48
+ puts string.to_s.yellow
49
+ end
50
+
51
+ # Options:
52
+ # +bool+ ([Boolean], default: +false+): requires a answer "yes" or "no".
53
+ # +list+ ([Hash] or [Array], default: +nil+): requires a answer from a list.
54
+ # +noecho+ ([Boolean], default: +false+): does not output answer.
55
+ def input(question, options = {})
56
+ bool, list, noecho = options.to_options_consumer.consume_all(:bool, :list, :noecho)
57
+ if list
58
+ request_from_list(question, list, noecho)
59
+ elsif bool
60
+ request_from_bool(question, noecho)
61
+ else
62
+ request_string(question, noecho)
63
+ end
64
+ end
65
+
66
+ def infov(*args)
67
+ r = []
68
+ args.each_with_index do |v, i|
69
+ if i.even?
70
+ r << "#{v}: ".cyan
71
+ else
72
+ r.last << v.to_s
73
+ end
74
+ end
75
+ puts r.join(', ')
76
+ end
77
+
78
+ def success(string)
79
+ puts string.to_s.green
80
+ end
81
+
82
+ private
83
+
84
+ def list_value(list, input)
85
+ values = list_values(list)
86
+ return input, true unless values
87
+ return input, false unless values.include?(input)
88
+ end
89
+
90
+ def list_values(list)
91
+ if list.is_a?(::Hash)
92
+ list.keys.map(&:to_s)
93
+ elsif list.is_a?(::Array)
94
+ list.map(&:to_s)
95
+ end
96
+ end
97
+
98
+ def request_from_bool(question, noecho)
99
+ request_from_list(question, { yes: true, y: true, no: false, n: false }, noecho)
100
+ end
101
+
102
+ def request_from_list(question, list, noecho)
103
+ list = ::EacCli::Speaker::List.build(list)
104
+ loop do
105
+ input = request_string("#{question} [#{list.valid_labels.join('/')}]", noecho)
106
+ return list.build_value(input) if list.valid_value?(input)
107
+
108
+ warn "Invalid input: \"#{input}\" (Valid: #{list.valid_labels.join(', ')})"
109
+ end
110
+ end
111
+
112
+ def request_string(question, noecho)
113
+ err_out.write "#{question}: ".to_s.yellow
114
+ noecho ? request_string_noecho : request_string_echo
115
+ end
116
+
117
+ def request_string_noecho
118
+ r = in_in.noecho(&:gets).chomp.strip
119
+ err_out.write("\n")
120
+ r
121
+ end
122
+
123
+ def request_string_echo
124
+ in_in.gets.chomp.strip
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/by_reference'
4
+
5
+ module EacCli
6
+ # https://github.com/fazibear/colorize
7
+ class Speaker
8
+ STDERR = ::EacRubyUtils::ByReference.new { $stderr }
9
+ STDIN = ::EacRubyUtils::ByReference.new { $stdin }
10
+ STDOUT = ::EacRubyUtils::ByReference.new { $stdout }
11
+ end
12
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/hash_with_indifferent_access'
4
+ require 'ostruct'
5
+
6
+ module EacCli
7
+ class Speaker
8
+ class List
9
+ class << self
10
+ def build(list)
11
+ return List.new(hash_to_values(list)) if list.is_a?(::Hash)
12
+ return List.new(array_to_values(list)) if list.is_a?(::Array)
13
+
14
+ raise "Invalid list: #{list} (#{list.class})"
15
+ end
16
+
17
+ private
18
+
19
+ def hash_to_values(list)
20
+ list.map { |key, value| ::OpenStruct.new(key: key, label: key, value: value) }
21
+ end
22
+
23
+ def array_to_values(list)
24
+ list.map { |value| ::OpenStruct.new(key: value, label: value, value: value) }
25
+ end
26
+ end
27
+
28
+ attr_reader :values
29
+
30
+ def initialize(values)
31
+ @values = values.map do |v|
32
+ ::OpenStruct.new(key: to_key(v.key), label: to_label(v.label), value: v.value)
33
+ end
34
+ end
35
+
36
+ def valid_labels
37
+ values.map(&:label)
38
+ end
39
+
40
+ def valid_value?(value)
41
+ values.any? { |v| v.key == to_key(value) }
42
+ end
43
+
44
+ def to_key(value)
45
+ to_label(value).downcase
46
+ end
47
+
48
+ def to_label(value)
49
+ value.to_s.strip
50
+ end
51
+
52
+ def build_value(value)
53
+ key = to_key(value)
54
+ values.each do |v| # rubocop:disable Style/HashEachMethods
55
+ return v.value if v.key == key
56
+ end
57
+ raise "Value not found: \"#{value}\" (#{values})"
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module EacCli
6
+ class Speaker
7
+ module Options
8
+ common_concern do
9
+ enable_listable
10
+ lists.add_symbol :option, :out_out, :err_out, :in_in, :parent, :err_line_prefix
11
+ end
12
+
13
+ def err_out
14
+ option(OPTION_ERR_OUT, ::EacCli::Speaker::STDERR)
15
+ end
16
+
17
+ def out_out
18
+ option(OPTION_OUT_OUT, ::EacCli::Speaker::STDOUT)
19
+ end
20
+
21
+ def in_in
22
+ option(OPTION_IN_IN, ::EacCli::Speaker::STDIN)
23
+ end
24
+
25
+ def err_line_prefix
26
+ option(OPTION_ERR_LINE_PREFIX, '')
27
+ end
28
+
29
+ def parent
30
+ options[OPTION_PARENT]
31
+ end
32
+
33
+ def option(key, default)
34
+ options[key] || parent.if_present(default) { |v| v.send(__METHOD__) }
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EacCli
4
- VERSION = '0.16.3'
4
+ VERSION = '0.20.1'
5
5
  end
metadata CHANGED
@@ -1,29 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eac_cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.3
4
+ version: 0.20.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esquilo Azul Company
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-06 00:00:00.000000000 Z
11
+ date: 2021-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: docopt
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.6.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.6.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: eac_config
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.3'
13
55
  - !ruby/object:Gem::Dependency
14
56
  name: eac_ruby_utils
15
57
  requirement: !ruby/object:Gem::Requirement
16
58
  requirements:
17
59
  - - "~>"
18
60
  - !ruby/object:Gem::Version
19
- version: '0.55'
61
+ version: '0.67'
20
62
  type: :runtime
21
63
  prerelease: false
22
64
  version_requirements: !ruby/object:Gem::Requirement
23
65
  requirements:
24
66
  - - "~>"
25
67
  - !ruby/object:Gem::Version
26
- version: '0.55'
68
+ version: '0.67'
27
69
  - !ruby/object:Gem::Dependency
28
70
  name: eac_ruby_gem_support
29
71
  requirement: !ruby/object:Gem::Requirement
@@ -46,6 +88,10 @@ extra_rdoc_files: []
46
88
  files:
47
89
  - Gemfile
48
90
  - lib/eac_cli.rb
91
+ - lib/eac_cli/config.rb
92
+ - lib/eac_cli/config/entry.rb
93
+ - lib/eac_cli/config/entry/options.rb
94
+ - lib/eac_cli/config/entry/undefined.rb
49
95
  - lib/eac_cli/core_ext.rb
50
96
  - lib/eac_cli/default_runner.rb
51
97
  - lib/eac_cli/definition.rb
@@ -60,6 +106,18 @@ files:
60
106
  - lib/eac_cli/docopt/doc_builder/alternative.rb
61
107
  - lib/eac_cli/docopt/runner_context_replacement.rb
62
108
  - lib/eac_cli/docopt/runner_extension.rb
109
+ - lib/eac_cli/docopt_runner.rb
110
+ - lib/eac_cli/docopt_runner/_doc.rb
111
+ - lib/eac_cli/docopt_runner/_settings.rb
112
+ - lib/eac_cli/docopt_runner/_subcommands.rb
113
+ - lib/eac_cli/docopt_runner/class_methods.rb
114
+ - lib/eac_cli/docopt_runner/context.rb
115
+ - lib/eac_cli/old_configs.rb
116
+ - lib/eac_cli/old_configs/entry_reader.rb
117
+ - lib/eac_cli/old_configs/password_entry_reader.rb
118
+ - lib/eac_cli/old_configs/read_entry_options.rb
119
+ - lib/eac_cli/old_configs/store_passwords_entry_reader.rb
120
+ - lib/eac_cli/old_configs_bridge.rb
63
121
  - lib/eac_cli/parser.rb
64
122
  - lib/eac_cli/parser/alternative.rb
65
123
  - lib/eac_cli/parser/alternative/argv.rb
@@ -82,7 +140,12 @@ files:
82
140
  - lib/eac_cli/runner_with.rb
83
141
  - lib/eac_cli/runner_with/help.rb
84
142
  - lib/eac_cli/runner_with/subcommands.rb
143
+ - lib/eac_cli/runner_with/subcommands/definition_concern.rb
85
144
  - lib/eac_cli/runner_with_set.rb
145
+ - lib/eac_cli/speaker.rb
146
+ - lib/eac_cli/speaker/_constants.rb
147
+ - lib/eac_cli/speaker/list.rb
148
+ - lib/eac_cli/speaker/options.rb
86
149
  - lib/eac_cli/version.rb
87
150
  homepage:
88
151
  licenses: []