tabry 0.1.5 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
+