tabry 0.1.5 → 0.2.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/bin/tabry-bash +2 -29
  3. data/bin/tabry-generate-bash-complete +31 -0
  4. data/bin/tabry-help +1 -1
  5. data/bin/tabry-test-options +1 -1
  6. data/bin/tabry-test-parse +1 -1
  7. data/lib/tabry/cli/all_in_one.rb +98 -0
  8. data/lib/tabry/cli/arg_proxy.rb +4 -4
  9. data/lib/tabry/cli/builder.rb +9 -7
  10. data/lib/tabry/cli/internals.rb +1 -1
  11. data/lib/tabry/cli/util.rb +23 -10
  12. data/lib/tabry/config_builder/arg_or_flag_builder.rb +39 -0
  13. data/lib/tabry/config_builder/flagarg_builder.rb +14 -0
  14. data/lib/tabry/config_builder/generic_builder.rb +103 -0
  15. data/lib/tabry/config_builder/sub_builder.rb +27 -0
  16. data/lib/tabry/config_builder/top_level_builder.rb +31 -0
  17. data/lib/tabry/config_builder.rb +16 -0
  18. data/lib/tabry/models/config_list.rb +2 -2
  19. data/lib/tabry/models/config_string_hash.rb +2 -2
  20. data/lib/tabry/runner.rb +6 -2
  21. data/lib/tabry/shells/bash/wrapper.rb +38 -0
  22. data/lib/tabry/shells/bash.rb +46 -9
  23. data/sh/bash/tabry_bash.sh +1 -1
  24. data/sh/bash/tabry_bash_core.sh +3 -2
  25. data/spec/fixtures/config_builder/args.rb +49 -0
  26. data/spec/fixtures/config_builder/args.tabry +32 -0
  27. data/spec/fixtures/config_builder/args.yml +63 -0
  28. data/spec/fixtures/config_builder/defs.rb +33 -0
  29. data/spec/fixtures/config_builder/defs.tabry +21 -0
  30. data/spec/fixtures/config_builder/defs.yml +33 -0
  31. data/spec/fixtures/config_builder/flags.rb +26 -0
  32. data/spec/fixtures/config_builder/flags.tabry +13 -0
  33. data/spec/fixtures/config_builder/flags.yml +27 -0
  34. data/spec/fixtures/config_builder/subs.rb +34 -0
  35. data/spec/fixtures/config_builder/subs.tabry +25 -0
  36. data/spec/fixtures/config_builder/subs.yml +46 -0
  37. data/spec/fixtures/config_builder/underscoresdashes.rb +30 -0
  38. data/spec/fixtures/config_builder/underscoresdashes.tabry +18 -0
  39. data/spec/fixtures/config_builder/underscoresdashes.yml +39 -0
  40. data/spec/tabry/cli/all_in_one_spec.rb +141 -0
  41. data/spec/tabry/cli/arg_proxy_spec.rb +2 -2
  42. data/spec/tabry/cli/builder_spec.rb +5 -5
  43. data/spec/tabry/cli/util_spec.rb +125 -0
  44. data/spec/tabry/config_builder_spec.rb +64 -0
  45. data/spec/tabry/runner_spec.rb +1 -1
  46. data/spec/tabry/shell_splitter_spec.rb +7 -5
  47. data/spec/tabry/shells/bash_spec.rb +44 -0
  48. data/tabry.gemspec +1 -1
  49. metadata +31 -3
  50. data/spec/lib/tabry/cli/util_spec.rb +0 -60
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe7c5c94f54f4d6fb603a73a8ec6b8364c63b6247a9dc3f095d4359fe96b5018
4
- data.tar.gz: 4a29c24624360099c44dfd5c8c1420ef701bb21ee6535dc1e7a3540315002811
3
+ metadata.gz: 5d76e2e99f45b1f9b964d21f0ed8820d37b973c358b032a97c3d5314afdae8a1
4
+ data.tar.gz: e84a7977c95fc91282291d1406ffc2e577f4c23a140e69b05f3b6fe10da4f85d
5
5
  SHA512:
6
- metadata.gz: b9b58e549801135afe35686048bd3b84b76c9e41cb5ecd5dc49bff3a1c3ea3ac26f10a2187eca5775ce4aa2d02d46157c559cefc2d376267ff62ebcf630b721c
7
- data.tar.gz: a21e775881c48a511363cce6690e99cd13ccb32a3b829670bf049f3de63ab6e590b351a591717bb8137f112eb0e81514f548cefb8e3a0f4a79e5339118fafb33
6
+ metadata.gz: d1a4d514766abadeee72d0f1db0e1de14c0bf1bc26964911460adffaa716f6a6dd9f36604632274b6a4772a3d734903af0543c8689257b214a439679a1ebb8e3
7
+ data.tar.gz: 23351f80640e72147ea32638327e99891aa4b46f8fedee2551ecdcadd2fb63102f6c505b430882171cd736320baac633dc7f93a26f9ccb89dfdd232c4bf6c833
data/bin/tabry-bash CHANGED
@@ -3,34 +3,7 @@
3
3
 
4
4
  # Run by the tabry_bash_core.sh bash tab completion function to plug into Tabry
5
5
  # to get completion options.
6
-
7
- require "shellwords"
8
- require "yaml"
9
- require_relative "../lib/tabry/util"
10
- require_relative "../lib/tabry/runner"
11
- require_relative "../lib/tabry/shell_splitter"
12
-
13
6
  # Bash-specific entrypoint, taking COMP_WORDS and COMP_CWORDS and returning possible options
14
7
 
15
- cmd_line, comp_point = ARGV
16
- cmd_name, args, last_arg = Tabry::ShellSplitter.split(cmd_line, comp_point)
17
- opts = Tabry::Runner.new(config_name: cmd_name).options(args, last_arg)
18
-
19
- if Tabry::Util.debug?
20
- require "json"
21
- $stderr.puts
22
- warn "debug: got command line and comp_point: #{cmd_line.inspect}, #{comp_point}"
23
- warn "using args: #{args.inspect}"
24
- warn "using lastarg: #{last_arg.inspect}"
25
- warn "results from Tabry#options(): #{opts.inspect}"
26
- warn "--- end debug output ---"
27
- end
28
-
29
- normal_opts = opts.select { |t| t.is_a?(String) }
30
- special_opts = opts.select { |t| t.is_a?(Symbol) }
31
-
32
- puts normal_opts.map { |t| Shellwords.escape(t) }.join("\n")
33
- if special_opts.any?
34
- puts
35
- puts special_opts.join("\n")
36
- end
8
+ require_relative "../lib/tabry/shells/bash/wrapper"
9
+ Tabry::Bash::Wrapper.run(*ARGV)
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/tabry/cli/all_in_one"
4
+ require_relative "../lib/tabry/shells/bash"
5
+
6
+ Tabry::CLI::AllInOne.run do
7
+ config(names_underscores_to_dashes: true) do
8
+ desc <<~DESC
9
+ Generates bash completion for the tabry relative to this script
10
+ for the given command name and tabry JSON/YML file. Generates
11
+ bash functions unique to the command name to avoid conflicting with
12
+ other potential tabry installations.
13
+ DESC
14
+ arg :command_name, "Name of the command, e.g. 'mycli'"
15
+ arg :tabry_json_path, "Path to the mycli.json or mycli.yml compiled tabry file"
16
+ flagarg "uniq-fn-id,i", <<~DESC
17
+ Unique identifier to use for the unique tabry completion bash functions
18
+ (default is the capitalized command name)
19
+ DESC
20
+ end
21
+
22
+ def main
23
+ puts Tabry::Shells::Bash.generate(
24
+ args.command_name,
25
+ args.tabry_json_path,
26
+ uniq_fn_id: flags.uniq_fn_id,
27
+ )
28
+ end
29
+ end
30
+
31
+
data/bin/tabry-help CHANGED
@@ -11,7 +11,7 @@ require_relative "../lib/tabry/runner"
11
11
  raise "Usage: tabry-help config_name [arg1 [arg2]]..." unless ARGV.length >= 1
12
12
 
13
13
  config_name, *args = ARGV
14
- result = Tabry::Runner.new(config_name: config_name).parse(args)
14
+ result = Tabry::Runner.new(config: config_name).parse(args)
15
15
  usage = result.usage(config_name)
16
16
  puts "(Usage info from tabry-help; running command directly may provide more help)\n\n"
17
17
  puts usage
@@ -6,4 +6,4 @@ require_relative "../lib/tabry/runner"
6
6
  raise "Usage: tabry-test-options config_name arg1 [arg2]..." unless ARGV.length >= 1
7
7
 
8
8
  config_name, *args, last = ARGV
9
- puts Tabry::Runner.new(config_name: config_name).options(args, last).inspect
9
+ puts Tabry::Runner.new(config: config_name).options(args, last).inspect
data/bin/tabry-test-parse CHANGED
@@ -8,7 +8,7 @@ require_relative "../lib/tabry/runner"
8
8
  raise "Usage: tabry-test-parse config_name arg1 [arg2]..." unless ARGV.length >= 1
9
9
 
10
10
  config_name, *args = ARGV
11
- runner = Tabry::Runner.new(config_name: config_name)
11
+ runner = Tabry::Runner.new(config: config_name)
12
12
  result = runner.parse(args)
13
13
 
14
14
  puts result.state
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+ require_relative "builder"
5
+ require_relative "../config_builder"
6
+
7
+ # Define a Tabry Config + CLI in one call to build or run. The block passed in should define
8
+ # your CLI methods and include a call to `config` with a tabry config or block that defines
9
+ # one (passed on to Tabry::ConfigBuilder)
10
+ module Tabry
11
+ module CLI
12
+ module AllInOne
13
+ class AllInOneBase < Base
14
+ def self.config(opts = {}, &blk)
15
+ require_relative "../models/config"
16
+ if opts.is_a?(Tabry::Models::Config)
17
+ conf = opts
18
+ else
19
+ require_relative "../config_builder"
20
+ conf = Tabry::ConfigBuilder.build(**opts, &blk)
21
+ end
22
+
23
+ # Set it in the CLI class itself so AllInOne.build can find it
24
+ instance_variable_set(:@tabry_all_in_one_config, conf)
25
+
26
+ # Hack to avoid processing block any more if only running completion
27
+ # That way you an have expensive requires after the include
28
+ if (ARGV.first == "completion") && ARGV.length == 3 && ARGV[2].to_s =~ /^[0-9]+$/
29
+ throw :run_completion, true
30
+ end
31
+ end
32
+ end
33
+
34
+ # Creates and runs a CLI whose only purpose is to create a completion
35
+ # wrapper for another program
36
+ def self.completion_only(completion_conf = nil, **opts, &cmd_conf_blk)
37
+ completion_conf ||= Tabry::ConfigBuilder.build(**opts, &cmd_conf_blk)
38
+ cmd_name = completion_conf.cmd or raise "cmd is mandatory for completion_only configs"
39
+
40
+ cli_class = Class.new(Tabry::CLI::Base)
41
+ cli_conf = Tabry::ConfigBuilder.build { completion }
42
+ define_completion_methods(cli_class, completion_conf, cmd_name: cmd_name)
43
+ Tabry::CLI::Builder.new(cli_conf, cli_class).run(ARGV)
44
+ end
45
+
46
+ def self.define_completion_methods(cli_class, config, cmd_name: nil)
47
+ cli_class.module_eval do
48
+ define_method :completion__bash do
49
+ require_relative "../shells/bash"
50
+ Kernel.puts Tabry::Shells::Bash.generate_self(cmd_name: cmd_name)
51
+ end
52
+
53
+ define_method :completion do
54
+ require_relative "../shells/bash/wrapper"
55
+ Tabry::Bash::Wrapper.run(args.cmd_line, args.comp_point, config: config)
56
+ end
57
+ end
58
+ end
59
+
60
+ # Instead of passing in config, you can also run `config` with a block or a config option
61
+ # in inside blk. Doing this will also enable the "run completion fast" (see config method)
62
+ def self.build(cli: nil, config: nil, &blk)
63
+ # def self.build(cli: nil, config: nil, &blk)
64
+ cli ||= Class.new(AllInOneBase)
65
+
66
+ # If block given. run it to define the CLI methods and/or setting the config
67
+ # with a call to AllInOneBase.config
68
+ if blk
69
+ run_completion = catch(:run_completion) do
70
+ cli.module_eval(&blk)
71
+ nil
72
+ end
73
+ end
74
+
75
+ config ||= cli.instance_variable_get(:@tabry_all_in_one_config)
76
+
77
+ # If we recognize command is going to be a completion command, fast track and
78
+ # run completion now
79
+ if run_completion
80
+ require_relative "../shells/bash/wrapper"
81
+ Tabry::Bash::Wrapper.run(ARGV[1], ARGV[2], config: config)
82
+ end
83
+
84
+ # If we recognize there is a "completion" subcommand, add completion
85
+ # methods -- if not already defined by caller in the block
86
+ if config.main.subs.by_name["completion"] && !cli.instance_methods.include?(:completion__bash)
87
+ define_completion_methods(cli, config)
88
+ end
89
+
90
+ Tabry::CLI::Builder.new(config, cli)
91
+ end
92
+
93
+ def self.run(**kwargs, &blk)
94
+ build(**kwargs, &blk).run(ARGV)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -15,8 +15,8 @@ module Tabry
15
15
  @reqd_arg_proxy = ArgProxy.new(args, named_args, reqd: true) unless @reqd
16
16
  end
17
17
 
18
- def each(...)
19
- @args.each(...)
18
+ def each(&blk)
19
+ @args.each(&blk)
20
20
  end
21
21
 
22
22
  def [](key)
@@ -24,14 +24,14 @@ module Tabry
24
24
  res = @args[key]
25
25
  if @reqd && !res
26
26
  warn "FATAL: Missing required argument number #{key + 1}"
27
- exit 1
27
+ Kernel.exit 1
28
28
  end
29
29
  else
30
30
  key = key.to_s
31
31
  res = @named_args[key]
32
32
  if @reqd && !res
33
33
  warn "FATAL: Missing required argument #{key}"
34
- exit 1
34
+ Kernel.exit 1
35
35
  end
36
36
  end
37
37
  res
@@ -4,14 +4,16 @@ require_relative "../runner"
4
4
  require_relative "../util"
5
5
  require_relative "internals"
6
6
 
7
+ # Responsible for building the CLI object, making a Runner to parse the
8
+ # arguments, and running the proper action method of the CLI object.
7
9
  module Tabry
8
10
  module CLI
9
11
  class Builder
10
- attr_reader :config, :cli_class, :runner
12
+ attr_reader :cli_class, :runner
11
13
 
12
14
  def initialize(config_name, cli_class)
13
15
  @cli_class = cli_class
14
- @runner = Tabry::Runner.new(config_name: config_name)
16
+ @runner = Tabry::Runner.new(config: config_name)
15
17
  end
16
18
 
17
19
  DISALLOWED_SUBCOMMAND_NAMES = %w[args flags internals].freeze
@@ -28,7 +30,7 @@ module Tabry
28
30
  ::Tabry::Util.debug "named_args: #{result.named_args.inspect}"
29
31
 
30
32
  internals = Internals.new(
31
- runner: runner, config: config, raw_args: raw_args,
33
+ runner: runner, raw_args: raw_args,
32
34
  state: state, met: met, result: result
33
35
  )
34
36
 
@@ -43,12 +45,12 @@ module Tabry
43
45
  def check_for_correct_usage(result)
44
46
  if result.help?
45
47
  puts result.usage(File.basename($0))
46
- exit 0
48
+ Kernel.exit 0
47
49
  elsif result.invalid_usage_reason
48
50
  puts "Invalid usage: #{result.invalid_usage_reason}"
49
51
  puts
50
52
  puts result.usage(File.basename($0))
51
- exit(1)
53
+ Kernel.exit(1)
52
54
  end
53
55
  end
54
56
 
@@ -63,7 +65,7 @@ module Tabry
63
65
  def get_cli_object_and_met(cli, met, internals)
64
66
  if DISALLOWED_SUBCOMMAND_NAMES.include?(met)
65
67
  warn %(FATAL: Tabry does not support top-level subcommands named: #{DISALLOWED_SUBCOMMAND_NAMES.join(",")})
66
- exit 1
68
+ Kernel.exit 1
67
69
  end
68
70
 
69
71
  return [cli, "main"] if met.to_s == ""
@@ -90,7 +92,7 @@ module Tabry
90
92
  run_hooks(cli, met, :@after_actions)
91
93
  else
92
94
  warn %(FATAL: CLI does not support command #{met})
93
- exit 1
95
+ Kernel.exit 1
94
96
  end
95
97
  end
96
98
 
@@ -3,7 +3,7 @@
3
3
  module Tabry
4
4
  module CLI
5
5
  Internals = Struct.new(
6
- :runner, :config_name, :config, :raw_args, :state, :met, :result,
6
+ :runner, :raw_args, :state, :met, :result,
7
7
  keyword_init: true
8
8
  )
9
9
  end
@@ -8,7 +8,7 @@ module Tabry
8
8
  module Util
9
9
  module_function
10
10
 
11
- def make_cmdline(cmdline, *args, echo: false, echo_only: false)
11
+ def make_cmdline(cmdline, *args, echo: false, echo_only: false, merge_stderr: false)
12
12
  # Allow to pass in an array, or varargs:
13
13
  args = args.first if args.length == 1 && args.first.is_a?(Array)
14
14
 
@@ -21,6 +21,7 @@ module Tabry
21
21
  end
22
22
  end
23
23
  cmdline = cmdline % args
24
+ cmdline = "{ #{cmdline} ;} 2>&1" if merge_stderr
24
25
 
25
26
  warn cmdline if echo || echo_only
26
27
  return nil if echo_only
@@ -35,26 +36,38 @@ module Tabry
35
36
 
36
37
  # TODO: would be nice to get separate STDERR and STDOUT
37
38
  def backtick_or_die(*cmdline, **opts)
38
- backtick_with_process_status(*cmdline, **opts).first
39
+ backtick_with_status(*cmdline, valid_statuses: [0], **opts).first
39
40
  end
40
41
 
41
- def backtick_with_process_status(*cmdline, valid_statuses: [0], **opts)
42
+ def backtick_with_status(*cmdline, valid_statuses: nil, **opts)
42
43
  cmdline = make_cmdline(*cmdline, **opts)
43
44
  return [nil, nil] unless cmdline
44
45
 
45
- res = `#{cmdline}`
46
+ enoent_error = false
47
+ begin
48
+ res = `#{cmdline}`
49
+ rescue Errno::ENOENT
50
+ enoent_error = true
51
+ end
52
+
46
53
  status = $?
47
54
  if valid_statuses && !valid_statuses.include?(status.exitstatus)
48
- warn "COMMAND FAILED with exit code #{status.exitstatus}: #{cmdline}"
49
- exit 1
55
+ msg = "COMMAND FAILED with exit code #{status.exitstatus}"
56
+ msg += " (command does not exist)" if enoent_error
57
+ Kernel.warn "#{msg}: #{cmdline}"
58
+ Kernel.warn "Command output:\n#{res}"
59
+ Kernel.exit 1
50
60
  end
51
61
  [res, status]
52
62
  end
53
63
 
54
- def open_web_page(url)
55
- command = RUBY_PLATFORM.include?("linux") ? "xdg-open" : "open"
56
- unless system("(%s %s 2>&1) >/dev/null", command, url)
57
- warn "WARNING: opening web page failed: #{make_cmdline("%s %s", command, url)}"
64
+ def open_command
65
+ RUBY_PLATFORM.include?("linux") ? "xdg-open" : "open"
66
+ end
67
+
68
+ def open_web_page(url_or_urls)
69
+ unless system("(%s %s 2>&1) >/dev/null", open_command, url_or_urls)
70
+ warn "WARNING: opening web page failed: #{make_cmdline("%s %s", open_command, url)}"
58
71
  end
59
72
  end
60
73
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "generic_builder"
4
+
5
+ module Tabry
6
+ module ConfigBuilder
7
+ class ArgOrFlagBuilder < GenericBuilder
8
+ init_setters :name, :description, split_name_to_aliases: true
9
+ simple_setter :desc, :description
10
+ simple_setter :title
11
+
12
+ def _include(inc)
13
+ _append :options, { "type" => "include", "value" => inc }
14
+ end
15
+
16
+ def opts(opt_hashes)
17
+ _append :options, *opt_hashes
18
+ end
19
+
20
+ def shell(cmd)
21
+ [{ "type" => "shell", "value" => cmd.to_s }]
22
+ end
23
+
24
+ def file
25
+ [{ "type" => "file" }]
26
+ end
27
+
28
+ def dir
29
+ [{ "type" => "dir" }]
30
+ end
31
+
32
+ def const(opts)
33
+ [opts].flatten.map do |opt|
34
+ [{ "type" => "const", "value" => opt.to_s }]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "arg_or_flag_builder"
4
+
5
+ module Tabry
6
+ module ConfigBuilder
7
+ class FlagargBuilder < ArgOrFlagBuilder
8
+ def _init(*args)
9
+ super(*args)
10
+ _set :arg, true
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ class GenericBuilder
4
+ attr_reader :_opts
5
+
6
+ private_class_method :new
7
+ def self.build(opts = {}, *args, &blk)
8
+ builder = new(opts, *args)
9
+ builder.instance_eval(&blk) if blk
10
+ builder._obj.compact
11
+ end
12
+
13
+ # Should be used instead of MyBuilder.build to pass in opts
14
+ def _build(builder, *args, &blk)
15
+ builder.build(_opts, *args, &blk)
16
+ end
17
+
18
+ def _obj
19
+ @_obj ||= {}
20
+ end
21
+
22
+ def _append(list_name, *value_or_values)
23
+ _obj[list_name.to_s] ||= []
24
+ _obj[list_name.to_s].concat([value_or_values].flatten)
25
+ value_or_values
26
+ end
27
+
28
+ def _set_hash(hash_name, key, value)
29
+ _obj[hash_name.to_s] ||= {}
30
+ _obj[hash_name.to_s][key.to_s] = value
31
+ end
32
+
33
+ def _set(key, val)
34
+ val = val.chomp if val.is_a?(String)
35
+ val = val.to_s if val.is_a?(Symbol)
36
+ if _opts[:names_underscores_to_dashes] && key.to_s == "name"
37
+ val = val.gsub("_", "-") if val.is_a?(String)
38
+ val = val.map { |name| name.to_s.gsub("_", "-") } if val.is_a?(Array)
39
+ end
40
+ _obj[key.to_s] = val
41
+ end
42
+
43
+ def initialize(opts, *args)
44
+ @_opts = opts
45
+ includes, not_includes = args.partition { |arg| (arg.is_a?(Symbol) || arg.is_a?(String)) && arg.to_s.start_with?("@") }
46
+ includes.each { |arg| include(arg) }
47
+ _init(*not_includes) if defined?(:_init)
48
+ end
49
+
50
+ def self.simple_setter(name, key = nil)
51
+ define_method name do |value|
52
+ value = value.chomp if value.is_a?(String)
53
+ _set (key || name), value
54
+ end
55
+ end
56
+
57
+ def self.builder_appender(name, key, builder, merge: {})
58
+ define_method name.to_sym do |*args, &blk|
59
+ value = _build(builder, *args, &blk).merge(merge.transform_keys(&:to_s))
60
+ _append key, value
61
+ end
62
+ end
63
+
64
+ def include(inc)
65
+ inc_str = inc.to_s
66
+ raise "include must be a start with @" unless inc_str.start_with?("@")
67
+
68
+ include_name = inc_str[1..]
69
+ if _opts[:names_underscores_to_dashes]
70
+ include_name = include_name.gsub("_", "-")
71
+ end
72
+
73
+ _include include_name
74
+ end
75
+
76
+ def _include(_inc)
77
+ raise "_include not implemented for #{self.class}"
78
+ end
79
+
80
+ def _augment_list_item(list_name, item)
81
+ item = _obj[list_name].find { |i| i.equal?(item) }
82
+ yield item
83
+ item
84
+ end
85
+
86
+ def self.init_setters(*setters, split_name_to_aliases: false)
87
+ define_method :_init do |*args|
88
+ args.zip(setters) do |arg, setter|
89
+ _set setter, arg
90
+ _split_name_to_aliases if split_name_to_aliases
91
+ end
92
+ end
93
+ end
94
+
95
+ def _split_name_to_aliases
96
+ name = _obj["name"]
97
+ if name
98
+ name = name.split(/[ ,]{1,2}/) unless name.is_a?(Array)
99
+ _obj["name"], *aliases = name
100
+ aliases.each { |a| _append :aliases, a }
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "arg_or_flag_builder"
4
+ require_relative "flagarg_builder"
5
+ require_relative "generic_builder"
6
+
7
+ module Tabry
8
+ module ConfigBuilder
9
+ class SubBuilder < GenericBuilder
10
+ init_setters :name, :description, split_name_to_aliases: true
11
+ simple_setter :desc, :description
12
+ builder_appender :arg, :args, ArgOrFlagBuilder
13
+ builder_appender :opt_arg, :args, ArgOrFlagBuilder, merge: { optional: true }
14
+ builder_appender :varargs, :args, ArgOrFlagBuilder, merge: { varargs: true }
15
+ builder_appender :opt_varargs, :args, ArgOrFlagBuilder, merge: { optional: true, varargs: true }
16
+ builder_appender :sub, :subs, SubBuilder
17
+ builder_appender :flag, :flags, ArgOrFlagBuilder
18
+ builder_appender :flagarg, :flags, FlagargBuilder
19
+
20
+ def _include(inc)
21
+ %i[args flags subs].each do |thing_type|
22
+ _append(thing_type, { "include" => inc })
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "arg_or_flag_builder"
4
+ require_relative "sub_builder"
5
+
6
+ module Tabry
7
+ module ConfigBuilder
8
+ class TopLevelBuilder < SubBuilder
9
+ simple_setter :cmd
10
+
11
+ def defargs(name, &blk)
12
+ name = name.to_s.gsub(/^@/, "")
13
+ _set_hash :arg_includes, name, _build(SubBuilder, &blk)
14
+ end
15
+
16
+ def defopts(name, &blk)
17
+ name = name.to_s.gsub(/^@/, "")
18
+ _set_hash :option_includes, name, _build(ArgOrFlagBuilder, &blk)["options"]
19
+ end
20
+
21
+ def completion
22
+ sub :completion do
23
+ desc "Get tab completion shell config"
24
+ sub :bash, "Get tab completion for bash or zsh"
25
+ arg :cmd_line, "(for internal usage, when used instead of subcommand) full command line for getting completion options"
26
+ arg :comp_point, "(for internal usage, when used instead of subcommand) comp point"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "config_builder/top_level_builder"
4
+ require_relative "models/config"
5
+
6
+ module Tabry
7
+ module ConfigBuilder
8
+ def self.build(names_underscores_to_dashes: false, &blk)
9
+ opts = { names_underscores_to_dashes: names_underscores_to_dashes }
10
+ conf = TopLevelBuilder.build(opts, &blk)
11
+ top_level = %w[cmd arg_includes option_includes]
12
+ conf = conf.slice(*top_level).merge("main" => conf.reject { |k, _v| top_level.include?(k) })
13
+ Tabry::Models::Config.new(raw: conf)
14
+ end
15
+ end
16
+ end
@@ -32,8 +32,8 @@ module Tabry
32
32
  to_a.[](*args)
33
33
  end
34
34
 
35
- def each(...)
36
- to_a.each(...)
35
+ def each(&blk)
36
+ to_a.each(&blk)
37
37
  end
38
38
 
39
39
  def length
@@ -28,8 +28,8 @@ module Tabry
28
28
  to_h.[](*args)
29
29
  end
30
30
 
31
- def each(...)
32
- to_h.each(...)
31
+ def each(&blk)
32
+ to_h.each(&blk)
33
33
  end
34
34
 
35
35
  def empty?
data/lib/tabry/runner.rb CHANGED
@@ -11,8 +11,12 @@ module Tabry
11
11
  class Runner
12
12
  attr_reader :config
13
13
 
14
- def initialize(config_name:)
15
- @config = ConfigLoader.load(name: config_name)
14
+ def initialize(config:)
15
+ @config = if config.is_a?(Tabry::Models::Config)
16
+ config
17
+ else
18
+ ConfigLoader.load(name: config)
19
+ end
16
20
  end
17
21
 
18
22
  def options(args, last = nil)