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
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shellwords"
4
+ require "yaml"
5
+ require_relative "../../util"
6
+ require_relative "../../runner"
7
+ require_relative "../../shell_splitter"
8
+
9
+ # Bash-specific entrypoint, taking COMP_WORDS and COMP_CWORDS and returning possible options
10
+ module Tabry
11
+ module Bash
12
+ module Wrapper
13
+ def self.run(cmd_line, comp_point, config: nil)
14
+ cmd_name, args, last_arg = Tabry::ShellSplitter.split(cmd_line, comp_point)
15
+ opts = Tabry::Runner.new(config: config || cmd_name).options(args, last_arg)
16
+
17
+ if Tabry::Util.debug?
18
+ require "json"
19
+ $stderr.puts
20
+ warn "debug: got command line and comp_point: #{cmd_line.inspect}, #{comp_point}"
21
+ warn "using args: #{args.inspect}"
22
+ warn "using lastarg: #{last_arg.inspect}"
23
+ warn "results from Tabry#options(): #{opts.inspect}"
24
+ warn "--- end debug output ---"
25
+ end
26
+
27
+ normal_opts = opts.select { |t| t.is_a?(String) }
28
+ special_opts = opts.select { |t| t.is_a?(Symbol) }
29
+
30
+ puts normal_opts.map { |t| Shellwords.escape(t) }.join("\n")
31
+ if special_opts.any?
32
+ puts
33
+ puts special_opts.join("\n")
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -7,27 +7,64 @@
7
7
  # conflicts.
8
8
  # See sh/bash/README.md and "Adding Tab Completion to your CLI" in main README
9
9
 
10
+ require "shellwords"
11
+
10
12
  module Tabry
11
13
  module Shells
12
14
  module Bash
13
15
  # NOTE! This code uses sh/bash/tabry_bash_core.sh and is described in
14
16
  # sh/bash/README.md; see that README for more info
15
- def self.generate(cmd_name, tabry_file_path)
16
- capitalized_cmd_name = cmd_name.upcase.gsub(/[^a-zA-Z0-9_]/, "_")
17
- tabry_file = Shellwords.escape(File.expand_path(tabry_file_path))
18
- path_to_tabry = Shellwords.escape(File.expand_path("#{__dir__}/../../../"))
17
+ # ruby_path and run_tabry_bash_directly are mutually
18
+ # exclusive -- use run_tabry_bash_directly to use the
19
+ # shebang in the tabry-bash file
20
+ def self.generate(cmd_name, tabry_file_path, uniq_fn_id: nil)
21
+ generate_internal(
22
+ cmd_name: cmd_name,
23
+ import_path: File.expand_path(tabry_file_path),
24
+ tabry_bash_executable: File.expand_path("#{path_to_tabry}/bin/tabry-bash"),
25
+ tabry_bash_arg: nil,
26
+ uniq_fn_id: uniq_fn_id
27
+ )
28
+ end
29
+
30
+ # Generate bash completion code that will run the currently running
31
+ # command ($0) with "completion" to get completion options. "cmd_name"
32
+ # is used to tell bash which command to make options for (and
33
+ # for naming of the _tabry_CMD_NAME_completions_internal bash function)
34
+ def self.generate_self(cmd_name: nil)
35
+ cmd_name ||= File.basename($0)
36
+ generate_internal(
37
+ cmd_name: cmd_name,
38
+ import_path: "",
39
+ tabry_bash_executable: File.expand_path($0),
40
+ tabry_bash_arg: "completion"
41
+ )
42
+ end
43
+
44
+ def self.path_to_tabry
45
+ File.expand_path("#{__dir__}/../../../")
46
+ end
47
+
48
+ def self.esc(s)
49
+ Shellwords.escape(s)
50
+ end
19
51
 
52
+ def self.generate_internal(cmd_name:, import_path:, tabry_bash_executable:, tabry_bash_arg:, uniq_fn_id: nil)
53
+ # uniq_fn_id is added to the bash functions to ensure they are unique
54
+ # -- by default this is the capitalized command name
55
+ uniq_fn_id ||= cmd_name
56
+ uniq_fn_id = uniq_fn_id.upcase.gsub(/[^A-Z0-9_]/, "_")
20
57
  core = File.read("#{__dir__}/../../../sh/bash/tabry_bash_core.sh")
21
- core.gsub! "_tabry_completions_internal()", "_tabry_#{capitalized_cmd_name}_completions_internal()"
58
+ core.gsub! "_tabry_completions_internal()", "_tabry_#{uniq_fn_id}_completions_internal()"
22
59
 
23
60
  <<~END_BASH_CODE_TEMPLATE + core
24
61
  # The following Autocomplete is for a Tabry-powered command. It was
25
62
  # generated by the command itself. See the documentation located in
26
- # #{path_to_tabry}/sh/bash/README.md
27
- _tabry_#{capitalized_cmd_name}_completions() {
28
- TABRY_IMPORTS_PATH=#{tabry_file} _tabry_#{capitalized_cmd_name}_completions_internal #{path_to_tabry}/bin/tabry-bash
63
+ # #{esc path_to_tabry}/sh/bash/README.md
64
+ _tabry_#{uniq_fn_id}_completions() {
65
+ TABRY_IMPORTS_PATH=#{esc import_path} _tabry_#{uniq_fn_id}_completions_internal #{esc tabry_bash_executable} #{tabry_bash_arg && esc(tabry_bash_arg)}
29
66
  }
30
- complete -F _tabry_#{capitalized_cmd_name}_completions #{Shellwords.escape cmd_name}
67
+ complete -F _tabry_#{uniq_fn_id}_completions #{esc cmd_name}
31
68
  END_BASH_CODE_TEMPLATE
32
69
  end
33
70
  end
@@ -17,6 +17,6 @@
17
17
 
18
18
  source "$( dirname "${BASH_SOURCE[0]:-${(%):-%x}}" )"/tabry_bash_core.sh
19
19
  _tabry_completions() {
20
- _tabry_completions_internal "$( dirname "${BASH_SOURCE[0]:-${(%):-%x}}" )"/../../bin/tabry-bash
20
+ _tabry_completions_internal ruby "$( dirname "${BASH_SOURCE[0]:-${(%):-%x}}" )"/../../bin/tabry-bash
21
21
  }
22
22
 
@@ -2,13 +2,14 @@
2
2
  # See sh/bash/README.md and sh/bash/tabry_bash.sh in the tabry gem
3
3
  _tabry_completions_internal()
4
4
  {
5
- local tabry_bash="$1"
5
+ local tabry_bash_executable="$1"
6
+ local tabry_bash_arg="$2"
6
7
 
7
8
  [[ -n "$TABRY_DEBUG" ]] && echo && echo -n tabry start bash: && date +%s.%N >&2
8
9
  local saveifs="$IFS"
9
10
  IFS=$'\n'
10
11
 
11
- local result=`ruby "$tabry_bash" "$COMP_LINE" "$COMP_POINT"`
12
+ local result=`"$tabry_bash_executable" ${tabry_bash_arg:+"$tabry_bash_arg"} "$COMP_LINE" "$COMP_POINT"`
12
13
  local specials
13
14
 
14
15
  if [[ $result == *$'\n'$'\n'* ]]; then
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tabry
4
+ module Spec
5
+ module Fixtures
6
+ module ConfigBuilder
7
+ Args = Tabry::ConfigBuilder.build do
8
+ cmd "argstest"
9
+
10
+ arg do
11
+ opts const :a
12
+ opts shell "ls"
13
+ opts const %w[T F true false yes no]
14
+ opts file
15
+ opts dir
16
+ end
17
+
18
+ arg :foo
19
+ arg :foo, :@whatever
20
+ arg :foo, "desc", :@whatever do
21
+ opts const :abc
22
+ end
23
+
24
+ opt_arg
25
+ opt_arg :foo, :@whatever do
26
+ opts const :abc
27
+ end
28
+
29
+ arg do
30
+ include :@foo
31
+ opts const :abc
32
+ end
33
+
34
+ opt_varargs "rest-args-optional" do
35
+ opts const %w[foo bar waz]
36
+ end
37
+
38
+ arg :thing_to_search_for do
39
+ title "thing to search for"
40
+ end
41
+
42
+ varargs :files_to_load do
43
+ title "file to load"
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,32 @@
1
+ cmd argstest
2
+
3
+ arg {
4
+ opts const a
5
+ opts shell "ls"
6
+ opts const (T F true false yes no)
7
+ opts file
8
+ opts dir
9
+ }
10
+
11
+ arg foo
12
+ arg foo @whatever
13
+ arg foo "desc" @whatever {
14
+ opts const abc
15
+ }
16
+
17
+ opt arg
18
+ opt arg foo @whatever {
19
+ opts const abc
20
+ }
21
+
22
+ arg {
23
+ include @foo
24
+ opts const abc
25
+ }
26
+
27
+ opt varargs rest-args-optional { opts const (foo bar waz) }
28
+
29
+ arg thing_to_search_for { title "thing to search for" }
30
+ varargs files_to_load { title "file to load" }
31
+
32
+
@@ -0,0 +1,63 @@
1
+ cmd: argstest
2
+ main:
3
+ args:
4
+ - options:
5
+ - type: const
6
+ value: a
7
+ - type: shell
8
+ value: ls
9
+ - type: const
10
+ value: T
11
+ - type: const
12
+ value: F
13
+ - type: const
14
+ value: "true"
15
+ - type: const
16
+ value: "false"
17
+ - type: const
18
+ value: "yes"
19
+ - type: const
20
+ value: "no"
21
+ - type: file
22
+ - type: dir
23
+ - name: foo
24
+ - name: foo
25
+ options:
26
+ - type: include
27
+ value: whatever
28
+ - name: foo
29
+ options:
30
+ - type: include
31
+ value: whatever
32
+ - type: const
33
+ value: abc
34
+ description: desc
35
+ - optional: true
36
+ - name: foo
37
+ optional: true
38
+ options:
39
+ - type: include
40
+ value: whatever
41
+ - type: const
42
+ value: abc
43
+ - options:
44
+ - type: include
45
+ value: foo
46
+ - type: const
47
+ value: abc
48
+ - name: rest-args-optional
49
+ optional: true
50
+ varargs: true
51
+ options:
52
+ - type: const
53
+ value: foo
54
+ - type: const
55
+ value: bar
56
+ - type: const
57
+ value: waz
58
+ - name: thing_to_search_for
59
+ title: thing to search for
60
+ - name: files_to_load
61
+ varargs: true
62
+ title: file to load
63
+
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tabry
4
+ module Spec
5
+ module Fixtures
6
+ module ConfigBuilder
7
+ Defs = Tabry::ConfigBuilder.build do
8
+ cmd :defstest
9
+
10
+ defopts :@bar do
11
+ opts const :def
12
+ end
13
+
14
+ defopts :@environment do
15
+ opts const %w[prod beta dev]
16
+ end
17
+
18
+ defargs :@verbose do
19
+ flag :verbose, "Show more info"
20
+ end
21
+
22
+ defargs "@project-and-environment" do
23
+ arg :project, "The project" do
24
+ opts const %w[project1 project2]
25
+ end
26
+
27
+ arg :environment, "The environment", :@environment
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ cmd defstest
2
+
3
+ defopts @bar {
4
+ opts const def
5
+ }
6
+
7
+ defopts @environment {
8
+ opts const (prod beta dev)
9
+ }
10
+
11
+ defargs @verbose {
12
+ flag verbose "Show more info"
13
+ }
14
+
15
+ defargs @project-and-environment {
16
+ arg project "The project" { opts const (project1 project2) }
17
+ arg environment "The environment" @environment
18
+ }
19
+
20
+
21
+
@@ -0,0 +1,33 @@
1
+ cmd: defstest
2
+ main: {}
3
+ option_includes:
4
+ bar:
5
+ - type: const
6
+ value: def
7
+ environment:
8
+ - type: const
9
+ value: prod
10
+ - type: const
11
+ value: beta
12
+ - type: const
13
+ value: dev
14
+ arg_includes:
15
+ verbose:
16
+ flags:
17
+ - name: verbose
18
+ description: Show more info
19
+ project-and-environment:
20
+ args:
21
+ - name: project
22
+ description: The project
23
+ options:
24
+ - type: const
25
+ value: project1
26
+ - type: const
27
+ value: project2
28
+ - name: environment
29
+ options:
30
+ - type: include
31
+ value: environment
32
+ description: The environment
33
+
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tabry
4
+ module Spec
5
+ module Fixtures
6
+ module ConfigBuilder
7
+ Flags = Tabry::ConfigBuilder.build do
8
+ cmd :flagstest
9
+
10
+ flag :foo1
11
+ flag :foo2, "Some desc"
12
+ flag "dry-run2,r", "Don't act, only show what would be done"
13
+
14
+ flagarg "foo3"
15
+ flagarg :b do
16
+ opts const :a
17
+ end
18
+
19
+ flagarg "f,format" do
20
+ opts const %w[json yml]
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ cmd flagstest
2
+
3
+ flag foo1
4
+ flag foo2 "Some desc"
5
+ flag dry-run2,r "Don't act, only show what would be done"
6
+
7
+ flagarg foo3
8
+ flagarg b {
9
+ opts const a
10
+ }
11
+
12
+ flagarg f,format { opts const (json yml) }
13
+
@@ -0,0 +1,27 @@
1
+ cmd: flagstest
2
+ main:
3
+ flags:
4
+ - name: foo1
5
+ - name: foo2
6
+ description: Some desc
7
+ - name: dry-run2
8
+ aliases:
9
+ - r
10
+ description: Don't act, only show what would be done
11
+ - name: foo3
12
+ arg: true
13
+ - name: b
14
+ arg: true
15
+ options:
16
+ - type: const
17
+ value: a
18
+ - name: f
19
+ aliases:
20
+ - format
21
+ arg: true
22
+ options:
23
+ - type: const
24
+ value: json
25
+ - type: const
26
+ value: yml
27
+
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tabry
4
+ module Spec
5
+ module Fixtures
6
+ module ConfigBuilder
7
+ Subs = Tabry::ConfigBuilder.build do
8
+ cmd :substest
9
+
10
+ sub :list, "List the things"
11
+ sub "delete,d", :@verbose do
12
+ desc "Delete a thing"
13
+ arg
14
+ end
15
+
16
+ # status command acts and project-and-environment as well
17
+ sub :status, "foo", "@project-and-environment", :@verbose do
18
+ flagarg :env, :@environment
19
+ end
20
+
21
+ sub :mysub1 do
22
+ sub :mysub2 do
23
+ flag :someopt
24
+ opt_arg "mysub2-arg"
25
+ end
26
+ end
27
+
28
+ # Top-level include
29
+ include "@project-and-environment"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,25 @@
1
+ cmd substest
2
+
3
+ sub list "List the things"
4
+ sub delete,d @verbose {
5
+ desc "Delete a thing"
6
+ arg
7
+ }
8
+
9
+ # status command acts and project-and-environment as well
10
+ sub status "foo" @project-and-environment @verbose {
11
+ flagarg env @environment
12
+ }
13
+
14
+ sub mysub1 {
15
+ sub mysub2 {
16
+ flag someopt
17
+ opt arg mysub2-arg
18
+ }
19
+ }
20
+
21
+ # Top-level include
22
+ include @project-and-environment
23
+
24
+
25
+
@@ -0,0 +1,46 @@
1
+ cmd: substest
2
+ main:
3
+ subs:
4
+ - name: list
5
+ description: List the things
6
+ - name: delete
7
+ aliases:
8
+ - d
9
+ args:
10
+ - include: verbose
11
+ - {}
12
+ flags:
13
+ - include: verbose
14
+ subs:
15
+ - include: verbose
16
+ description: Delete a thing
17
+ - name: status
18
+ description: foo
19
+ args:
20
+ - include: project-and-environment
21
+ - include: verbose
22
+ flags:
23
+ - include: project-and-environment
24
+ - include: verbose
25
+ - name: env
26
+ options:
27
+ - type: include
28
+ value: environment
29
+ arg: true
30
+ subs:
31
+ - include: project-and-environment
32
+ - include: verbose
33
+ - name: mysub1
34
+ subs:
35
+ - name: mysub2
36
+ flags:
37
+ - name: someopt
38
+ args:
39
+ - name: mysub2-arg
40
+ optional: true
41
+ - include: project-and-environment
42
+ args:
43
+ - include: project-and-environment
44
+ flags:
45
+ - include: project-and-environment
46
+
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tabry
4
+ module Spec
5
+ module Fixtures
6
+ module ConfigBuilder
7
+ Underscoresdashes = Tabry::ConfigBuilder.build(names_underscores_to_dashes: true) do
8
+ # this is not translated, because that actually affects things
9
+ cmd :underscores_test
10
+
11
+ arg :foo_bar1, :@project_and_environment
12
+
13
+ flagarg :foo_bar2, :@project_and_environment do
14
+ include "@waz_abc"
15
+ end
16
+
17
+ sub "s,status_foo", "foo", :@project_and_environment do
18
+ flagarg :env, :@environment
19
+ end
20
+
21
+ include :@project_and_environment
22
+
23
+ defopts :@project_and_environment do
24
+ opts const :a
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ cmd underscores_test
2
+
3
+ arg foo-bar1 @project-and-environment
4
+
5
+ flagarg foo-bar2 @project-and-environment {
6
+ include @waz-abc
7
+ }
8
+
9
+ sub s,status-foo "foo" @project-and-environment {
10
+ flagarg env @environment
11
+ }
12
+
13
+ include @project-and-environment
14
+
15
+ defopts @project_and_environment {
16
+ opts const a
17
+ }
18
+
@@ -0,0 +1,39 @@
1
+ cmd: underscores_test
2
+ main:
3
+ args:
4
+ - name: foo-bar1
5
+ options:
6
+ - type: include
7
+ value: project-and-environment
8
+ - include: project-and-environment
9
+ flags:
10
+ - name: foo-bar2
11
+ options:
12
+ - type: include
13
+ value: project-and-environment
14
+ - type: include
15
+ value: waz-abc
16
+ arg: true
17
+ - include: project-and-environment
18
+ subs:
19
+ - name: s
20
+ aliases:
21
+ - status-foo
22
+ description: foo
23
+ args:
24
+ - include: project-and-environment
25
+ flags:
26
+ - include: project-and-environment
27
+ - name: env
28
+ options:
29
+ - type: include
30
+ value: environment
31
+ arg: true
32
+ subs:
33
+ - include: project-and-environment
34
+ - include: project-and-environment
35
+ option_includes:
36
+ project_and_environment:
37
+ - type: const
38
+ value: a
39
+