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
@@ -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,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+ require 'eac_cli/patches/module/speaker'
5
+
6
+ module EacCli
7
+ class OldConfigs
8
+ require_sub __FILE__
9
+ enable_speaker
10
+
11
+ class << self
12
+ def entry_key_to_envvar_name(entry_key)
13
+ ::EacCli::OldConfigs::EntryReader.entry_key_to_envvar_name(entry_key)
14
+ end
15
+ end
16
+
17
+ attr_reader :configs
18
+
19
+ def initialize(configs_key, options = {})
20
+ options.assert_argument(::Hash, 'options')
21
+ @configs = ::EacConfig::OldConfigs.new(configs_key, options.merge(autosave: true))
22
+ end
23
+
24
+ def read_password(entry_key, options = {})
25
+ ::EacCli::OldConfigs::PasswordEntryReader.new(self, entry_key, options).read
26
+ end
27
+
28
+ def read_entry(entry_key, options = {})
29
+ ::EacCli::OldConfigs::EntryReader.new(self, entry_key, options).read
30
+ end
31
+
32
+ def store_passwords?
33
+ ::EacCli::OldConfigs::StorePasswordsEntryReader.new(self) == 'yes'
34
+ end
35
+ end
36
+ 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 = request_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
@@ -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,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_cli/speaker'
4
+ require 'eac_ruby_utils/patch'
5
+
6
+ class Module
7
+ def enable_speaker
8
+ ::EacRubyUtils.patch(self, ::EacCli::Speaker)
9
+ end
10
+ 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,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'colorize'
4
+ require 'io/console'
5
+ require 'eac_ruby_utils/patches/hash/options_consumer'
6
+ require 'eac_ruby_utils/require_sub'
7
+ ::EacRubyUtils.require_sub __FILE__
8
+
9
+ module EacCli
10
+ # https://github.com/fazibear/colorize
11
+ module Speaker
12
+ def on_speaker_node(&block)
13
+ ::EacCli::Speaker.on_node(&block)
14
+ end
15
+
16
+ def puts(string = '')
17
+ string.to_s.each_line do |line|
18
+ current_node.stderr.puts(current_node.stderr_line_prefix.to_s + line)
19
+ end
20
+ end
21
+
22
+ def out(string = '')
23
+ current_node.stdout.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 request_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
+ current_node.stderr.write "#{question}: ".to_s.yellow
114
+ noecho ? request_string_noecho : request_string_echo
115
+ end
116
+
117
+ def request_string_noecho
118
+ r = current_node.stdin.noecho(&:gets).chomp.strip
119
+ current_node.stderr.write("\n")
120
+ r
121
+ end
122
+
123
+ def request_string_echo
124
+ current_node.stdin.gets.chomp.strip
125
+ end
126
+
127
+ def current_node
128
+ ::EacCli::Speaker.current_node
129
+ end
130
+ end
131
+ end